* [RFC PATCH 1/1] [media] pci: Add support for DVB PCIe cards from Prospero Technologies Ltd.
2015-02-16 19:48 [RFC PATCH 0/1] [media] pci: Add support for DVB PCIe cards from Prospero Technologies Ltd Philip Downer
@ 2015-02-16 19:48 ` Philip Downer
2015-02-19 11:06 ` Sean Young
2015-02-19 12:40 ` Sean Young
2015-02-16 20:01 ` [RFC PATCH 0/1] " Antti Palosaari
1 sibling, 2 replies; 12+ messages in thread
From: Philip Downer @ 2015-02-16 19:48 UTC (permalink / raw)
To: linux-media; +Cc: Philip Downer
This patch adds support for the Vortex 1 PCIe card from Prospero
Technologies Ltd. The Vortex 1 supports up to 8 tuner modules and
currently ships with 8xDibcom 7090p tuners. The card also has raw
infra-red support and a hardware demuxer.
Signed-off-by: Philip Downer <pdowner@prospero-tech.com>
---
drivers/media/pci/Kconfig | 1 +
drivers/media/pci/Makefile | 2 +
drivers/media/pci/prospero/Kconfig | 7 +
drivers/media/pci/prospero/Makefile | 7 +
drivers/media/pci/prospero/prospero_common.h | 264 ++++
drivers/media/pci/prospero/prospero_fe.h | 5 +
drivers/media/pci/prospero/prospero_fe_main.c | 466 ++++++
drivers/media/pci/prospero/prospero_i2c.c | 449 ++++++
drivers/media/pci/prospero/prospero_i2c.h | 3 +
drivers/media/pci/prospero/prospero_ir.c | 150 ++
drivers/media/pci/prospero/prospero_ir.h | 4 +
drivers/media/pci/prospero/prospero_main.c | 2086 +++++++++++++++++++++++++
12 files changed, 3444 insertions(+)
create mode 100644 drivers/media/pci/prospero/Kconfig
create mode 100644 drivers/media/pci/prospero/Makefile
create mode 100644 drivers/media/pci/prospero/prospero_common.h
create mode 100644 drivers/media/pci/prospero/prospero_fe.h
create mode 100644 drivers/media/pci/prospero/prospero_fe_main.c
create mode 100644 drivers/media/pci/prospero/prospero_i2c.c
create mode 100644 drivers/media/pci/prospero/prospero_i2c.h
create mode 100644 drivers/media/pci/prospero/prospero_ir.c
create mode 100644 drivers/media/pci/prospero/prospero_ir.h
create mode 100644 drivers/media/pci/prospero/prospero_main.c
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
index 218144a..5c7c356 100644
--- a/drivers/media/pci/Kconfig
+++ b/drivers/media/pci/Kconfig
@@ -46,6 +46,7 @@ source "drivers/media/pci/pt3/Kconfig"
source "drivers/media/pci/mantis/Kconfig"
source "drivers/media/pci/ngene/Kconfig"
source "drivers/media/pci/ddbridge/Kconfig"
+source "drivers/media/pci/prospero/Kconfig"
source "drivers/media/pci/smipcie/Kconfig"
endif
diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
index 0baf0d2..d792604 100644
--- a/drivers/media/pci/Makefile
+++ b/drivers/media/pci/Makefile
@@ -11,6 +11,7 @@ obj-y += ttpci/ \
mantis/ \
ngene/ \
ddbridge/ \
+ prospero/ \
saa7146/ \
smipcie/
@@ -26,4 +27,5 @@ obj-$(CONFIG_VIDEO_SAA7164) += saa7164/
obj-$(CONFIG_VIDEO_TW68) += tw68/
obj-$(CONFIG_VIDEO_MEYE) += meye/
obj-$(CONFIG_STA2X11_VIP) += sta2x11/
+obj-$(CONFIG_PROSPERO) += prospero/
obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/
diff --git a/drivers/media/pci/prospero/Kconfig b/drivers/media/pci/prospero/Kconfig
new file mode 100644
index 0000000..960f370
--- /dev/null
+++ b/drivers/media/pci/prospero/Kconfig
@@ -0,0 +1,7 @@
+config DVB_PROSPERO
+ tristate "Prospero cards"
+ depends on DVB_CORE && PCI
+ help
+ Support for PCIe DVB-T cards from Prospero Technologies Ltd.
+
+ Say Y or M if you own such a device and want to use it.
diff --git a/drivers/media/pci/prospero/Makefile b/drivers/media/pci/prospero/Makefile
new file mode 100644
index 0000000..ea35912
--- /dev/null
+++ b/drivers/media/pci/prospero/Makefile
@@ -0,0 +1,7 @@
+obj-m := prospero.o
+prospero-objs := prospero_main.o prospero_fe_main.o prospero_i2c.o prospero_ir.o
+
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/pci/prospero
diff --git a/drivers/media/pci/prospero/prospero_common.h b/drivers/media/pci/prospero/prospero_common.h
new file mode 100644
index 0000000..f8aece1
--- /dev/null
+++ b/drivers/media/pci/prospero/prospero_common.h
@@ -0,0 +1,264 @@
+#ifndef PROSPERO_COMMON
+
+#define PROSPERO_COMMON
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+
+#define MAXIMUM_NUM_OF_FE 8
+
+#define MAX_NUM_OF_DEMUXS 8
+#define DEMUX_PER_FE 1
+
+#define STREAMS_PER_DEMUX 30
+
+#define MAX_STREAMS (STREAMS_PER_DEMUX * MAX_NUM_OF_DEMUXS)
+
+#define WILD_PID 0x2000
+
+#define PROSPERO_REGISTER_BASE 0x000
+
+#define PID_TABLE_START (PROSPERO_REGISTER_BASE + 0x40000)
+
+#define I2C_RESETS (PROSPERO_REGISTER_BASE + 0x70)
+
+#define TS_CAP_ENABLE (PROSPERO_REGISTER_BASE + 0x74)
+
+#define INTERRUPT_ENABLE (PROSPERO_REGISTER_BASE + 0x78)
+#define INTERRUPT_CONTROL (PROSPERO_REGISTER_BASE + 0x40)
+
+#define PID_TABLE_SLOT_SIZE 0x04
+
+#define CONTROL_BITS_OFFSET 0x00
+
+/*Control bit masks*/
+#define NULL_PACKET 0x8
+#define INTERRUPT_ENABLED 0x4
+#define GUARD_VALUE 0x2
+#define CHANNEL_ENABLED 0x1
+
+#define INTERRUPT_FIFO 0x44
+#define GUARD_BAND_FIFO 0x48
+
+#define FIFO_BUFF_PTR_MASK 0x3F
+#define FIFO_TUNER_NUM_MASK 0x700
+#define FIFO_NUM_ENTRIES_MASK 0x1FF0000
+
+#define LED_OFFSET 0x0
+
+#define BUFFER_BASE_ADDRESS 0x04
+#define BUFFER_END_ADDRESS 0x08
+#define GUARD_A 0x0C
+#define INT_TRIGGER_ADDRESS 0x10
+#define CURRENT_WP_A 0x14
+
+#define BUFFER_SLOT_SIZE 0x20
+
+#define BIT_MASK_26 0x3FFFFFF
+#define BIT_MASK_22 0x3FFFFF
+#define BIT_MASK_20 0xFFFFF
+
+#define MAX_PIDS_PER_STREAM 32
+
+#define PIDS_START 1
+
+#define FIRMWARE_VERSION 0x3FC
+
+/*Flash reprogramming registers*/
+#define FLASH_ERASE 0x80
+#define FLASH_WRITE 0x84
+#define FLASH_ADDRESS 0x88
+#define FLASH_DATA 0x90
+#define FLASH_BUSY 0x80
+
+enum card_type {
+ PRO_UNK = 0,
+ PRO_DIBCOM,
+};
+
+struct prospero_fifo {
+ u8 buffer_pointer;
+ u8 tuner_number;
+ u16 num_entries;
+};
+
+struct channel_control_bits {
+
+ bool null_packets;
+ bool interrupt_enabled;
+ bool guard_value_enabled;
+ bool channel_enabled;
+
+};
+
+struct prospero_pid_table {
+
+ int pid;
+ u8 bdp;
+ bool pid_enabled;
+
+};
+
+struct stream_data {
+
+ int buffnum;
+ u32 buffer_base_address;
+ u32 sub_base_address;
+ u32 buffer_end_address;
+ u32 last_read;
+ long byte_count;
+ int pids[MAX_PIDS_PER_STREAM + 1];
+ int active;
+ int serviced;
+ struct dvb_demux_feed *feed;
+
+};
+
+struct stream_link {
+ unsigned long buffer_pointer;
+ int demux_id;
+ int count;
+};
+
+enum fifos {
+ GUARD,
+ INTERRUPT,
+};
+
+struct prospero_i2c_adapter {
+ struct prospero_device *p;
+ struct i2c_adapter i2c_adap;
+
+ u8 no_base_addr;
+ int id;
+ int I2C_Command_reg;
+ int I2C_TxRx_reg;
+
+};
+
+struct prospero_demux {
+ u8 *ts_membuf_ptr;
+ u8 *wildcard_membuf_ptr;
+ dma_addr_t ts_cdma;
+ dma_addr_t wildcard_cdma;
+ int Pid_Table_Offset;
+ int Buffer_Definition_Ram;
+ int buffer1_ptr;
+ int buffer2_ptr;
+ int buffers[MAX_STREAMS];
+ int num_pids;
+};
+
+/* Control structure for data definitions */
+struct prospero_device {
+ /* general */
+ struct device *dev; /* for firmware_class */
+
+#define P_STATE_DVB_INIT 0x01
+#define P_STATE_I2C_INIT 0x02
+#define P_STATE_FE_INIT 0x04
+ int init_state;
+
+ /* dvb stuff */
+ struct dvb_adapter dvb_adapter;
+ struct dvb_frontend *fe[MAXIMUM_NUM_OF_FE];
+ struct dvb_net dvbnet;
+
+ struct timer_list timer;
+
+ int num_init;
+ int num_demuxs;
+ int num_frontends;
+ int flash_in_progress;
+ int demux_link[MAX_NUM_OF_DEMUXS];
+
+ struct stream_link *stream_links[MAX_STREAMS];
+
+ int command_seqno;
+
+ struct dvb_demux demux[MAX_NUM_OF_DEMUXS];
+ struct dmxdev dmxdev[MAX_NUM_OF_DEMUXS];
+
+ struct dmx_frontend hw_frontend[MAXIMUM_NUM_OF_FE];
+ struct dmx_frontend dmx_only;
+ int (*fe_sleep)(struct dvb_frontend *);
+
+ struct module *owner;
+
+ void *bus_specific;
+
+ struct stream_data *streams[MAX_STREAMS];
+
+ struct prospero_i2c_adapter p_i2c_adap[MAXIMUM_NUM_OF_FE];
+ struct mutex i2c_mutex;
+
+ struct prospero_demux p_demux[MAXIMUM_NUM_OF_FE];
+
+ /*debugging fifo */
+ int sequence_track;
+ int seqstart;
+ int entry_count;
+ int int_buffer;
+ /*end debugging fifo */
+
+ struct prospero_IR *ir;
+
+ int wild_buffer_increment;
+ int wild_interrupt_increment;
+ int channel_buffer_size;
+ int buffer_increment;
+ int interrupt_increment;
+
+};
+
+struct prospero_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 prospero_pci {
+
+ struct pci_dev *pcidev;
+
+#define P_PCI_INIT 0x01
+#define P_PCI_DMA_INIT 0x02
+ int init_state;
+
+ void __iomem *io_mem;
+ void __iomem *BAR0;
+ u32 irq;
+
+ struct prospero_dma dma;
+
+ int count;
+ int count_prev;
+ int stream_problem;
+
+ spinlock_t irq_lock;
+ unsigned long last_irq;
+
+ struct delayed_work irq_check_work;
+ struct prospero_device *p_dev;
+};
+
+#endif
diff --git a/drivers/media/pci/prospero/prospero_fe.h b/drivers/media/pci/prospero/prospero_fe.h
new file mode 100644
index 0000000..c2aefa3
--- /dev/null
+++ b/drivers/media/pci/prospero/prospero_fe.h
@@ -0,0 +1,5 @@
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+extern int prospero_frontend_init(struct prospero_device *p);
diff --git a/drivers/media/pci/prospero/prospero_fe_main.c b/drivers/media/pci/prospero/prospero_fe_main.c
new file mode 100644
index 0000000..65f10d6
--- /dev/null
+++ b/drivers/media/pci/prospero/prospero_fe_main.c
@@ -0,0 +1,466 @@
+/*
+ * Driver for the frontend modules from Prospero Technology Ltd.
+ *
+ * Copyright Prospero Technology Ltd. 2014
+ * Written/Maintained by Philip Downer
+ * Contact: pdowner@prospero-tech.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;
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/kobject.h>
+
+#include "dvb_frontend.h"
+#include "prospero_common.h"
+#include "prospero_fe.h"
+#include "prospero_i2c.h"
+#include "dib7000p.h"
+#include "dib0090.h"
+
+int ADDRESS_TABLE_START = 0x81000;
+
+struct prospero_adapter_state {
+ int (*set_param_save)(struct dvb_frontend *);
+ struct dib7000p_ops dib7000p_ops;
+} pro_state;
+
+static int dib7090_agc_restart(struct dvb_frontend *fe, u8 restart)
+{
+ pr_debug("agc restart\n");
+ /* before AGC startup */
+ if (restart == 0)
+ dib0090_set_dc_servo(fe, 1);
+
+ 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;
+
+ pr_debug("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;
+ }
+ pr_debug("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;
+ pr_debug("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 dibx000_bandwidth_config pll;
+ u16 target;
+ struct dib7090p_best_adc adc;
+ int ret;
+
+ ret = pro_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_offset(fe) * 8 + 1) / 2;
+ pro_state.dib7000p_ops.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;
+
+ pro_state.dib7000p_ops.update_pll(fe, &pll);
+ pro_state.dib7000p_ops.ctrl_timf(fe, DEMOD_TIMF_SET, adc.timf);
+ }
+ return 0;
+}
+
+static int dib_update_lna_prospero(struct dvb_frontend *fe, uint16_t agc_gloabl)
+{
+
+ pr_debug("called: UPDATE LNA!\n");
+
+ return 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,
+};
+
+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 dib7000p_config nim7090_dib7000p_config = {
+ .output_mpeg2_in_188_bytes = 1,
+ .hostbus_diversity = 1,
+ .tuner_is_baseband = 1,
+ .update_lna = dib_update_lna_prospero,
+
+ .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_SERIAL,
+ .enMpegOutput = 1,
+ .default_i2c_addr = 0x20,
+};
+
+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_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,
+
+ .freq_offset_khz_uhf = 0,
+ .freq_offset_khz_vhf = 0,
+
+ .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,
+};
+
+int prodib_fe_attach(struct i2c_adapter *i2c_adap, int fenum)
+{
+
+ struct prospero_i2c_adapter *p_i2c = i2c_get_adapdata(i2c_adap);
+ struct prospero_device *p = p_i2c->p;
+
+ pr_debug("attach dibcom as fe %d\n", fenum);
+
+ if (!dvb_attach(dib7000p_attach, &pro_state.dib7000p_ops))
+ return -ENODEV;
+
+ p->p_demux[fenum].num_pids = PIDS_START;
+ p->p_demux[fenum].Pid_Table_Offset = PROSPERO_REGISTER_BASE + PID_TABLE_START + (fenum * 0x8000);
+ p->p_demux[fenum].Buffer_Definition_Ram = p->p_demux[fenum].Pid_Table_Offset + 0x4000;
+ p->p_demux[fenum].buffer1_ptr = ADDRESS_TABLE_START + (fenum * 0x8) + (fenum * 0x8);
+ p->p_demux[fenum].buffer2_ptr = p->p_demux[fenum].buffer1_ptr + 0x8;
+
+ if (pro_state.dib7000p_ops.i2c_enumeration(i2c_adap, 1, 0x20, &nim7090_dib7000p_config) != 0) {
+ dev_info(p->dev, "%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__);
+ /*dvb_detach(&pro_state.dib7000p_ops);*/
+ return -ENODEV;
+ }
+
+ p->fe[fenum] = pro_state.dib7000p_ops.init(i2c_adap, 0x80, &nim7090_dib7000p_config);
+
+ dev_info(p->dev, "successfully attached the dib7000\n");
+
+ pr_debug("fe pointer = %p\n", p->fe[fenum]);
+ pr_debug("sub i2c driver = %p\n", (&i2c_adap->dev)->p);
+
+ return p->fe[fenum] == NULL ? -ENODEV : 0;
+
+}
+
+static int prodib_tuner_attach(struct i2c_adapter *i2c_adap, int fenum)
+{
+ struct prospero_i2c_adapter *p_i2c = i2c_get_adapdata(i2c_adap);
+ struct prospero_device *p = p_i2c->p;
+
+ struct i2c_adapter *tun_i2c = pro_state.dib7000p_ops.get_i2c_tuner(p->fe[fenum]);
+
+ if (dvb_attach(dib0090_register, p->fe[fenum], tun_i2c, &nim7090_dib0090_config) == NULL)
+ return -ENODEV;
+
+ pro_state.dib7000p_ops.set_gpio(p->fe[fenum], 8, 0, 1);
+
+ pro_state.set_param_save = p->fe[fenum]->ops.tuner_ops.set_params;
+ p->fe[fenum]->ops.tuner_ops.set_params = dib7090_agc_startup;
+ return 0;
+}
+
+static struct {
+ enum card_type type;
+ int (*attach)(struct i2c_adapter *i2c_adap, int fenum);
+ int (*tuner_attach)(struct i2c_adapter *i2c_adap, int fenum);
+
+} prospero_frontends[] = {
+ {
+ PRO_DIBCOM, prodib_fe_attach, prodib_tuner_attach}
+};
+
+int prospero_frontend_init(struct prospero_device *p)
+{
+
+ int ret = 0;
+ int i = 0;
+ int x = 0;
+
+ struct prospero_pci *p_pci = p->bus_specific;
+
+ p->num_frontends = 0;
+
+ pr_debug("reset all tuners\n");
+ iowrite8(0xFF, p_pci->io_mem + I2C_RESETS);
+ usleep_range(5000, 10000);
+ iowrite8(0x00, p_pci->io_mem + I2C_RESETS);
+ usleep_range(5000, 10000);
+ iowrite8(0xFF, p_pci->io_mem + I2C_RESETS);
+ pr_debug("finished high low high reset");
+ usleep_range(5000, 10000);
+
+ for (i = 0; i < MAXIMUM_NUM_OF_FE; i++) {
+ for (x = 0; x < ARRAY_SIZE(prospero_frontends); x++) {
+ usleep_range(1000, 5000);
+
+ if (prospero_frontends[x].attach(&p->p_i2c_adap[i].i2c_adap, i) < 0) {
+ pr_debug("detach fe\n");
+ if (p->fe[i]) {
+ dvb_frontend_detach(p->fe[i]);
+ p->fe[i] = NULL;
+ }
+ continue;
+ }
+
+ pr_debug("fe attached\n");
+
+ p->fe[i]->id = i;
+
+ ret = dvb_register_frontend(&p->dvb_adapter, p->fe[i]);
+ if (ret < 0)
+ goto exit;
+
+ if (prospero_frontends[x].tuner_attach(&p->p_i2c_adap[i].i2c_adap, i) < 0) {
+ dev_info(p->dev, "tuner init for fe %d failed, detach fe\n", i);
+ if (p->fe[i]) {
+ dvb_frontend_detach(p->fe[i]);
+ p->fe[i] = NULL;
+ }
+ continue;
+
+ }
+
+ p->num_frontends++;
+ break;
+
+ }
+ }
+
+ return ret;
+
+ exit:
+ dvb_unregister_frontend(p->fe[i]);
+ dvb_frontend_detach(p->fe[i]);
+ if (p->num_frontends > 0) {
+ dev_info(p->dev, "found %d DVB frontends\n", p->num_frontends);
+ return 0;
+ }
+
+ return -1;
+
+}
+EXPORT_SYMBOL(prospero_frontend_init);
+
+MODULE_DESCRIPTION("Prospero Frontend");
+MODULE_AUTHOR("Philip Downer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/prospero/prospero_i2c.c b/drivers/media/pci/prospero/prospero_i2c.c
new file mode 100644
index 0000000..831197d
--- /dev/null
+++ b/drivers/media/pci/prospero/prospero_i2c.c
@@ -0,0 +1,449 @@
+/*
+ * I2c driver for PCIe DVB cards from Prospero Technology Ltd.
+ *
+ * Copyright Prospero Technology Ltd. 2014
+ * Written/Maintained by Philip Downer
+ * Contact: pdowner@prospero-tech.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;
+ *
+ */
+
+#include "prospero_i2c.h"
+
+#include <linux/delay.h>
+
+MODULE_LICENSE("GPL");
+
+#define I2C_COMMAND_START 0x01
+#define I2C_COMMAND_WRITE 0x02
+#define I2C_COMMAND_READ_ACK 0x04
+#define I2C_COMMAND_STOP 0x08
+#define I2C_COMMAND_READ_NACK 0x10
+
+static void read_i2c_command(uint8_t *buffer, struct i2c_adapter *i2c_adap)
+{
+ struct prospero_pci *p_pci;
+ int offset;
+ struct prospero_i2c_adapter *p_i2c = i2c_get_adapdata(i2c_adap);
+
+ p_pci = p_i2c->p->bus_specific;
+ offset = p_i2c->I2C_Command_reg;
+
+ *buffer = (ioread8(p_pci->io_mem + offset) & 0x30);
+ pr_debug("ctrl: i2c command (reg %x) is set to: %d\n", offset, *buffer);
+
+}
+
+static void write_i2c_command(uint8_t command, struct i2c_adapter *i2c_adap)
+{
+
+ struct prospero_i2c_adapter *p_i2c = i2c_get_adapdata(i2c_adap);
+ struct prospero_pci *p_pci = p_i2c->p->bus_specific;
+ int offset = p_i2c->I2C_Command_reg;
+
+ pr_debug("ctrl: user wrote %x to i2c command (reg %x)\n", command, offset);
+
+ iowrite8(command, p_pci->io_mem + offset);
+
+}
+
+static void write_i2c_write(uint8_t command, struct i2c_adapter *i2c_adap)
+{
+ struct prospero_i2c_adapter *p_i2c = i2c_get_adapdata(i2c_adap);
+ struct prospero_pci *p_pci = p_i2c->p->bus_specific;
+ int offset = p_i2c->I2C_TxRx_reg;
+
+ pr_debug("user wrote %x to i2c write (reg %x)\n", command, offset);
+
+ iowrite8(command, p_pci->io_mem + offset);
+
+}
+
+static void read_i2c_read(uint8_t *buffer, struct i2c_adapter *i2c_adap)
+{
+ struct prospero_i2c_adapter *p_i2c = i2c_get_adapdata(i2c_adap);
+ struct prospero_pci *p_pci = p_i2c->p->bus_specific;
+ int offset = p_i2c->I2C_TxRx_reg;
+
+ *buffer = ioread8(p_pci->io_mem + offset);
+
+ pr_debug("i2c read (reg %x) is set to: %d\n", offset, *buffer);
+
+}
+
+bool busy_wait(struct i2c_adapter *i2c_adap)
+{
+ uint8_t rByte;
+ uint8_t busy = 0x10;
+ uint8_t device_busy;
+
+ int count;
+
+ for (count = 0; count < 20; count++) {
+ read_i2c_command(&rByte, i2c_adap);
+ device_busy = rByte & busy;
+
+ if (device_busy == 0)
+ return true;
+
+ udelay(200);
+
+ }
+
+ return false;
+
+}
+
+static void StartI2C(struct i2c_adapter *i2c_adap)
+{
+ pr_debug("i2cdbg: Send Start Bit\n");
+
+ /* there needs to be at least a 40 microsecond delay
+ here or the dibcom 7090 can miss the start bit
+ in a conversation */
+ udelay(40);
+ write_i2c_command(I2C_COMMAND_START, i2c_adap);
+
+}
+
+static void StopI2C(struct i2c_adapter *i2c_adap)
+{
+ pr_debug("i2cdbg: Send Stop Bit\n");
+ write_i2c_command(I2C_COMMAND_STOP, i2c_adap);
+}
+
+bool CheckTransferOk(struct i2c_adapter *i2c_adap)
+{
+ uint8_t rByte;
+ uint8_t error = 0x20;
+ bool RetVal;
+
+ RetVal = false;
+ read_i2c_command(&rByte, i2c_adap);
+ if ((rByte & error) == 0)
+ RetVal = true;
+
+ return RetVal;
+}
+
+bool WriteToWire(uint8_t tuner, uint8_t Data, struct i2c_adapter *i2c_adap)
+{
+ bool RetVal;
+
+ RetVal = false;
+ pr_debug("i2cdbg: write 0x%x\n", Data);
+
+ write_i2c_write(Data, i2c_adap);
+ write_i2c_command(I2C_COMMAND_WRITE, i2c_adap);
+ if (busy_wait(i2c_adap) && (CheckTransferOk(i2c_adap)))
+ RetVal = true;
+
+ return RetVal;
+}
+
+bool ReadFromWire(uint8_t *buffer, struct i2c_adapter *i2c_adap, bool ack)
+{
+ bool RetVal;
+ int command = I2C_COMMAND_READ_NACK;
+
+ RetVal = false;
+
+ if (ack)
+ command = I2C_COMMAND_READ_ACK;
+
+ write_i2c_command(command, i2c_adap);
+ if (busy_wait(i2c_adap) && (CheckTransferOk(i2c_adap))) {
+ read_i2c_read(buffer, i2c_adap);
+ RetVal = true;
+ }
+
+ pr_debug("i2cdbg: read 0x%x\n", *buffer);
+ return RetVal;
+}
+
+bool WriteToI2c_msg(u8 addr, uint8_t *buffer, uint8_t len, struct i2c_adapter *i2c_adap)
+{
+
+ uint8_t Cnt;
+ uint8_t tuner;
+ bool RetVal;
+ char dstring[50] = "0x";
+ char tstring[7];
+ char vstring[50] = "0x";
+
+ RetVal = false;
+ tuner = 0x0;
+
+ if (busy_wait(i2c_adap)) {
+ RetVal = false;
+ StartI2C(i2c_adap);
+ if (WriteToWire(tuner, addr, i2c_adap)) {
+
+ for (Cnt = 0; Cnt < len; Cnt++) {
+ sprintf(tstring, "%02x", buffer[Cnt]);
+
+ if (Cnt < 2)
+ strcat(dstring, tstring);
+ else
+ strcat(vstring, tstring);
+
+ if (!WriteToWire(tuner, buffer[Cnt], i2c_adap)) {
+ StopI2C(i2c_adap);
+ break;
+ }
+ }
+ StopI2C(i2c_adap);
+ RetVal = true;
+ }
+ }
+ pr_debug("i2cdbg2: wrote to reg %s value %s i2c address 0x%x\n", dstring, vstring, addr);
+
+ return RetVal;
+}
+
+bool WriteToI2cImmediate(u8 iDeviceAddr, u8 *bData, uint8_t len, struct i2c_adapter *i2c_adap)
+{
+ uint8_t Cnt;
+ uint8_t tuner = 0x0;
+ bool RetVal;
+ char dstring[50] = "0x";
+ char tstring[7];
+ char vstring[50] = "0x";
+
+ RetVal = false;
+
+ StartI2C(i2c_adap);
+ if (WriteToWire(tuner, iDeviceAddr, i2c_adap)) {
+ for (Cnt = 0; Cnt < len; Cnt++) {
+ sprintf(tstring, "%02x", bData[Cnt]);
+
+ if (Cnt < 2)
+ strcat(dstring, tstring);
+ else
+ strcat(vstring, tstring);
+
+ RetVal = false;
+ if (WriteToWire(tuner, bData[Cnt], i2c_adap))
+ RetVal = true;
+
+ if (!RetVal)
+ break;
+ }
+ }
+
+ StopI2C(i2c_adap);
+ pr_debug("i2cdbg2: wrote to reg %s value %s i2c address 0x%x\n", dstring, vstring, iDeviceAddr);
+
+ return RetVal;
+}
+
+static int ReadFromI2cImmediate(u8 iDeviceAddr, u8 regAddr, u8 *bBuffer, u8 len, struct i2c_adapter *i2c_adap)
+{
+ uint8_t tuner = 0x0;
+ int RetVal = -1;
+
+ StartI2C(i2c_adap);
+
+ if (!WriteToWire(tuner, iDeviceAddr, i2c_adap))
+ goto exit;
+
+ if (!WriteToWire(tuner, regAddr, i2c_adap))
+ goto exit;
+
+ StartI2C(i2c_adap);
+
+ if (!WriteToWire(tuner, iDeviceAddr | 0x01, i2c_adap))
+ goto exit;
+
+ if (!ReadFromWire(&bBuffer[0], i2c_adap, 0))
+ goto exit;
+
+ StopI2C(i2c_adap);
+ RetVal = 1;
+
+ exit:
+ pr_debug("i2cdbg2: *immediate* Read 0x%02x from %02x i2c address %02x; retval = %d",
+ bBuffer[0], regAddr, iDeviceAddr, RetVal);
+ return RetVal;
+}
+
+static int ReadFromI2c_msg(u8 addr, u8 *uRegIndex, u8 *buffer, u8 len, struct i2c_adapter *i2c_adap)
+{
+
+ /*16 bit register address read routine*/
+ bool ack = 0;
+ u8 Cnt = -1;
+ u8 tuner = 0x0;
+ bool RetVal;
+
+ RetVal = false;
+
+ if (!busy_wait(i2c_adap))
+ goto exit;
+
+ StartI2C(i2c_adap);
+
+ if (!WriteToWire(tuner, addr, i2c_adap))
+ goto exit;
+
+ if (!WriteToWire(tuner, uRegIndex[0], i2c_adap))
+ goto exit;
+
+ if (!WriteToWire(tuner, uRegIndex[1], i2c_adap))
+ goto exit;
+
+ StopI2C(i2c_adap);
+
+ StartI2C(i2c_adap);
+
+ if (!WriteToWire(tuner, addr | 0x01, i2c_adap))
+ goto exit;
+
+ for (Cnt = 0; Cnt < len; Cnt++) {
+ if (Cnt < (len - 1)) {
+ /* send an ack if we aren't finished reading*/
+ ack = 1;
+ } else {
+ ack = 0;
+ }
+
+ if (ReadFromWire(&buffer[Cnt], i2c_adap, ack))
+ pr_debug("RFI: 7\n");
+ else
+ break;
+
+ }
+
+
+ exit:
+ if (Cnt == 2)
+ pr_debug("i2cdbg2: Read 0x%02x%02x from 0x%02x%02x i2c address 0x%02x\n", buffer[0], buffer[1], uRegIndex[0], uRegIndex[1], addr);
+
+ return Cnt;
+}
+
+static int prospero_i2c_msg(int busywait, struct i2c_adapter *i2c_adap, uint8_t addr, uint8_t *wbuf, u16 wlen, uint8_t *rbuf, u16 rlen)
+{
+ int wo = (rbuf == NULL || rlen == 0);
+ int retval = -1;
+
+ if (wo) {
+ if (busywait)
+ retval = WriteToI2c_msg(addr, wbuf, wlen, i2c_adap);
+ else
+ retval = WriteToI2cImmediate(addr, wbuf, wlen, i2c_adap);
+
+ pr_debug("i2c: wrote 0x%x to i2c to address 0x%x\n", *wbuf, addr);
+
+ } else {
+ if (busywait)
+ retval = ReadFromI2c_msg(addr, wbuf, rbuf, rlen, i2c_adap);
+ else
+ retval = ReadFromI2cImmediate(addr, *wbuf, rbuf, rlen, i2c_adap);
+
+ }
+ return retval;
+}
+
+/* master xfer callback for demodulator */
+static int prospero_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+ struct prospero_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap);
+ int i;
+
+ for (i = 0; i < num; i++) {
+ if (i + 1 < num && (msgs[i].flags & I2C_M_RD) == 0 && (msgs[i + 1].flags & I2C_M_RD)) {
+ /* reading */
+ if ((msgs[i].flags & I2C_M_IGNORE_NAK) || (msgs[i + 1].flags & I2C_M_IGNORE_NAK)) {
+ if (prospero_i2c_msg(0, i2c_adap, msgs[i].addr, msgs[i].buf, msgs[i].len, msgs[i + 1].buf, msgs[i + 1].len) < 0)
+ break;
+
+ } else {
+
+ if (prospero_i2c_msg(1, i2c_adap, msgs[i].addr, msgs[i].buf, msgs[i].len, msgs[i + 1].buf, msgs[i + 1].len) < 0)
+ break;
+
+ }
+
+ i = i + 2; /* skip the following message */
+
+ } else {
+ /* writing */
+ if (msgs[i].flags & I2C_M_IGNORE_NAK) {
+ if (prospero_i2c_msg(0, i2c_adap, msgs[i].addr, msgs[i].buf, msgs[i].len, NULL, 0) < 0)
+ break;
+
+ } else {
+ if (prospero_i2c_msg(1, i2c_adap, msgs[i].addr, msgs[i].buf, msgs[i].len, NULL, 0) < 0)
+ break;
+
+ }
+ }
+ }
+
+ mutex_unlock(&i2c->p->i2c_mutex);
+
+ return (i - 1);
+}
+
+static u32 prospero_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm prospero_algo = {
+ .master_xfer = prospero_master_xfer,
+ .functionality = prospero_i2c_func,
+};
+
+int prospero_i2c_init(struct prospero_device *p)
+{
+ int ret;
+ int i;
+
+ mutex_init(&p->i2c_mutex);
+
+ for (i = 0; i < MAXIMUM_NUM_OF_FE; i++) {
+
+ p->p_i2c_adap[i].p = p;
+
+ strlcpy(p->p_i2c_adap[i].i2c_adap.name, "Prospero I2C to demod", sizeof(p->p_i2c_adap[i].i2c_adap.name));
+
+ i2c_set_adapdata(&p->p_i2c_adap[i].i2c_adap, &p->p_i2c_adap[i]);
+
+ p->p_i2c_adap[i].i2c_adap.algo = &prospero_algo;
+ p->p_i2c_adap[i].i2c_adap.algo_data = NULL;
+ p->p_i2c_adap[i].i2c_adap.dev.parent = p->dev;
+
+ p->p_i2c_adap[i].I2C_Command_reg = PROSPERO_REGISTER_BASE + (i * 8);
+ p->p_i2c_adap[i].I2C_TxRx_reg = p->p_i2c_adap[i].I2C_Command_reg + 0x04;
+
+ ret = i2c_add_adapter(&p->p_i2c_adap[i].i2c_adap);
+ if (ret < 0)
+ return ret;
+
+ }
+
+ p->init_state |= P_STATE_I2C_INIT;
+ return 0;
+}
+
+void prospero_i2c_exit(struct prospero_device *p)
+{
+ if (p->init_state & P_STATE_I2C_INIT)
+ i2c_del_adapter(&p->p_i2c_adap[0].i2c_adap);
+
+ p->init_state &= ~P_STATE_I2C_INIT;
+}
diff --git a/drivers/media/pci/prospero/prospero_i2c.h b/drivers/media/pci/prospero/prospero_i2c.h
new file mode 100644
index 0000000..19f65d2
--- /dev/null
+++ b/drivers/media/pci/prospero/prospero_i2c.h
@@ -0,0 +1,3 @@
+#include "prospero_common.h"
+
+int prospero_i2c_init(struct prospero_device *p);
diff --git a/drivers/media/pci/prospero/prospero_ir.c b/drivers/media/pci/prospero/prospero_ir.c
new file mode 100644
index 0000000..01e5204
--- /dev/null
+++ b/drivers/media/pci/prospero/prospero_ir.c
@@ -0,0 +1,150 @@
+/*
+ * Infra-red driver for PCIe DVB cards from Prospero Technology Ltd.
+ *
+ * Copyright Prospero Technology Ltd. 2014
+ * Written/Maintained by Philip Downer
+ * Contact: pdowner@prospero-tech.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.
+ *
+ */
+
+#include <media/rc-core.h>
+#include "prospero_ir.h"
+
+#define DURATION_MASK 0x7FFFF
+#define PULSE_MASK 0x1000000
+#define FIFO_FILL_MASK 0xFF
+
+#define FIFO_FILL 0x60
+#define FIFO 0x64
+
+struct prospero_IR {
+ struct prospero_device *pdev;
+ struct rc_dev *dev;
+
+ int users;
+
+ char name[32];
+ char phys[32];
+};
+
+static int prospero_ir_open(struct rc_dev *rc)
+{
+ struct prospero_device *p = rc->priv;
+
+ p->ir->users++;
+ return 0;
+
+}
+
+static void prospero_ir_close(struct rc_dev *rc)
+{
+ struct prospero_device *p = rc->priv;
+
+ p->ir->users--;
+
+}
+
+void ir_interrupt(struct prospero_pci *p_pci)
+{
+
+ struct prospero_device *p = p_pci->p_dev;
+ struct prospero_IR *ir = p->ir;
+ struct ir_raw_event ev;
+ int tmp = 0;
+ int fill = 0;
+ int pulse = 0;
+ int duration = 0;
+
+ pr_debug("Infra: Interrupt!\n");
+
+ tmp = ioread32(p_pci->io_mem + FIFO_FILL);
+ fill = tmp & FIFO_FILL_MASK;
+
+ init_ir_raw_event(&ev);
+
+ while (fill > 0) {
+
+ pr_debug("Infra: fifo fill = %d\n", fill);
+
+ tmp = ioread32(p_pci->io_mem + FIFO);
+ pr_debug("Infra: raw dump = 0x%x\n", tmp);
+ pulse = (tmp & PULSE_MASK) >> 24;
+ duration = (tmp & DURATION_MASK) * 1000; /* Convert uS to nS */
+
+ pr_debug("Infra: pulse = %d; duration = %d\n", pulse, duration);
+
+ ev.pulse = pulse;
+ ev.duration = duration;
+ ir_raw_event_store_with_filter(ir->dev, &ev);
+ fill--;
+ }
+ ir_raw_event_handle(ir->dev);
+
+}
+
+int prospero_ir_init(struct prospero_device *p)
+{
+
+ struct prospero_pci *p_pci = p->bus_specific;
+ struct pci_dev *pci = p_pci->pcidev;
+ struct prospero_IR *ir;
+ struct rc_dev *dev;
+ int err = -ENOMEM;
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+
+ dev = rc_allocate_device();
+
+ if (!ir || !dev)
+ goto err_out_free;
+
+ ir->dev = dev;
+
+ snprintf(ir->name, sizeof(ir->name), "prospero IR");
+ 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;
+ dev->input_id.vendor = pci->vendor;
+ dev->input_id.product = pci->device;
+
+ dev->dev.parent = &pci->dev;
+ dev->map_name = RC_MAP_LIRC;
+
+ dev->driver_name = "prospero";
+ dev->priv = p;
+ dev->open = prospero_ir_open;
+ dev->close = prospero_ir_close;
+ dev->driver_type = RC_DRIVER_IR_RAW;
+ dev->timeout = 10 * 1000 * 1000;
+
+ iowrite32(0x12000, p_pci->io_mem + FIFO_FILL);
+
+ ir->pdev = p;
+ p->ir = ir;
+
+ err = rc_register_device(dev);
+ if (err)
+ goto err_out_free;
+
+ return 0;
+
+ err_out_free:
+ rc_free_device(dev);
+ p->ir = NULL;
+ kfree(ir);
+ return -ENOMEM;
+
+}
diff --git a/drivers/media/pci/prospero/prospero_ir.h b/drivers/media/pci/prospero/prospero_ir.h
new file mode 100644
index 0000000..fa8cee3
--- /dev/null
+++ b/drivers/media/pci/prospero/prospero_ir.h
@@ -0,0 +1,4 @@
+#include "prospero_common.h"
+
+void ir_interrupt(struct prospero_pci *p_pci);
+int prospero_ir_init(struct prospero_device *p);
diff --git a/drivers/media/pci/prospero/prospero_main.c b/drivers/media/pci/prospero/prospero_main.c
new file mode 100644
index 0000000..89a1dd2
--- /dev/null
+++ b/drivers/media/pci/prospero/prospero_main.c
@@ -0,0 +1,2086 @@
+/*
+ * Driver for PCIe DVB cards from Prospero Technology Ltd.
+ *
+ * Copyright Prospero Technology Ltd. 2014
+ * Written/Maintained by Philip Downer
+ * Contact: pdowner@prospero-tech.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.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/time.h>
+#include <asm/byteorder.h>
+#include <linux/kfifo.h>
+#include <linux/errno.h>
+
+#include "prospero_common.h"
+#include "prospero_fe.h"
+#include "prospero_i2c.h"
+#include "prospero_ir.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static struct prospero_device *pro;
+
+MODULE_LICENSE("GPL");
+
+#define P_LOG_PREFIX "prospero"
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_NAME "Prospero Technologies Digital Video Broadcast PCI Driver"
+#define DRIVER_AUTHOR "Philip Downer <pdowner@prospero-tech.com>"
+
+#define DMX_WRITE_LOCATION 0x58
+#define DMX_WRITE_CTRL 0x5C
+
+int DVB_INT_STATUS_MASK = 0x1;
+int IR_INT_FIFO_FILLING_MASK = 0x2;
+int IR_INT_TIMEOUT_MASK = 0x4;
+int PCI_INTERRUPTS = 0x80050;
+
+int ts_buffer_size = 4194304;
+int wildcard_buffer_size = 1048476;
+
+module_param(ts_buffer_size, int, 0);
+MODULE_PARM_DESC(ts_buffer_size, "Change the transport stream buffer size.");
+
+module_param(wildcard_buffer_size, int, 0);
+MODULE_PARM_DESC(wildcard_buffer_size, "Change the wildcard transport stream buffer size.");
+
+struct prospero_device *prospero_device_kmalloc(size_t bus_specific_len)
+{
+ void *bus;
+ struct prospero_device *p = kzalloc(sizeof(struct prospero_device),
+ GFP_KERNEL);
+ if (!p)
+ return NULL;
+
+ bus = kzalloc(bus_specific_len, GFP_KERNEL);
+ if (!bus) {
+ kfree(p);
+ return NULL;
+ }
+
+ p->bus_specific = bus;
+
+ return p;
+}
+
+static int prospero_get_fifo(struct device *dev, struct prospero_fifo *fifo, enum fifos fifo_type)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+
+ struct prospero_pci *p_pci = pdev->bus_specific;
+
+ int offset;
+
+ u32 tmp;
+
+ switch (fifo_type) {
+ case GUARD:
+ pr_debug("guard fifo read\n");
+ offset = GUARD_BAND_FIFO;
+ break;
+ case INTERRUPT:
+ pr_debug("interrupt fifo read\n");
+ offset = INTERRUPT_FIFO;
+ break;
+ default:
+ return -EIO;
+ };
+
+ tmp = ioread32(p_pci->io_mem + offset);
+
+ fifo->buffer_pointer = tmp & FIFO_BUFF_PTR_MASK;
+ fifo->tuner_number = ((tmp & FIFO_TUNER_NUM_MASK) >> 8);
+ fifo->num_entries = (tmp & FIFO_NUM_ENTRIES_MASK) >> 16;
+
+ return 0;
+
+}
+
+static int prospero_get_buffer_details(struct device *dev, int buffnum, u32 *buf, u8 buf_offset, int stream_id)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ struct prospero_pci *p_pci = pdev->bus_specific;
+ struct stream_link *stream_l = pdev->stream_links[stream_id];
+ int demux_id = stream_l->demux_id;
+ int fenum = pdev->demux_link[demux_id];
+ int offset = pdev->p_demux[fenum].Buffer_Definition_Ram + (CONTROL_BITS_OFFSET + (BUFFER_SLOT_SIZE * buffnum)) + buf_offset;
+
+ *buf = (ioread32(p_pci->io_mem + offset));
+ return 0;
+
+}
+
+static int prospero_set_buffer_details(struct device *dev, u32 buf, u8 buf_offset, int stream_id)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ struct prospero_pci *p_pci = pdev->bus_specific;
+ struct stream_link *stream_l = pdev->stream_links[stream_id];
+ int demux_id = stream_l->demux_id;
+ int fenum = pdev->demux_link[demux_id];
+ int buffnum = pdev->streams[stream_id]->buffnum;
+ int offset = pdev->p_demux[fenum].Buffer_Definition_Ram + (CONTROL_BITS_OFFSET + (BUFFER_SLOT_SIZE * buffnum)) + buf_offset;
+
+ iowrite32(buf, p_pci->io_mem + offset);
+ pr_debug("set buffer_details at offset %x to %x\n", offset, buf);
+ return 0;
+
+}
+
+static int prospero_get_buffer_base_address(struct device *dev, u32 *buf, int stream_id)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ struct prospero_pci *p_pci = pdev->bus_specific;
+ struct stream_link *stream_l = pdev->stream_links[stream_id];
+ int demux_id = stream_l->demux_id;
+ int fenum = pdev->demux_link[demux_id];
+ u32 tmp;
+ int buffnum = pdev->streams[stream_id]->buffnum;
+ int offset = pdev->p_demux[fenum].Buffer_Definition_Ram + (CONTROL_BITS_OFFSET + (BUFFER_SLOT_SIZE * buffnum)) + BUFFER_BASE_ADDRESS;
+
+ tmp = (ioread32(p_pci->io_mem + offset));
+
+ *buf = tmp & BIT_MASK_26;
+
+ pr_debug("buffer_base_address(%p) = %x\n", p_pci->io_mem + offset, *buf);
+
+ return 0;
+
+}
+
+static int prospero_set_buffer_base_address(struct device *dev, u32 buf, int stream_id)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ struct prospero_pci *p_pci = pdev->bus_specific;
+ int ret = 0;
+ struct stream_link *stream_l = pdev->stream_links[stream_id];
+ int demux_id = stream_l->demux_id;
+ int fenum = pdev->demux_link[demux_id];
+ int buffnum = pdev->streams[stream_id]->buffnum;
+ int offset = pdev->p_demux[fenum].Buffer_Definition_Ram + (CONTROL_BITS_OFFSET + (BUFFER_SLOT_SIZE * buffnum)) + BUFFER_BASE_ADDRESS;
+
+ if (buf > BIT_MASK_26) {
+ ret = -EIO;
+ pr_err("Error (prospero_set_buffer_base_address) the value passed in is too large\n");
+
+ } else {
+ iowrite32(buf, p_pci->io_mem + offset);
+ pr_debug("set buffer_details at offset %x to %x\n", offset, buf);
+
+ }
+
+ return ret;
+
+}
+
+static int prospero_set_buffer_end_address(struct device *dev, u32 buf, int stream_id)
+{
+
+ int ret = 0;
+
+ /* the buffer end address is one packet (188 bytes) less than the
+ * buffer size */
+
+ buf = buf - 188;
+ pr_debug("buffer %d end address = %x bit_mask_20 = %x", stream_id, buf, BIT_MASK_22);
+ ret = prospero_set_buffer_details(dev, buf, BUFFER_END_ADDRESS, stream_id);
+
+ return ret;
+
+}
+
+static int prospero_get_guard_value_A(struct device *dev, int buffnum, u32 *buf, int stream_id)
+{
+
+ u32 tmp;
+ int ret;
+
+ ret = prospero_get_buffer_details(dev, buffnum, &tmp, GUARD_A, stream_id);
+ *buf = tmp & BIT_MASK_22;
+
+ return ret;
+
+}
+
+static int prospero_set_guard_value_A(struct device *dev, u32 buf, int stream_id)
+{
+
+ int ret = 0;
+
+ if (buf > BIT_MASK_22) {
+ ret = -EIO;
+ pr_err("Error (prospero_set_guard_value_A) the value passed in is too large\n");
+
+ } else {
+ ret = prospero_set_buffer_details(dev, buf, GUARD_A, stream_id);
+
+ }
+
+ return ret;
+
+}
+
+static int prospero_get_int_trigger_address(struct device *dev, int buffnum, u32 *buf, int stream_id)
+{
+
+ u32 tmp;
+ int ret;
+
+ ret = prospero_get_buffer_details(dev, buffnum, &tmp, INT_TRIGGER_ADDRESS, stream_id);
+ *buf = tmp & BIT_MASK_22;
+
+ return ret;
+
+}
+
+static int prospero_set_int_trigger_address(struct device *dev, int buffnum, u32 buf, int stream_id)
+{
+
+ int ret = 0;
+
+ if (buf > BIT_MASK_22) {
+ ret = -EIO;
+ pr_err("Error (prospero_set_int_trigger_address) the value passed in is too large\n");
+ } else {
+ pr_debug("set interrupt trigger: buf = %x, id = %d", buf, stream_id);
+ ret = prospero_set_buffer_details(dev, buf, INT_TRIGGER_ADDRESS, stream_id);
+
+ }
+
+ return ret;
+
+}
+
+static int prospero_get_current_write_pointer_A(struct device *dev, int buffnum, u32 *buf, int stream_id)
+{
+
+ u32 tmp = 0;
+ int ret = 0;
+
+ ret = prospero_get_buffer_details(dev, buffnum, &tmp, CURRENT_WP_A, stream_id);
+ *buf = tmp & BIT_MASK_22;
+
+ return ret;
+
+}
+
+static int prospero_set_current_write_pointer_A(struct device *dev, int buffnum, u32 buf, int stream_id)
+{
+
+ int ret = 0;
+
+ ret = prospero_set_buffer_details(dev, buf, CURRENT_WP_A, stream_id);
+
+ return ret;
+
+}
+
+static int prospero_get_control_bits(struct device *dev, struct channel_control_bits *cbits, int buffer, int stream_id)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ struct prospero_pci *p_pci = pdev->bus_specific;
+ u32 temp;
+ struct stream_link *stream_l = pdev->stream_links[stream_id];
+ int demux_id = stream_l->demux_id;
+ int fenum = pdev->demux_link[demux_id];
+ int offset;
+
+ offset = pdev->p_demux[fenum].Buffer_Definition_Ram + (CONTROL_BITS_OFFSET + (BUFFER_SLOT_SIZE * buffer));
+
+ temp = (ioread32(p_pci->io_mem + offset)) & 0xF;
+
+ cbits->null_packets = temp & NULL_PACKET;
+ cbits->interrupt_enabled = temp & INTERRUPT_ENABLED;
+ cbits->guard_value_enabled = temp & GUARD_VALUE;
+ cbits->channel_enabled = temp & CHANNEL_ENABLED;
+
+ return 0;
+}
+
+static int prospero_set_channel_interrupt(struct device *dev, u32 cb, int stream_id)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ struct prospero_pci *p_pci = pdev->bus_specific;
+ struct stream_link *stream_l = pdev->stream_links[stream_id];
+ int demux_id = stream_l->demux_id;
+ u32 temp;
+ int fenum = pdev->demux_link[demux_id];
+ int buffnum = pdev->streams[stream_id]->buffnum;
+ int offset;
+
+ offset = pdev->p_demux[fenum].Buffer_Definition_Ram + (CONTROL_BITS_OFFSET + (BUFFER_SLOT_SIZE * buffnum));
+ temp = (ioread32(p_pci->io_mem + offset)) & 0xB;
+ temp = (cb << 2) | temp;
+ iowrite32(temp, p_pci->io_mem + offset);
+
+ return 0;
+
+}
+
+static int prospero_set_guard_value_enabled(struct device *dev, u32 cb, int stream_id)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ struct prospero_pci *p_pci = pdev->bus_specific;
+ u32 temp;
+ struct stream_link *stream_l = pdev->stream_links[stream_id];
+ int demux_id = stream_l->demux_id;
+ int fenum = pdev->demux_link[demux_id];
+ int buffnum = pdev->streams[stream_id]->buffnum;
+ int offset = pdev->p_demux[fenum].Buffer_Definition_Ram + (CONTROL_BITS_OFFSET + (BUFFER_SLOT_SIZE * buffnum));
+
+ temp = (ioread32(p_pci->io_mem + offset)) & 0xD;
+ temp = (cb << 1) | temp;
+ iowrite32(temp, p_pci->io_mem + offset);
+
+ return 0;
+
+}
+
+static int prospero_set_channel_enabled(struct device *dev, u32 cb, int stream_id)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ struct prospero_pci *p_pci = pdev->bus_specific;
+ u32 temp;
+ u32 temp1;
+ struct stream_link *stream_l = pdev->stream_links[stream_id];
+ int demux_id = stream_l->demux_id;
+ int fenum = pdev->demux_link[demux_id];
+ int buffnum = pdev->streams[stream_id]->buffnum;
+ int offset = pdev->p_demux[fenum].Buffer_Definition_Ram + (CONTROL_BITS_OFFSET + (BUFFER_SLOT_SIZE * buffnum));
+
+ temp = (ioread32(p_pci->io_mem + offset)) & 0xE;
+ temp = cb | temp;
+
+ iowrite32(temp, p_pci->io_mem + offset);
+ temp1 = ioread32(p_pci->io_mem + offset);
+
+ pr_debug("pid, wrote %x to control bits at mem %p", temp, (p_pci->io_mem + offset));
+ return 0;
+
+}
+
+static int prospero_get_pid_table(struct device *dev, int stream_id, struct prospero_pid_table *pid_table, int i)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ struct prospero_pci *p_pci = pdev->bus_specific;
+ u32 tmp_pid;
+ struct stream_link *stream_l = pdev->stream_links[stream_id];
+ int demux_id = stream_l->demux_id;
+ int fenum = pdev->demux_link[demux_id];
+
+ tmp_pid = (ioread32(p_pci->io_mem + pdev->p_demux[fenum].Pid_Table_Offset + (PID_TABLE_SLOT_SIZE * i)));
+ pid_table->pid = tmp_pid & 0x3FFF;
+ pid_table->pid_enabled = (tmp_pid & 0x4000) >> 14;
+ pid_table->bdp = (tmp_pid & 0x3F0000) >> 16;
+
+ return 0;
+
+}
+
+static int prospero_get_pid_slot(struct device *dev, int stream_id, u16 pid, int *slot)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ int i;
+ int z;
+ struct prospero_pid_table tmp_pid;
+ int free_slot = -1;
+ int ret = 0;
+ struct dvb_frontend *fe;
+ struct stream_link *stream_l = pdev->stream_links[stream_id];
+ int demux_id = stream_l->demux_id;
+ int fenum = pdev->demux_link[demux_id];
+
+ fe = pdev->fe[fenum];
+
+ for (i = PIDS_START; i <= pdev->p_demux[fenum].num_pids; i++) {
+ prospero_get_pid_table(dev, stream_id, &tmp_pid, i);
+
+ if ((tmp_pid.pid_enabled == 0) && (free_slot < 0)) {
+ free_slot = i;
+
+ } else if ((tmp_pid.pid == pid) && (tmp_pid.pid_enabled)) {
+ for (z = PIDS_START; z <= pdev->p_demux[fenum].num_pids; z++)
+ prospero_get_pid_table(dev, stream_id, &tmp_pid, z);
+
+ ret = -EIO;
+ }
+ }
+
+ if (free_slot < 0) {
+ pr_err("Error! no free slots available for pid %d on tuner %d\n", pid, fenum);
+ ret = -EIO;
+ } else {
+ pr_debug("pid %d allocated to free slot = %d on tuner %d\n", pid, free_slot, fenum);
+ *slot = free_slot;
+ }
+
+ return ret;
+}
+
+static int get_stream_id(struct prospero_device *p, int *stream_id, unsigned long buff_id)
+{
+
+ int i;
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ if (p->stream_links[i] == NULL)
+ goto exit;
+
+
+ if (p->stream_links[i]->buffer_pointer == buff_id) {
+ *stream_id = i;
+
+ return 0;
+ }
+ }
+
+ exit:
+ return -EIO;
+
+}
+
+static int set_stream_id(struct prospero_device *p, int *stream_id, unsigned long buff_id, int demux_id)
+{
+
+ int i;
+ struct stream_link *s_link;
+ int ret = -EIO;
+
+ ret = get_stream_id(p, stream_id, buff_id);
+ if (ret >= 0) {
+ ret = 0;
+ goto exit;
+ }
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+
+ if (p->stream_links[i] == NULL) {
+
+ p->stream_links[i] = kmalloc(sizeof(struct stream_link), GFP_KERNEL);
+ if (p->stream_links[i] == NULL) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ s_link = p->stream_links[i];
+ s_link->buffer_pointer = buff_id;
+ s_link->demux_id = demux_id;
+ s_link->count = 1;
+
+ *stream_id = i;
+ ret = 0;
+ goto exit;
+
+ } else if (p->stream_links[i]->buffer_pointer == 0) {
+
+ s_link = p->stream_links[i];
+ s_link->buffer_pointer = buff_id;
+ s_link->count = 1;
+ s_link->demux_id = demux_id;
+ *stream_id = i;
+ ret = 0;
+ goto exit;
+
+ }
+ }
+
+ exit:
+ return ret;
+
+}
+
+static int prospero_find_pid(struct device *dev, int stream_id, u16 pid, int *slot)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ int i;
+ struct prospero_pid_table tmp_pid;
+ struct dvb_frontend *fe;
+ struct stream_link *stream_l = pdev->stream_links[stream_id];
+ int demux_id = stream_l->demux_id;
+ int fenum = pdev->demux_link[demux_id];
+
+ fe = pdev->fe[fenum];
+ i = PIDS_START;
+ prospero_get_pid_table(dev, stream_id, &tmp_pid, i);
+
+ while (tmp_pid.pid >= 0) {
+ if ((tmp_pid.pid == pid) && (tmp_pid.pid_enabled)) {
+ *slot = i;
+ return 0;
+ }
+
+ i++;
+ prospero_get_pid_table(dev, stream_id, &tmp_pid, i);
+
+ }
+
+ pr_err("Error couldn't find pid!\n");
+ return -EIO;
+
+}
+
+static int prospero_disable_pid(struct device *dev, struct dvb_demux_feed *feed)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ struct prospero_pci *p_pci = pdev->bus_specific;
+ struct dmx_ts_feed *ts_feed;
+ struct dmx_section_feed *sec_feed;
+ struct dmxdev_filter *dmxdevfilter;
+ struct dmxdev *demux_dev;
+ struct dvb_device *dvbdev;
+ u32 tmp;
+ int slot = 0;
+ u32 demux_id;
+ int offset;
+ int ret = 0;
+ int fenum = 0;
+ int stream_id = 0;
+ unsigned long buff_id = 0;
+
+ if (feed->type == DMX_TYPE_TS) {
+ ts_feed = (struct dmx_ts_feed *)feed;
+ dmxdevfilter = ts_feed->priv;
+
+ } else if (feed->type == DMX_TYPE_SEC) {
+ sec_feed = (struct dmx_section_feed *)feed;
+ dmxdevfilter = sec_feed->priv;
+
+ } else {
+ return -ENOTSUPP;
+ }
+
+ demux_dev = dmxdevfilter->dev;
+ dvbdev = demux_dev->dvbdev;
+ demux_id = dvbdev->id;
+ fenum = pdev->demux_link[demux_id];
+ buff_id = (long)&dmxdevfilter->buffer;
+
+ ret = get_stream_id(pdev, &stream_id, buff_id);
+ pr_debug("prospero_disable_pid demux device id = %d", stream_id);
+
+ ret = prospero_find_pid(dev, stream_id, feed->pid, &slot);
+
+ if (ret == 0) {
+ if (feed->pid != WILD_PID)
+ pdev->p_demux[fenum].num_pids--;
+
+
+ offset = pdev->p_demux[fenum].Pid_Table_Offset + (PID_TABLE_SLOT_SIZE * slot);
+ tmp = ioread32(p_pci->io_mem + offset);
+ tmp = tmp & 0x3F3FFF;
+ iowrite32(tmp, p_pci->io_mem + offset);
+
+ }
+
+ return ret;
+}
+
+static int prospero_set_pid(struct device *dev, struct dvb_demux_feed *feed)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ struct prospero_pci *p_pci = pdev->bus_specific;
+ struct dmx_section_feed *sec_feed;
+ struct dmx_ts_feed *ts_feed;
+ struct dmxdev_filter *dmxdevfilter;
+ struct dmxdev *demux_dev;
+ struct dvb_device *dvbdev;
+ struct stream_data *stream;
+ u32 tmp;
+ int slot = -1;
+ u32 demux_id = 0;
+ int offset;
+ int ret = 0;
+ int i;
+ struct prospero_pid_table tmp_pid_table;
+ int fenum = 0;
+ int stream_id = 0;
+ unsigned long buff_id;
+
+ if (feed->type == DMX_TYPE_TS) {
+
+ ts_feed = (struct dmx_ts_feed *)feed;
+ dmxdevfilter = ts_feed->priv;
+
+ } else if (feed->type == DMX_TYPE_SEC) {
+
+ sec_feed = (struct dmx_section_feed *)feed;
+ dmxdevfilter = sec_feed->priv;
+
+ } else {
+ return -ENOTSUPP;
+ }
+
+ demux_dev = dmxdevfilter->dev;
+ dvbdev = demux_dev->dvbdev;
+ demux_id = dvbdev->id;
+ fenum = pdev->demux_link[demux_id];
+ buff_id = (long)&dmxdevfilter->buffer;
+
+ ret = get_stream_id(pdev, &stream_id, buff_id);
+ stream = pdev->streams[stream_id];
+
+ pr_debug("set pid to %x\n", feed->pid);
+
+ if (feed->pid == WILD_PID) {
+ pr_debug("Wildcard PID!\n");
+ slot = 0;
+ stream->pids[0] = slot;
+
+ } else {
+ ret = prospero_get_pid_slot(dev, stream_id, feed->pid, &slot);
+ if (ret == 0) {
+ pdev->p_demux[fenum].num_pids++;
+ for (i = 0; i < MAX_PIDS_PER_STREAM; i++) {
+ if (stream->pids[i] < 0) {
+ stream->pids[i] = slot;
+ goto done;
+ }
+ }
+ }
+ }
+
+ done:
+ if (slot >= 0) {
+
+ offset = pdev->p_demux[fenum].Pid_Table_Offset + (PID_TABLE_SLOT_SIZE * slot);
+
+ /* set the PID to enabled. */
+ tmp = feed->pid | 0x4000 | (stream->buffnum << 16);
+
+ iowrite32(tmp, p_pci->io_mem + offset);
+ prospero_get_pid_table(dev, stream_id, &tmp_pid_table, slot);
+
+ }
+
+ return ret;
+
+}
+
+void prospero_device_kfree(struct prospero_device *p)
+{
+ kfree(p->bus_specific);
+ kfree(p);
+}
+
+static int prospero_clear_interrupt(struct prospero_pci *p_pci, int device)
+{
+
+ int offset = INTERRUPT_CONTROL;
+
+ iowrite8(device, p_pci->io_mem + offset);
+ return 0;
+
+}
+
+static void tsCopy(u8 *ptr, size_t bytes, struct dvb_demux_feed *dvbdmxfeed, int fenum)
+{
+
+ int b = 641644;
+ u8 *wptr;
+ u8 *end;
+
+ struct dmx_ts_feed *feed = &dvbdmxfeed->feed.ts;
+ struct dmxdev_filter *dmxdevfilter = feed->priv;
+
+ if (dmxdevfilter->params.pes.output == DMX_OUT_TAP || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP) {
+ pr_debug("mtune copy\n");
+ dvbdmxfeed->cb.ts(ptr, bytes, 0, 0, &dvbdmxfeed->feed.ts, 0);
+
+ } else {
+
+ if (bytes <= b) {
+ pr_debug("normal copy %d\n", bytes);
+ dvbdmxfeed->cb.ts(ptr, bytes, 0, 0, &dvbdmxfeed->feed.ts, 0);
+
+ } else {
+
+ pr_debug("wildcard copy\n");
+ wptr = ptr;
+ end = ptr + bytes;
+
+ while ((wptr + b) < end) {
+ pr_debug("wcp copy from %p, copy %d bytes, end = %p\n", wptr, b, (wptr + b));
+
+ dvbdmxfeed->cb.ts(wptr, b, 0, 0, &dvbdmxfeed->feed.ts, 0);
+ wptr += b;
+ }
+
+ pr_debug("wcp END! copy from %p, copy %d bytes, end = %p\n", wptr, (end - wptr), (end));
+ dvbdmxfeed->cb.ts(wptr, (end - wptr), 0, 0, &dvbdmxfeed->feed.ts, 0);
+
+ }
+ }
+}
+
+static void get_data(struct prospero_device *pdev, int fenum, int buffnum)
+{
+
+ struct stream_data *stream;
+ struct device *dev = pdev->dev;
+ struct dvb_demux *demux;
+ struct dvb_demux_feed *dvbdmxfeed;
+ u8 *bufferptr = 0;
+ u8 *ptr = 0;
+ uint32_t tempgp;
+ struct timeval tv;
+ size_t bytes;
+ u32 byte_count = 0;
+ u32 bba;
+ u32 interrupt_address;
+ u32 curr_write = 0;
+ u32 curr_write1;
+ int buffer_size;
+ int demux_id = fenum;
+ int stream_id = pdev->p_demux[fenum].buffers[buffnum];
+
+ demux = &pdev->demux[demux_id];
+ stream = pdev->streams[stream_id];
+ dvbdmxfeed = stream->feed;
+
+ bytes = 0;
+ byte_count = 0;
+
+ /* The wildcard pid uses the whole buffer, normal pids have their own
+ * channel buffer which is within the larger buffer
+ */
+
+ if (dvbdmxfeed->pid == WILD_PID)
+ buffer_size = wildcard_buffer_size;
+ else
+ buffer_size = pdev->channel_buffer_size;
+
+ do_gettimeofday(&tv);
+
+ pr_debug("prospero: interrupt for buffer %x", buffnum);
+
+ prospero_get_current_write_pointer_A(dev, buffnum, &curr_write, stream_id);
+ prospero_get_current_write_pointer_A(dev, buffnum, &curr_write1, 0);
+ prospero_get_current_write_pointer_A(dev, buffnum, &curr_write, stream_id);
+
+ pr_debug("prospero: Current Write Pointer for id %d = %x\n", stream_id, curr_write);
+
+ prospero_get_buffer_base_address(dev, &bba, stream_id);
+
+ if (dvbdmxfeed->pid == WILD_PID) {
+ bufferptr = pdev->p_demux[fenum].wildcard_membuf_ptr;
+ pr_debug("buffer = %p : wildcard\n", bufferptr);
+ } else {
+ bufferptr = pdev->p_demux[fenum].ts_membuf_ptr;
+ pr_debug("buffer = %p : normal\n", bufferptr);
+ }
+
+ pr_debug("last_read = %d, curr_write = %d\n", stream->last_read, curr_write);
+
+ prospero_get_guard_value_A(dev, buffnum, &tempgp, stream_id);
+ prospero_get_int_trigger_address(dev, buffnum, &interrupt_address, stream_id);
+
+ if (curr_write > stream->last_read) {
+
+ bytes = curr_write - stream->last_read;
+ byte_count = bytes;
+ ptr = bufferptr + stream->last_read;
+
+ pr_debug("cp1 stream_id = %d: copy from %p, copy %d bytes, end = %p\n", stream_id, ptr, bytes, (ptr + bytes));
+
+ tsCopy(ptr, bytes, dvbdmxfeed, fenum);
+
+ } else if (curr_write != stream->last_read) {
+
+ bytes = (buffer_size - (stream->last_read - stream->sub_base_address));
+ byte_count = bytes;
+ ptr = bufferptr + stream->last_read;
+ pr_debug("cp2 stream_id = %d: copy from %p, copy %d bytes, end = %p\n", stream_id, ptr, bytes, (ptr + bytes));
+ tsCopy(ptr, bytes, dvbdmxfeed, fenum);
+
+ if (curr_write > stream->sub_base_address) {
+
+ bytes = (curr_write - stream->sub_base_address);
+ byte_count += bytes;
+ ptr = (bufferptr + stream->sub_base_address);
+ pr_debug("cp3 stream_id = %d: copy from %p, copy %d bytes, end = %p\n", stream_id, ptr, bytes, (ptr + bytes));
+ tsCopy(ptr, bytes, dvbdmxfeed, fenum);
+
+ } else {
+ pr_debug("skip copy 3 tempgp = %d; curr_write = %d; base= %d\n", tempgp, curr_write, stream->sub_base_address);
+ }
+
+ pr_debug("stream %d bytes copied = %d\n", stream_id, byte_count);
+
+ }
+
+ stream->byte_count += byte_count;
+
+ pr_debug("stream %d total bytes copied = %ld : 0x%lx\n", stream_id, stream->byte_count, stream->byte_count);
+
+ stream->last_read = curr_write;
+
+ if (dvbdmxfeed->pid == WILD_PID) {
+ tempgp = ((curr_write + pdev->wild_buffer_increment) % buffer_size) + stream->sub_base_address;
+ interrupt_address = ((curr_write + pdev->wild_interrupt_increment) % buffer_size) + stream->sub_base_address;
+
+ } else {
+ tempgp = ((curr_write + pdev->buffer_increment) % buffer_size) + stream->sub_base_address;
+ interrupt_address = ((curr_write + pdev->interrupt_increment) % buffer_size) + stream->sub_base_address;
+
+ }
+
+ prospero_set_guard_value_A(dev, tempgp, stream_id);
+ prospero_set_int_trigger_address(dev, buffnum, interrupt_address, stream_id);
+
+}
+
+void prospero_irq_tasklet(unsigned long pdev)
+{
+
+ struct prospero_device *p = (struct prospero_device *)(*((struct prospero_device **)pdev));
+ struct dvb_frontend *fe;
+ int stream_id;
+ struct prospero_fifo guard_fifo;
+ int cleared = 0;
+
+ int interrupt_buffer = p->int_buffer;
+ int zero = 0;
+ int i = 0;
+
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+
+ pr_debug("tasklet! %ld\n", tv.tv_usec);
+
+ p->entry_count = 0;
+
+ pr_debug("\nstart irq\n");
+ do {
+ if (i > 10)
+ pr_debug("gfifo num_entries is %d!\n", i);
+
+ prospero_get_fifo(p->dev, &guard_fifo, GUARD);
+ pr_debug("guard buffer = %d; guard_tuner = %d; int buffer=%d\n", guard_fifo.buffer_pointer, guard_fifo.tuner_number, interrupt_buffer);
+ if (guard_fifo.buffer_pointer == interrupt_buffer)
+ cleared = 1;
+
+
+ if (guard_fifo.num_entries > 0) {
+ if (i == 0)
+ p->entry_count = guard_fifo.num_entries;
+
+ else if (guard_fifo.num_entries != (p->entry_count - 1))
+ pr_debug("entry count differs gfifo = %d, orig = %d\n", guard_fifo.num_entries, p->entry_count);
+
+
+ pr_debug("buffer_pointer = %d, tuner_number = %d, num_entries = %d\n", guard_fifo.buffer_pointer, guard_fifo.tuner_number, guard_fifo.num_entries);
+
+ fe = p->fe[guard_fifo.tuner_number];
+ stream_id = p->p_demux[guard_fifo.tuner_number].buffers[guard_fifo.buffer_pointer];
+
+ get_data(p, guard_fifo.tuner_number, guard_fifo.buffer_pointer);
+
+ p->streams[stream_id]->serviced = 1;
+
+ } else {
+ pr_debug("gfifo! zero entries, don't read!\n");
+ zero = 1;
+ }
+ pr_debug("i = %d; entry = %d\n", i, p->entry_count);
+
+ i++;
+
+ } while (i < p->entry_count);
+
+ pr_debug("end irq\n\n");
+
+ if ((cleared != 1) && (zero == 0))
+ pr_debug("interrupt buffer wasn't cleared\n");
+
+}
+
+DECLARE_TASKLET(prospero_tasklet, prospero_irq_tasklet, (unsigned long)&pro);
+
+void dvb_interrupt(struct prospero_pci *p_pci)
+{
+
+ struct prospero_device *p = p_pci->p_dev;
+ struct prospero_fifo int_fifo;
+
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+
+ pr_debug("interrupt! %ld\n", tv.tv_usec);
+
+ do {
+ prospero_get_fifo(p->dev, &int_fifo, INTERRUPT);
+
+ p->int_buffer = int_fifo.buffer_pointer;
+ pr_debug("interrupt! buffer_pointer = %d, tuner = %d, num_entries = %d; p->int_buffer = %d\n", int_fifo.buffer_pointer, int_fifo.tuner_number, int_fifo.num_entries, p->int_buffer);
+
+ } while (int_fifo.num_entries > 1);
+
+ tasklet_schedule(&prospero_tasklet);
+
+}
+
+static irqreturn_t prospero_pci_isr(int irq, void *dev_id)
+{
+
+ struct prospero_pci *p_pci = dev_id;
+ int status;
+ unsigned long flags;
+ irqreturn_t ret = IRQ_HANDLED;
+
+ spin_lock_irqsave(&p_pci->irq_lock, flags);
+ status = ioread8(p_pci->io_mem + INTERRUPT_CONTROL);
+
+ /* check for DVB Interrupts */
+ if (status & DVB_INT_STATUS_MASK) {
+ dvb_interrupt(p_pci);
+ prospero_clear_interrupt(p_pci, DVB_INT_STATUS_MASK);
+ }
+ /* check for IR Interrupts */
+ if (status & IR_INT_TIMEOUT_MASK) {
+ ir_interrupt(p_pci);
+ prospero_clear_interrupt(p_pci, IR_INT_TIMEOUT_MASK);
+ }
+
+ if (status & IR_INT_FIFO_FILLING_MASK) {
+ ir_interrupt(p_pci);
+ prospero_clear_interrupt(p_pci, IR_INT_FIFO_FILLING_MASK);
+ }
+
+ spin_unlock_irqrestore(&p_pci->irq_lock, flags);
+
+ return ret;
+
+}
+
+static int prospero_pci_init(struct prospero_pci *p_pci)
+{
+
+ u8 card_rev;
+ u16 vendor;
+ u16 device;
+ int ret;
+ u32 interrupts;
+
+ pci_read_config_word(p_pci->pcidev, PCI_VENDOR_ID, &vendor);
+ pr_info("vendor ID = %x", vendor);
+
+ pci_read_config_word(p_pci->pcidev, PCI_DEVICE_ID, &device);
+ pr_info("device ID = %x", device);
+
+ pci_read_config_byte(p_pci->pcidev, PCI_CLASS_REVISION, &card_rev);
+ pr_info("card revision %x", card_rev);
+
+ ret = pci_enable_device(p_pci->pcidev);
+ if (ret != 0) {
+ pr_err("Enabling prospero pci device failed");
+ return ret;
+
+ }
+
+ pr_debug("Prospero pci dvb-T card enabled");
+
+ pci_set_master(p_pci->pcidev);
+
+ ret = pci_request_regions(p_pci->pcidev, DRIVER_NAME);
+ if (ret != 0) {
+ pr_err("Failed to allocate pci memory regions");
+ goto err_pci_disable_device;
+
+ }
+
+ pr_debug("allocated pci memory regions");
+
+ /* map the BAR */
+ p_pci->io_mem = pci_iomap(p_pci->pcidev, 2, 0);
+
+ if (!p_pci->io_mem) {
+ pr_err("cannot map io memory\n");
+ ret = -EIO;
+ goto err_pci_release_regions;
+
+ }
+
+ p_pci->BAR0 = pci_iomap(p_pci->pcidev, 0, 0);
+
+ if (!p_pci->BAR0) {
+ pr_err("cannot map io memory\n");
+ ret = -EIO;
+ goto err_pci_release_regions;
+
+ }
+
+ pci_set_drvdata(p_pci->pcidev, p_pci);
+ spin_lock_init(&p_pci->irq_lock);
+
+ ret = request_irq(p_pci->pcidev->irq, prospero_pci_isr, IRQF_SHARED, DRIVER_NAME, p_pci);
+ if (ret != 0)
+ goto err_pci_iounmap;
+
+ /* enable the interrupts in the pci registers of the prospero card */
+ iowrite32(0x000000FF, p_pci->io_mem + PCI_INTERRUPTS);
+ interrupts = ioread32(p_pci->io_mem + PCI_INTERRUPTS);
+
+ p_pci->init_state |= P_PCI_INIT;
+
+ return ret;
+
+ err_pci_iounmap:
+ pci_iounmap(p_pci->pcidev, p_pci->io_mem);
+ pci_set_drvdata(p_pci->pcidev, NULL);
+ err_pci_release_regions:
+ pci_release_regions(p_pci->pcidev);
+ err_pci_disable_device:
+ pci_disable_device(p_pci->pcidev);
+ return ret;
+
+}
+
+int prospero_frontend_exit(struct prospero_device *p)
+{
+
+ int i;
+
+ for (i = 0; i < p->num_frontends; i++) {
+ if (dvb_unregister_frontend(p->fe[i]))
+ pr_err("failed to unregister frontend %d\n", i);
+
+ dvb_frontend_detach(p->fe[i]);
+ }
+
+ return 0;
+
+}
+
+static void prospero_dvb_exit(struct prospero_device *p)
+{
+ int dev = 0;
+
+ if (p->init_state & P_STATE_DVB_INIT) {
+
+ while (p->num_init > 0) {
+
+ pr_debug("num_init = %d", p->num_init);
+
+ dev = p->num_init - 1;
+
+ pr_debug("dev = %d\n", dev);
+
+ p->demux[dev].dmx.close(&p->demux[dev].dmx);
+
+ if (dev == 0) {
+ pr_debug("dmx.remove hw_frontend\n");
+ p->demux[dev].dmx.remove_frontend(&p->demux[dev].dmx, &p->hw_frontend[dev]);
+ }
+
+ dvb_dmxdev_release(&p->dmxdev[dev]);
+ dvb_dmx_release(&p->demux[dev]);
+ p->num_init--;
+ }
+
+ prospero_frontend_exit(p);
+
+ dvb_unregister_adapter(&p->dvb_adapter);
+
+ }
+ p->init_state &= ~P_STATE_DVB_INIT;
+
+}
+
+static void prospero_pci_exit(struct prospero_device *prospero_dev)
+{
+
+ struct prospero_pci *p_pci = prospero_dev->bus_specific;
+
+ prospero_dvb_exit(prospero_dev);
+
+ if (p_pci->init_state & P_PCI_INIT) {
+
+ free_irq(p_pci->pcidev->irq, p_pci);
+ pci_iounmap(p_pci->pcidev, p_pci->io_mem);
+ pci_set_drvdata(p_pci->pcidev, NULL);
+ pci_release_regions(p_pci->pcidev);
+ pci_disable_device(p_pci->pcidev);
+ }
+ p_pci->init_state &= ~P_PCI_INIT;
+
+}
+
+static int prospero_enable_interrupts(struct prospero_pci *p_pci)
+{
+
+ int offset = INTERRUPT_ENABLE;
+
+ /* we now write 0x3 to enable the infra-red interrupts */
+ iowrite8(0x03, p_pci->io_mem + offset);
+
+ return 0;
+
+}
+
+static int prospero_disable_interrupts(struct prospero_pci *p_pci)
+{
+
+ int offset = INTERRUPT_ENABLE;
+
+ /* this disables the infra-red interrupts */
+ iowrite8(0x00, p_pci->io_mem + offset);
+
+ return 0;
+
+}
+
+static int get_buffer(struct prospero_device *pdev, int fenum, int *buffernum, int stream_id)
+{
+
+ /* this routine should find a free buffer space from the
+ * buffer definition ram for use in start_feed */
+
+ struct channel_control_bits cbits;
+ int i;
+
+ pr_debug("get_buffer for frontend %d, stream %d", fenum, stream_id);
+
+ for (i = 0; i < STREAMS_PER_DEMUX; i++) {
+
+ cbits.interrupt_enabled = 0;
+ cbits.guard_value_enabled = 0;
+ cbits.channel_enabled = 0;
+
+ prospero_get_control_bits(pdev->dev, &cbits, i, stream_id);
+
+ pr_debug("interrupt_enabled = %d, guard_value_enabled = %d, channel_enabled = %d\n", cbits.interrupt_enabled, cbits.guard_value_enabled, cbits.channel_enabled);
+
+ if (!cbits.channel_enabled) {
+ *buffernum = i;
+ return 0;
+ }
+
+ }
+
+ pr_err("couldn't find a spare buffer for frontend %d, stream %d\n", fenum, stream_id);
+
+ return -EIO;
+
+}
+
+static int prospero_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+
+ struct prospero_device *p = dvbdmxfeed->demux->priv;
+ struct prospero_pci *p_pci = p->bus_specific;
+
+ struct dmx_ts_feed *ts_feed;
+ struct dmx_section_feed *sec_feed;
+ struct dmxdev_filter *dmxdevfilter;
+ struct dmxdev *demux_dev;
+ struct dvb_device *dvbdev;
+
+ u32 demux_id = 0;
+ int stream_id = 0;
+ unsigned long buff_id = 0;
+ int ret = 0;
+
+ struct channel_control_bits cbits;
+ struct stream_data *stream = 0;
+ float seconds = 0.05;
+ float jiffyCount = 0;
+ int fenum = 0;
+ u32 curr_write = 0;
+ int buffnum = 0;
+
+ jiffyCount = HZ * seconds;
+
+ p->seqstart = 1;
+ p->sequence_track = 0;
+
+ if (dvbdmxfeed->type == DMX_TYPE_TS) {
+ pr_debug("TS feed!\n");
+ ts_feed = &dvbdmxfeed->feed.ts;
+ dmxdevfilter = ts_feed->priv;
+
+ } else if (dvbdmxfeed->type == DMX_TYPE_SEC) {
+ pr_debug("section feed!\n");
+
+ sec_feed = &dvbdmxfeed->feed.sec;
+
+ pr_debug("is_filtering = %d\n", sec_feed->is_filtering);
+
+ dmxdevfilter = sec_feed->priv;
+
+ pr_debug("devfilter setup\n");
+
+ } else {
+ return -ENOTSUPP;
+ }
+
+ demux_dev = dmxdevfilter->dev;
+ dvbdev = demux_dev->dvbdev;
+
+ demux_id = dvbdev->id;
+ buff_id = (long)&dmxdevfilter->buffer;
+ fenum = p->demux_link[demux_id];
+
+ ret = set_stream_id(p, &stream_id, buff_id, demux_id);
+
+ pr_debug("fenum = %d, demux_id = %d, stream_id = %d\n", fenum, demux_id, stream_id);
+
+ if ((p->streams[stream_id] == NULL) || (p->streams[stream_id]->active == 0)) {
+ if (p->streams[stream_id] == NULL) {
+
+ p->streams[stream_id] = kmalloc(sizeof(struct stream_data), GFP_KERNEL);
+ if (p->streams[stream_id] == NULL) {
+ ret = -EIO;
+ goto exit;
+ }
+ }
+
+ stream = p->streams[stream_id];
+ stream->buffnum = 0;
+
+ if (dvbdmxfeed->pid == WILD_PID) {
+ stream->buffer_base_address = 0 + (fenum << 23) + (1 << 22);
+ stream->sub_base_address = 0;
+ stream->buffer_end_address = stream->buffer_base_address + wildcard_buffer_size;
+ stream->feed = dvbdmxfeed;
+
+ } else {
+ ret = get_buffer(p, fenum, &buffnum, stream_id);
+ if (ret >= 0) {
+ stream->buffnum = buffnum;
+ stream->buffer_base_address = 0 + (fenum << 23) + (p->channel_buffer_size * buffnum);
+ stream->sub_base_address = (p->channel_buffer_size * buffnum);
+ stream->buffer_end_address = p->channel_buffer_size * (stream->buffnum + 1);
+ stream->feed = dvbdmxfeed;
+
+ } else {
+ pr_err("couldn't allocate a buffer!\n");
+ goto exit;
+ }
+
+ }
+
+ stream->last_read = stream->sub_base_address;
+ prospero_set_current_write_pointer_A(p->dev, buffnum, stream->last_read, stream_id);
+
+ stream->byte_count = 0;
+ prospero_get_current_write_pointer_A(p->dev, buffnum, &curr_write, stream_id);
+
+ memset(&stream->pids[0], -1, sizeof(stream->pids));
+
+ } else {
+ p->stream_links[stream_id]->count++;
+ stream = p->streams[stream_id];
+ buffnum = stream->buffnum;
+
+ if (dvbdmxfeed->pid != WILD_PID) {
+ stream->buffer_base_address = 0 + (fenum << 23) + (p->channel_buffer_size * buffnum);
+ stream->sub_base_address = (p->channel_buffer_size * buffnum);
+ stream->buffer_end_address = p->channel_buffer_size * (stream->buffnum + 1);
+
+ } else {
+ stream->buffer_base_address = 0 + (fenum << 23) + (1 << 22);
+ stream->sub_base_address = stream->buffer_base_address;
+ stream->buffer_end_address = stream->buffer_base_address + wildcard_buffer_size;
+
+ }
+
+ stream->last_read = stream->sub_base_address;
+ prospero_set_current_write_pointer_A(p->dev, buffnum, stream->last_read, stream_id);
+
+ }
+
+ /* set the guard pointers and enable channel */
+ prospero_get_control_bits(p->dev, &cbits, stream->buffnum, stream_id);
+
+ if (!cbits.guard_value_enabled) {
+ prospero_set_buffer_base_address(p->dev, stream->buffer_base_address, stream_id);
+ prospero_set_buffer_end_address(p->dev, stream->buffer_end_address, stream_id);
+
+ if (dvbdmxfeed->pid == WILD_PID)
+ prospero_set_guard_value_A(p->dev, (stream->sub_base_address + p->wild_buffer_increment), stream_id);
+ else
+ prospero_set_guard_value_A(p->dev, (stream->sub_base_address + p->buffer_increment), stream_id);
+
+
+ prospero_set_guard_value_enabled(p->dev, 1, stream_id);
+
+ }
+
+ prospero_get_control_bits(p->dev, &cbits, stream->buffnum, stream_id);
+
+ if (!cbits.interrupt_enabled) {
+ if (dvbdmxfeed->pid == WILD_PID)
+ prospero_set_int_trigger_address(p->dev, buffnum, (stream->sub_base_address + p->wild_interrupt_increment), stream_id);
+ else
+ prospero_set_int_trigger_address(p->dev, buffnum, (stream->sub_base_address + p->interrupt_increment), stream_id);
+
+ prospero_set_channel_interrupt(p->dev, 1, stream_id);
+
+ }
+
+ prospero_get_control_bits(p->dev, &cbits, stream->buffnum, stream_id);
+
+ if ((cbits.guard_value_enabled) && (cbits.interrupt_enabled)) {
+ p->p_demux[fenum].buffers[buffnum] = stream_id;
+ prospero_set_channel_enabled(p->dev, 1, stream_id);
+
+ stream->active = 1;
+ stream->serviced = 0;
+
+ }
+
+ ret = prospero_set_pid(p->dev, dvbdmxfeed);
+ if (ret != 0)
+ goto exit;
+
+ iowrite8(0xFF, p_pci->io_mem + TS_CAP_ENABLE);
+
+ prospero_get_control_bits(p->dev, &cbits, stream->buffnum, stream_id);
+ stream = p->streams[stream_id];
+
+ pr_debug("start feed, demux index = %d\n", dvbdmxfeed->index);
+
+ exit:
+ return ret;
+
+}
+
+static int prospero_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+
+ struct prospero_device *p = dvbdmxfeed->demux->priv;
+ struct dmx_ts_feed *ts_feed;
+ struct dmx_section_feed *sec_feed;
+ struct dmxdev_filter *dmxdevfilter;
+ int ret = 0;
+ int stream_id = 0;
+ unsigned long buff_id;
+
+ if (dvbdmxfeed->type == DMX_TYPE_TS) {
+ ts_feed = (struct dmx_ts_feed *)dvbdmxfeed;
+ dmxdevfilter = ts_feed->priv;
+
+ } else if (dvbdmxfeed->type == DMX_TYPE_SEC) {
+ sec_feed = (struct dmx_section_feed *)dvbdmxfeed;
+ dmxdevfilter = sec_feed->priv;
+
+ } else {
+ return -ENOTSUPP;
+ }
+
+ buff_id = (long)&dmxdevfilter->buffer;
+
+ ret = get_stream_id(p, &stream_id, buff_id);
+
+ /* need to clear the pid table */
+ ret = prospero_disable_pid(p->dev, dvbdmxfeed);
+
+ p->streams[stream_id]->active = 0;
+
+ /* disable the stream */
+ p->stream_links[stream_id]->count--;
+ if (p->stream_links[stream_id]->count < 1)
+ p->stream_links[stream_id]->buffer_pointer = 0;
+
+ pr_debug("stop feed stream %d buffer pointer = %lx", stream_id, p->stream_links[stream_id]->buffer_pointer);
+
+ prospero_set_guard_value_enabled(p->dev, 0, stream_id);
+ prospero_set_channel_interrupt(p->dev, 0, stream_id);
+ prospero_set_channel_enabled(p->dev, 0, stream_id);
+
+ return 0;
+
+}
+
+static int init_demux(int fenum, int devnum, struct prospero_device *p)
+{
+
+ int ret;
+
+ p->demux[devnum].dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+ p->demux[devnum].priv = p;
+ p->demux[devnum].filternum = p->demux[devnum].feednum = 255;
+ p->demux[devnum].start_feed = prospero_dvb_start_feed;
+ p->demux[devnum].stop_feed = prospero_dvb_stop_feed;
+ p->demux[devnum].write_to_decoder = NULL;
+
+ /*
+ * Initialise the demux
+ * This doesn't start the feeds etc, just sets the demux
+ * settings to default values and allocates memory
+ */
+ ret = dvb_dmx_init(&p->demux[devnum]);
+ if (ret < 0) {
+ pr_err("demux initialisation failed: error %d", ret);
+ goto err_dmx;
+ }
+
+ p->hw_frontend[fenum].source = fenum + 1;
+
+ pr_debug("setting demux %d to frontend %d", devnum, fenum);
+
+ p->dmxdev[devnum].filternum = p->demux[devnum].feednum;
+ p->dmxdev[devnum].demux = &p->demux[devnum].dmx;
+ p->dmxdev[devnum].capabilities = 0;
+
+ ret = dvb_dmxdev_init(&p->dmxdev[devnum], &p->dvb_adapter);
+ if (ret < 0) {
+ pr_err("demux device initialisation failed: error %d", ret);
+ goto err_dmx_dev;
+ }
+
+ ret = p->demux[devnum].dmx.add_frontend(&p->demux[devnum].dmx, &p->hw_frontend[fenum]);
+ if (ret < 0) {
+ pr_err("adding hw_frontend to demux failed: error %d", ret);
+ goto err_dmx_add_hw_frontend;
+ }
+
+ ret = p->demux[devnum].dmx.connect_frontend(&p->demux[devnum].dmx, &p->hw_frontend[fenum]);
+ if (ret < 0) {
+ pr_err("connect frontend failed: error %d", ret);
+ goto err_connect_frontend;
+ }
+
+ p->demux_link[devnum] = fenum;
+ return 0;
+
+ err_connect_frontend:
+ p->demux[devnum].dmx.remove_frontend(&p->demux[devnum].dmx, &p->hw_frontend[fenum]);
+
+ err_dmx_add_hw_frontend:
+ dvb_dmxdev_release(&p->dmxdev[devnum]);
+
+ err_dmx_dev:
+ dvb_dmx_release(&p->demux[devnum]);
+
+ err_dmx:
+ return ret;
+
+}
+
+static int prospero_demux_init(struct prospero_device *p)
+{
+ int x;
+ int num_init = 0;
+ int fenum = 0;
+ int ret = 0;
+ int max_fe = p->num_frontends;
+
+ for (x = 0; x < max_fe; x++) {
+
+ ret = init_demux(fenum, x, p);
+ if (ret == 0) {
+ num_init++;
+ p->num_demuxs++;
+
+ if (fenum < (max_fe - 1))
+ fenum++;
+ else
+ fenum = 0;
+ }
+ }
+
+ if (num_init < 1)
+ goto err_dmx_init;
+
+ p->num_init = num_init;
+ return 0;
+
+ err_dmx_init:
+ pr_err("error during demux initialisation");
+ dvb_unregister_adapter(&p->dvb_adapter);
+ return -EIO;
+}
+
+static int prospero_dvb_init(struct prospero_device *p)
+{
+
+ int ret = dvb_register_adapter(&p->dvb_adapter,
+ "Prospero Digital TV device", p->owner,
+ p->dev, adapter_nr);
+ if (ret < 0) {
+ pr_err("error registering DVB adapter");
+ return ret;
+ }
+
+ p->dvb_adapter.priv = p;
+
+ dev_set_drvdata(p->dev, p);
+
+ p->num_demuxs = 0;
+
+ /* initialise the command sequence number */
+ p->command_seqno = 1;
+
+ p->init_state |= P_STATE_DVB_INIT;
+
+ return 0;
+
+}
+
+void prospero_device_exit(struct prospero_device *p)
+{
+ prospero_dvb_exit(p);
+}
+
+static ssize_t show_firmware_version(struct device *dev, struct device_attribute *attr, char *buf)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+ struct prospero_pci *p_pci = pdev->bus_specific;
+
+ u8 version;
+
+ version = ioread8(p_pci->io_mem + FIRMWARE_VERSION);
+
+ return sprintf(buf, "%x", version);
+
+}
+
+static ssize_t store_firmware_version(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+
+ unsigned int tmp;
+ uint8_t leds;
+ int ret;
+
+ ret = kstrtouint(buf, 16, &tmp);
+ if (ret != 0)
+ return 0;
+
+ leds = tmp;
+
+ /* placeholder routine for writing to the firmware version */
+
+ return count;
+
+}
+
+static ssize_t show_fwflash(struct device *dev, struct device_attribute *attr, char *buf)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+
+ pr_debug("flash in progress %d\n", pdev->flash_in_progress);
+
+ return sprintf(buf, "%d\n", pdev->flash_in_progress);
+
+}
+
+static ssize_t show_led_status(struct device *dev, struct device_attribute *attr, char *buf)
+{
+
+ /*struct prospero_device *pdev = to_prospero_device(dev);
+ struct prospero_device *pdev = container_of(dev, struct prospero_device, dev );*/
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+
+ /* struct prospero_pci *p_pci = container_of(pdev, struct prospero_pci, pdev); */
+
+ struct prospero_pci *p_pci = pdev->bus_specific;
+
+ uint8_t leds;
+
+ leds = ioread8(p_pci->BAR0 + LED_OFFSET);
+
+ pr_debug("leds are set to: %d\n", leds);
+
+ return sprintf(buf, "%d", leds);
+
+}
+
+static ssize_t store_leds(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+
+ struct prospero_pci *p_pci = pdev->bus_specific;
+
+ unsigned int tmp;
+ uint8_t leds;
+ int ret;
+
+ ret = kstrtouint(buf, 16, &tmp);
+ if (ret != 0)
+ return 0;
+
+ leds = tmp;
+
+ pr_debug("user wrote %x to leds\n", leds);
+
+ iowrite8(leds, p_pci->BAR0 + LED_OFFSET);
+
+ return count;
+
+}
+
+static void eeprom_erase(struct prospero_pci *p_pci)
+{
+
+ int address = 0x000000;
+ int tmp = 0;
+
+ pr_debug("starting the erase cycle\n");
+
+ iowrite32(address, p_pci->io_mem + FLASH_ADDRESS);
+ iowrite8(0x1, p_pci->io_mem + FLASH_ERASE);
+ do {
+ tmp = ioread8(p_pci->io_mem + FLASH_BUSY);
+ } while (tmp != 0);
+
+ pr_debug("end of the erase cycle\n");
+
+}
+
+static void send_file(struct prospero_pci *p_pci, const struct firmware *fw)
+{
+
+ int address = 0x00;
+ int x = 0;
+ int bytes = 0;
+ /* int loop = fw->size / 256; */
+ uint8_t data;
+ int dcp = 0;
+ uint8_t tmp;
+
+ dev_info(p_pci->p_dev->dev, "sending fw file\n");
+ pr_debug("firmware file size = %d", fw->size);
+
+ while (address < fw->size) {
+ if ((fw->size - address) > 256)
+ bytes = 256;
+ else
+ bytes = fw->size - address;
+
+
+ for (x = 0; x < 256; x++) {
+ do {
+ tmp = ioread8(p_pci->io_mem + FLASH_BUSY);
+ pr_debug("FLASH_busy!\n");
+ } while (tmp != 0);
+
+ if (x < bytes) {
+ memcpy(&data, fw->data + dcp, 1);
+ iowrite8(data, p_pci->io_mem + FLASH_DATA);
+ } else {
+ iowrite8(0xFF, p_pci->io_mem + FLASH_DATA);
+ }
+
+ dcp++;
+ }
+
+ do {
+ tmp = ioread8(p_pci->io_mem + FLASH_BUSY);
+ pr_debug("FLASH_busy!\n");
+ } while (tmp != 0);
+
+ iowrite32(address, p_pci->io_mem + FLASH_ADDRESS);
+ iowrite8(0x1, p_pci->io_mem + FLASH_WRITE);
+
+ do {
+ tmp = ioread8(p_pci->io_mem + FLASH_BUSY);
+ } while (tmp != 0);
+
+ address = address + 256;
+
+ }
+
+ dev_info(p_pci->p_dev->dev, "finished sending fw\n");
+
+}
+
+static ssize_t store_fwflash(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+
+ int input = 0;
+ int ret = 0;
+
+ char name[] = "prospero_fw.bin";
+
+ const struct firmware *fw = NULL;
+
+ struct prospero_device *pdev = dev_get_drvdata(dev);
+
+ struct prospero_pci *p_pci = pdev->bus_specific;
+
+ dev_info(dev, "firmware update!");
+
+ if (pdev->flash_in_progress > 0)
+ goto error;
+
+ pdev->flash_in_progress = 1;
+
+ ret = kstrtouint(buf, 16, &input);
+ if (ret != 0)
+ return 0;
+
+ if (input > 0) {
+
+ ret = request_firmware(&fw, name, dev);
+ if (ret != 0) {
+ pr_err("Failed to get the firmware file");
+
+ } else {
+ dev_info(dev, "successfully loaded the firmware file\n");
+
+ /* the firmware image is now in fw and should probably be
+ * checked then copied to the flash. Once we have finished
+ * with the image then we need to release it as below
+ */
+
+ eeprom_erase(p_pci);
+ send_file(p_pci, fw);
+ release_firmware(fw);
+
+ }
+
+ }
+
+ pdev->flash_in_progress = 0;
+
+ return count;
+
+ error:
+ pr_err("Device is already writing to flash\n");
+ return count;
+
+}
+
+DEVICE_ATTR(firmware_version, 0644, show_firmware_version, store_firmware_version);
+DEVICE_ATTR(fwflash, 0644, show_fwflash, store_fwflash);
+DEVICE_ATTR(leds, 0644, show_led_status, store_leds);
+
+int prospero_device_initialize(struct prospero_device *p)
+{
+ int ret = 0;
+
+ ret = prospero_ir_init(p);
+ if (ret != 0) {
+ pr_err("prospero infra-red device failed to initialise\n");
+ return ret;
+ }
+
+ ret = prospero_dvb_init(p);
+ if (ret != 0)
+ return ret;
+
+ ret = prospero_i2c_init(p);
+ if (ret != 0)
+ return ret;
+
+ ret = prospero_frontend_init(p);
+ if (ret != 0)
+ return ret;
+
+ prospero_demux_init(p);
+
+ pr_info("Prospero Frontend Initialised\n");
+
+ ret = device_create_file(p->dev, &dev_attr_firmware_version);
+ if (ret < 0)
+ pr_err("failed to create sysfs firmware version file");
+
+ ret = device_create_file(p->dev, &dev_attr_fwflash);
+ if (ret < 0)
+ pr_err("Failed to create sysfs reflash files");
+
+ ret = device_create_file(p->dev, &dev_attr_leds);
+ if (ret < 0)
+ pr_err("Failed to create sysfs LED files");
+
+ return ret;
+}
+
+static int prospero_allocate_dma(struct pci_dev *pcidev, int fenum, struct device *dev)
+{
+
+ int ret = 0;
+ struct prospero_device *p = dev_get_drvdata(dev);
+
+ pr_debug("buffer_size %d, channel_buffer_size=%d, guard = %d, int = %d", ts_buffer_size, p->channel_buffer_size, p->buffer_increment, p->interrupt_increment);
+
+ p->p_demux[fenum].ts_membuf_ptr = pci_alloc_consistent(pcidev, ts_buffer_size, &p->p_demux[fenum].ts_cdma);
+ if (p->p_demux[fenum].ts_membuf_ptr != NULL) {
+ pr_debug("DMA SUCCESS for ts buffer; virt_add = %p; size = %d\n", p->p_demux[fenum].ts_membuf_ptr, ts_buffer_size);
+
+ memset(p->p_demux[fenum].ts_membuf_ptr, 0xCC, ts_buffer_size);
+
+ ret = 0;
+
+ } else {
+ pr_err("DMA FAILURE for transport stream buffer\n");
+ ret = -EIO;
+ }
+
+ p->p_demux[fenum].wildcard_membuf_ptr = pci_alloc_consistent(pcidev, wildcard_buffer_size, &p->p_demux[fenum].wildcard_cdma);
+ if (p->p_demux[fenum].wildcard_membuf_ptr != NULL) {
+ pr_debug("DMA SUCCESS for wildcard buffer; virt_add = %p; size = %d\n", p->p_demux[fenum].wildcard_membuf_ptr, wildcard_buffer_size);
+
+ memset(p->p_demux[fenum].wildcard_membuf_ptr, 0xCC, wildcard_buffer_size);
+
+ ret = 0;
+
+ } else {
+ pr_err("DMA FAILURE for transport stream buffer\n");
+ ret = -EIO;
+ }
+
+ return ret;
+
+}
+
+static int prospero_pci_dma_init(struct pci_dev *pcidev, struct prospero_device *p)
+{
+
+ struct prospero_pci *p_pci = p->bus_specific;
+ struct device *dev = p->dev;
+ struct dvb_frontend *fe;
+ int *offset1;
+ int *offset2;
+ int dma;
+ int ret;
+ int i;
+ u64 mask;
+
+ mask = dma_get_required_mask(dev);
+ dma = pci_dma_supported(pcidev, mask);
+
+ pr_debug("DMA ALLOWED = %d\n", dma);
+
+ for (i = 0; i < p->num_frontends; i++) {
+ if (p->fe[i] == NULL)
+ continue;
+
+ fe = p->fe[i];
+
+ offset1 = p_pci->io_mem + p->p_demux[i].buffer1_ptr;
+ offset2 = p_pci->io_mem + p->p_demux[i].buffer2_ptr;
+
+ ret = prospero_allocate_dma(pcidev, i, dev);
+
+ if (ret < 0)
+ return ret;
+
+ pr_debug("buffer1 before write dma address is set to %x\n", ioread32(offset1));
+ pr_debug("buffer2 before write dma address is set to %x\n", ioread32(offset2));
+
+ iowrite32(p->p_demux[i].ts_cdma, offset1);
+ iowrite32(p->p_demux[i].wildcard_cdma, offset2);
+
+ pr_debug("buffer1 virtual mem = %p\n", p->p_demux[i].ts_membuf_ptr);
+ pr_debug("buffer2 virtual mem = %p\n", p->p_demux[i].wildcard_membuf_ptr);
+
+ p_pci->init_state |= P_PCI_DMA_INIT;
+
+ pr_debug("DMA state = %04X\n", p_pci->init_state);
+
+ /* initialise the PID LOOKUP RAM */
+ memset(p_pci->io_mem + p->p_demux[i].Pid_Table_Offset, 0x00, 320);
+ memset(p_pci->io_mem + p->p_demux[i].Buffer_Definition_Ram, 0x00, 2048);
+
+ }
+
+ return 0;
+}
+
+static void prospero_pci_dma_exit(struct pci_dev *pcidev)
+{
+ struct prospero_device *prospero_dev = dev_get_drvdata(&pcidev->dev);
+ struct dvb_frontend *fe;
+ struct prospero_pci *p_pci = pci_get_drvdata(pcidev);
+ int temp;
+ int z = 0;
+ int x = 0;
+
+ pr_debug("DMA state = %04X\n", p_pci->init_state);
+
+ temp = p_pci->init_state & P_PCI_DMA_INIT;
+
+ pr_debug("result = %d, compare = %d", temp, P_PCI_DMA_INIT);
+
+ for (x = 0; x < prospero_dev->num_frontends; x++) {
+ fe = prospero_dev->fe[x];
+
+ pr_debug("DMA freeing ts buffer, for frontend %d\n", x);
+ pci_free_consistent(pcidev, ts_buffer_size, prospero_dev->p_demux[x].ts_membuf_ptr, prospero_dev->p_demux[x].ts_cdma);
+
+ pr_debug("DMA freeing wildcard buffer, for frontend %d\n", x);
+ pci_free_consistent(pcidev, wildcard_buffer_size, prospero_dev->p_demux[x].wildcard_membuf_ptr, prospero_dev->p_demux[x].wildcard_cdma);
+
+ }
+
+ for (z = 0; z < MAX_STREAMS; z++) {
+ if (prospero_dev->streams[z] != NULL)
+ kfree(prospero_dev->streams[z]);
+ }
+
+}
+
+static int prospero_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
+{
+
+ struct prospero_device *p;
+ struct prospero_pci *p_pci;
+ u8 version = 0;
+ int ret = -ENOMEM;
+
+ pr_debug("loading prospero pci\n");
+
+ p = prospero_device_kmalloc(sizeof(struct prospero_pci));
+ if (p == NULL) {
+ pr_err("out of memory\n");
+ return -ENOMEM;
+ }
+
+ /* calculate buffer sizes and pointer increments, ensuring they are a multiple of packet size */
+ p->wild_buffer_increment = ((((wildcard_buffer_size / 100) * 50) / 188) * 188);
+ p->wild_interrupt_increment = ((((wildcard_buffer_size / 100) * 70) / 188) * 188);
+ p->channel_buffer_size = (((ts_buffer_size / STREAMS_PER_DEMUX) / 188) * 188);
+ p->buffer_increment = ((((p->channel_buffer_size / 100) * 50) / 188) * 188);
+ p->interrupt_increment = ((((p->channel_buffer_size / 100) * 70) / 188) * 188);
+
+ pr_debug("wildcard_buffer_size= %d;", wildcard_buffer_size);
+ pr_debug("ts_buffer_size= %d;", ts_buffer_size);
+ pr_debug("channel_buffer_size= %d\n", p->channel_buffer_size);
+ pr_debug("wild_buffer_increment= %d;", p->wild_buffer_increment);
+ pr_debug("wild_interrupt_increment= %d;", p->wild_interrupt_increment);
+ pr_debug("buffer_increment= %d;", p->buffer_increment);
+ pr_debug("interrupt_increment= %d\n", p->interrupt_increment);
+
+ p_pci = p->bus_specific;
+ p_pci->p_dev = p;
+
+ p->dev = &pcidev->dev;
+ p->owner = THIS_MODULE;
+
+ /* bus specific part */
+ p_pci->pcidev = pcidev;
+ ret = prospero_pci_init(p_pci);
+ if (ret != 0)
+ goto err_kfree;
+
+ ret = prospero_device_initialize(p);
+ if (ret != 0)
+ goto err_pci_exit;
+
+ ret = prospero_pci_dma_init(pcidev, p);
+ if (ret != 0)
+ goto err_pci_exit;
+
+ prospero_enable_interrupts(p_pci);
+
+ pro = p;
+
+ memset(&p->streams, 0, sizeof(p->streams));
+ memset(&p->stream_links, 0, sizeof(p->stream_links));
+
+ version = ioread8(p_pci->io_mem + FIRMWARE_VERSION);
+
+ pr_debug("Prospero Firmware version is %x\n", version);
+
+ pr_debug("wildcard_buffer_size= %d; ts_buffer_size= %d; channel_buffer_size= %d\n", wildcard_buffer_size, ts_buffer_size, p->channel_buffer_size);
+ pr_debug("wild_buffer_increment= %d; wild_interrupt_increment= %d; buffer_increment= %d; interrupt_increment= %d\n", p->wild_buffer_increment, p->wild_interrupt_increment, p->buffer_increment,
+ p->interrupt_increment);
+
+ return ret;
+
+ err_pci_exit:
+ prospero_pci_exit(p);
+
+ err_kfree:
+ prospero_device_kfree(p);
+ return ret;
+
+}
+
+static void prospero_pci_remove(struct pci_dev *pcidev)
+{
+
+ struct prospero_device *prospero_dev = dev_get_drvdata(&pcidev->dev);
+ struct prospero_pci *p_pci = prospero_dev->bus_specific;
+
+ /* uninitialise the device here */
+
+ pr_debug("remove prospero\n");
+ pr_debug("wildcard_buffer_size= %d;\n", wildcard_buffer_size);
+ pr_debug("ts_buffer_size= %d;\n", ts_buffer_size);
+ pr_debug("channel_buffer_size= %d\n", prospero_dev->channel_buffer_size);
+ pr_debug("wild_buffer_increment= %d;\n", prospero_dev->wild_buffer_increment);
+ pr_debug("wild_interrupt_increment= %d;\n", prospero_dev->wild_interrupt_increment);
+ pr_debug("buffer_increment= %d;\n", prospero_dev->buffer_increment);
+ pr_debug("interrupt_increment= %d\n", prospero_dev->interrupt_increment);
+
+ iowrite8(0x00, p_pci->io_mem + TS_CAP_ENABLE);
+
+ prospero_disable_interrupts(p_pci);
+ device_remove_file(&pcidev->dev, &dev_attr_firmware_version);
+ device_remove_file(&pcidev->dev, &dev_attr_fwflash);
+ device_remove_file(&pcidev->dev, &dev_attr_leds);
+
+ prospero_pci_dma_exit(pcidev);
+
+ prospero_device_exit(prospero_dev);
+
+ prospero_pci_exit(prospero_dev);
+
+ prospero_device_kfree(prospero_dev);
+
+}
+
+static struct pci_device_id prospero_pci_tbl[] = {
+ {PCI_DEVICE(0x1d3a, 0x1001)},
+ {},
+};
+
+MODULE_DEVICE_TABLE(pci, prospero_pci_tbl);
+
+static struct pci_driver prospero_pci_driver = {
+ .name = "prospero",
+ .id_table = prospero_pci_tbl,
+ .probe = prospero_pci_probe,
+ .remove = prospero_pci_remove,
+};
+
+static int __init prospero_init(void)
+{
+
+ return pci_register_driver(&prospero_pci_driver);
+
+}
+
+static void __exit prospero_exit(void)
+{
+ pci_unregister_driver(&prospero_pci_driver);
+}
+
+module_init(prospero_init);
+module_exit(prospero_exit);
--
2.1.4
^ permalink raw reply related [flat|nested] 12+ messages in thread