#!/bin/sh
#
# ** Patch für die TT S2-6400 Unterstützung
#
# Der Hauptteil des Patches besteht aus den neuen Quellcode Dateien.
#
# Generiert aus dem linuxtv.org/endriss/media_build_experimental repository, welches
# wie folgt geholt werden kann:
#
#  hg clone http://linuxtv.org/hg/~endriss/media_build_experimental
#  cd media_build_experimental/
#  make distclean
#  make download
#  make untar
#
# Zusätzlich sind die folgenden Anpassungen an die Kernelumgebung hinzugefügt:
#
#  1) drivers/media/common/Kconfig (include hinzugefügt)
#  2) drivers/media/common/Makefile (Unterverzeichnis hinzugefügt)
#  3) drivers/media/common/saa716x/Makefile  (auf neue cflags korrigiert)
#  4) Nötigen Patch ./experimental/saa716x.include-osd.diff hinzugefügt
#  5) drivers/media/common/saa716x/saa716x_ff_main.c und
#     drivers/media/common/saa716x/saa716x_ff_ir.c [include "linux/version.h" fehlte]
#
# ------------------------------------------------------------------------------------
#
# Der Patch kann mit dem folgenden Skript neu erzeugt werden.
# (d.h. das Skript besteht aus allen Zeile mit #| am Anfang!)
#
# Zum Erzeugen des Skripts aus diesem Patch einfach den Patch
# mit 'sh linux-s2-6400-saa716x.patch' starten oder diese Zeile ausführen:
#
   grep '^#| ' linux-s2-6400-saa716x.patch | sed 's/^#| //' >make-patch.sh; chmod +x make-patch.sh; return
#
# ------------------------------------------------------------------------------------
#
#| #!/bin/bash
#| 
#| n=linux-s2-6400-saa716x.patch
#| p=$(pwd)
#| f=$p/$n
#| 
#| cat >$f <<EOF
#| #!/bin/sh
#| #
#| # ** Patch für die TT S2-6400 Unterstützung
#| #
#| # Der Hauptteil des Patches besteht aus den neuen Quellcode Dateien.
#| #
#| # Generiert aus dem linuxtv.org/endriss/media_build_experimental repository, welches
#| # wie folgt geholt werden kann:
#| #
#| #  hg clone http://linuxtv.org/hg/~endriss/media_build_experimental
#| #  cd media_build_experimental/
#| #  make distclean
#| #  make download
#| #  make untar
#| #
#| # Zusätzlich sind die folgenden Anpassungen an die Kernelumgebung hinzugefügt:
#| #
#| #  1) drivers/media/common/Kconfig (include hinzugefügt)
#| #  2) drivers/media/common/Makefile (Unterverzeichnis hinzugefügt)
#| #  3) drivers/media/common/saa716x/Makefile  (auf neue cflags korrigiert)
#| #  4) Nötigen Patch ./experimental/saa716x.include-osd.diff hinzugefügt
#| #  5) drivers/media/common/saa716x/saa716x_ff_main.c und
#| #     drivers/media/common/saa716x/saa716x_ff_ir.c [include "linux/version.h" fehlte]
#| #
#| # ------------------------------------------------------------------------------------
#| #
#| # Der Patch kann mit dem folgenden Skript neu erzeugt werden.
#| # (d.h. das Skript besteht aus allen Zeile mit #| am Anfang!)
#| #
#| # Zum Erzeugen des Skripts aus diesem Patch einfach den Patch
#| # mit 'sh $n' starten oder diese Zeile ausführen:
#| #
#|    grep '^#| ' $n | sed 's/^#| //' >make-patch.sh; chmod +x make-patch.sh; return
#| #
#| # ------------------------------------------------------------------------------------
#| #
#| EOF
#| 
#| sed 's/^/#| /g' $0 >>$f
#| echo "#" >>$f
#| echo "# ------------------------------------------------------------------------------------" >>$f
#| echo >>$f
#| 
#| # Aus Repository herunterladen
#| if [ ! -d media_build_experimental ]; then
#|   hg clone http://linuxtv.org/hg/~endriss/media_build_experimental
#|   cd media_build_experimental/
#|   make distclean
#|   make download
#|   make untar
#|   cd $p
#| fi  
#| 
#| # Patch bauen...
#| cd media_build_experimental/experimental/v4l-dvb-saa716x/
#| v=linux.vanilla/drivers/media/pci/saa716x/
#| mkdir -p $v
#| mv linux/drivers/media/common linux/drivers/media/pci
#| diff -ruN $v linux/drivers/media/pci/saa716x/ >>$f
#| mv linux/drivers/media/pci linux/drivers/media/common
#| cd $p
#| 
#| # Patches aus dem Repository auch übernehmen...
#| cat media_build_experimental/experimental/saa716x.include-osd.diff >>$f
#| cat media_build_experimental/experimental/saa716x.pci-kconfig.diff >>$f
#| cat media_build_experimental/experimental/saa716x.pci-makefile.diff >>$f
#| 
#| cat >>$f <<EOF
#| #
#| # Korrekturen am Patch selbst
#| #
#| --- linux.vanilla/drivers/media/pci/saa716x/Makefile	2015-08-08 11:23:50.892996241 +0100
#| +++ linux/drivers/media/pci/saa716x/Makefile	2015-08-08 11:23:41.713055915 +0100
#| @@ -24,4 +24,4 @@
#|  obj-\$(CONFIG_DVB_SAA716X_HYBRID)  += saa716x_hybrid.o
#|  obj-\$(CONFIG_DVB_SAA716X_FF)	  += saa716x_ff.o
#|  
#| -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/
#| +ccflags-y += -Idrivers/media/dvb-core -Idrivers/media/dvb-frontends/
#| --- linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_main.c	2013-01-11 11:45:40.744703568 +0100
#| +++ linux/drivers/media/pci/saa716x/saa716x_ff_main.c	2013-01-11 11:45:59.364586452 +0100
#| @@ -3,6 +3,7 @@
#|  #include <linux/kernel.h>
#|  #include <linux/pci.h>
#|  #include <linux/mutex.h>
#| +#include <linux/version.h>
#|  
#|  #include <asm/io.h>
#|  #include <asm/pgtable.h>
#| --- linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_ir.c	2013-01-11 11:46:31.104386754 +0100
#| +++ linux/drivers/media/pci/saa716x/saa716x_ff_ir.c	2013-01-11 11:46:55.874230860 +0100
#| @@ -22,6 +22,7 @@
#|  
#|  #include <linux/types.h>
#|  #include <linux/input.h>
#| +#include <linux/version.h>
#|  
#|  #include "saa716x_spi.h"
#|  #include "saa716x_priv.h"
#| --- linux.vanilla/drivers/media/pci/saa716x/saa716x_pci.c	2017-08-12 11:39:01.000000000 +0200
#| +++ linux/drivers/media/pci/saa716x/saa716x_pci.c	2017-08-12 12:38:14.420953555 +0200
#| @@ -1,6 +1,8 @@
#| +#ifndef CONFIG_X86_64
#|  #include <asm/io.h>
#|  #include <asm/pgtable.h>
#|  #include <asm/page.h>
#| +#endif
#|  #include <linux/kmod.h>
#|  #include <linux/vmalloc.h>
#|  #include <linux/init.h>
#| @@ -39,12 +41,9 @@ static int saa716x_enable_msi(struct saa
#|  static int saa716x_enable_msix(struct saa716x_dev *saa716x)
#|  {
#|  	struct pci_dev *pdev = saa716x->pdev;
#| -	int i, ret = 0;
#| -
#| -	for (i = 0; i < SAA716x_MSI_MAX_VECTORS; i++)
#| -		saa716x->msix_entries[i].entry = i;
#| +	int ret = 0;
#|  
#| -	ret = pci_enable_msix(pdev, saa716x->msix_entries, SAA716x_MSI_MAX_VECTORS);
#| +	ret = pci_alloc_irq_vectors(pdev, SAA716x_MSI_MAX_VECTORS, SAA716x_MSI_MAX_VECTORS, PCI_IRQ_MSIX);
#|  	if (ret < 0)
#|  		dprintk(SAA716x_ERROR, 1, "MSI-X request failed <%d>", ret);
#|  	if (ret > 0)
#| @@ -88,7 +85,7 @@ static int saa716x_request_irq(struct sa
#|  
#|  	if (saa716x->int_type == MODE_MSI_X) {
#|  		for (i = 0; SAA716x_MSI_MAX_VECTORS; i++) {
#| -			ret = request_irq(saa716x->msix_entries[i].vector,
#| +			ret = request_irq(pci_irq_vector(pdev, i),
#|  					  saa716x->saa716x_msix_handler[i].handler,
#|  					  IRQF_SHARED,
#|  					  saa716x->saa716x_msix_handler[i].desc,
#| @@ -125,7 +122,7 @@ static void saa716x_free_irq(struct saa7
#|  	if (saa716x->int_type == MODE_MSI_X) {
#|  
#|  		for (i = 0; i < SAA716x_MSI_MAX_VECTORS; i++) {
#| -			vector = saa716x->msix_entries[i].vector;
#| +			vector = pci_irq_vector(pdev, i);
#|  			free_irq(vector, saa716x);
#|  		}
#|  
#| @@ -141,7 +138,7 @@ static void saa716x_free_irq(struct saa7
#|  int saa716x_pci_init(struct saa716x_dev *saa716x)
#|  {
#|  	struct pci_dev *pdev = saa716x->pdev;
#| -	int err = 0, ret = -ENODEV, i, use_dac, pm_cap;
#| +	int err = 0, ret = -ENODEV, use_dac, pm_cap;
#|  	u32 msi_cap;
#|  	u8 revision;
#|  
#| @@ -197,9 +194,6 @@ int saa716x_pci_init(struct saa716x_dev
#|  		goto fail2;
#|  	}
#|  
#| -	for (i = 0; i < SAA716x_MSI_MAX_VECTORS; i++)
#| -		saa716x->msix_entries[i].entry = i;
#| -
#|  	err = saa716x_request_irq(saa716x);
#|  	if (err < 0) {
#|  		dprintk(SAA716x_ERROR, 1, "SAA716x IRQ registration failed, err=%d", err);
#| --- linux.vanilla/drivers/media/pci/saa716x/saa716x_priv.h	2017-08-12 11:39:01.000000000 +0200
#| +++ linux/drivers/media/pci/saa716x/saa716x_priv.h	2017-08-12 15:08:18.729941656 +0200
#| @@ -134,7 +134,6 @@ struct saa716x_dev {
#|  #define MODE_MSI_X	2
#|  	u8				int_type;
#|  
#| -	struct msix_entry		msix_entries[SAA716x_MSI_MAX_VECTORS];
#|  	struct saa716x_msix_entry	saa716x_msix_handler[56];
#|  	u8				handlers; /* no. of active handlers */
#|  
#| --- linux.vanilla/drivers/media/pci/saa716x/saa716x_priv.h	2017-08-12 11:39:01.000000000 +0200
#| +++ linux/drivers/media/pci/saa716x/saa716x_priv.h	2017-08-12 15:08:18.729941656 +0200
#| @@ -36,13 +36,13 @@
#|  #define dprintk(__x, __y, __fmt, __arg...) do {								\\
#|  	if (__y) {											\\
#|  		if	((SAA716x_VERBOSE > SAA716x_ERROR) && (SAA716x_VERBOSE > __x))			\\
#| -			printk(KERN_ERR "%s (%d): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\\
#| +			printk(KERN_ERR "%s (%i): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\\
#|  		else if	((SAA716x_VERBOSE > SAA716x_NOTICE) && (SAA716x_VERBOSE > __x))			\\
#| -			printk(KERN_NOTICE "%s (%d): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\\
#| +			printk(KERN_NOTICE "%s (%i): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\\
#|  		else if ((SAA716x_VERBOSE > SAA716x_INFO) && (SAA716x_VERBOSE > __x))			\\
#| -			printk(KERN_INFO "%s (%d): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\\
#| +			printk(KERN_INFO "%s (%i): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\\
#|  		else if ((SAA716x_VERBOSE > SAA716x_DEBUG) && (SAA716x_VERBOSE > __x))			\\
#| -			printk(KERN_DEBUG "%s (%d): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\\
#| +			printk(KERN_DEBUG "%s (%i): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\\
#|  	} else {											\\
#|  		if (SAA716x_VERBOSE > __x)								\\
#|  			printk(__fmt , ##__arg);							\\
#| --- linux.vanilla/drivers/media/pci/saa716x/saa716x_rom.c	2017-08-12 15:02:05.053690801 +0200
#| +++ linux/drivers/media/pci/saa716x/saa716x_rom.c	2017-08-12 15:49:19.991838792 +0200
#| @@ -113,7 +113,7 @@ static int saa716x_eeprom_header(struct
#|  	memcpy(rom_header, &buf[*offset], sizeof (struct saa716x_romhdr));
#|  	if (rom_header->header_size != sizeof (struct saa716x_romhdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%li bytes, Expected=%i",
#|  			sizeof (struct saa716x_romhdr),
#|  			rom_header->header_size);
#|  
#| @@ -237,7 +237,7 @@ static int saa716x_decoder_info(struct s
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_decoder_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_decoder_hdr));
#|  
#| @@ -268,7 +268,7 @@ static int saa716x_gpio_info(struct saa7
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_gpio_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_gpio_hdr));
#|  
#| @@ -305,7 +305,7 @@ static int saa716x_video_decoder_info(st
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_video_decoder_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_video_decoder_hdr));
#|  
#| @@ -386,7 +386,7 @@ static int saa716x_audio_decoder_info(st
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_audio_decoder_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_audio_decoder_hdr));
#|  
#| @@ -417,7 +417,7 @@ static int saa716x_event_source_info(str
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_evsrc_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_evsrc_hdr));
#|  
#| @@ -448,7 +448,7 @@ static int saa716x_crossbar_info(struct
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_xbar_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_xbar_hdr));
#|  
#| @@ -486,7 +486,7 @@ static int saa716x_tuner_info(struct saa
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_tuner_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_tuner_hdr));
#|  
#| @@ -516,7 +516,7 @@ static int saa716x_pll_info(struct saa71
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_pll_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_pll_hdr));
#|  
#| @@ -546,7 +546,7 @@ static int saa716x_channel_decoder_info(
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_channel_decoder_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_channel_decoder_hdr));
#|  
#| @@ -576,7 +576,7 @@ static int saa716x_encoder_info(struct s
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_encoder_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_encoder_hdr));
#|  
#| @@ -606,7 +606,7 @@ static int saa716x_ir_info(struct saa716
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_ir_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_ir_hdr));
#|  
#| @@ -637,7 +637,7 @@ static int saa716x_eeprom_info(struct sa
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_eeprom_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_eeprom_hdr));
#|  
#| @@ -668,7 +668,7 @@ static int saa716x_filter_info(struct sa
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_filter_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_filter_hdr));
#|  
#| @@ -699,7 +699,7 @@ static int saa716x_streamdev_info(struct
#|  	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
#|  	if (header.size != sizeof (struct saa716x_streamdev_hdr)) {
#|  		dprintk(SAA716x_ERROR, 1,
#| -			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
#| +			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
#|  			header.size,
#|  			sizeof (struct saa716x_streamdev_hdr));
#|  
#| @@ -789,7 +789,7 @@ static int saa716x_device_info(struct sa
#|  
#|  	memcpy(device, &buf[*offset], sizeof (struct saa716x_devinfo));
#|  	if (device->struct_size != sizeof (struct saa716x_devinfo)) {
#| -		dprintk(SAA716x_ERROR, 1, "ERROR: Device size mismatch! Read=%d bytes, expected=%d bytes",
#| +		dprintk(SAA716x_ERROR, 1, "ERROR: Device size mismatch! Read=%i bytes, Expected=%li bytes",
#|  		device->struct_size,
#|  		sizeof (struct saa716x_devinfo));
#|  
#| EOF
#
# ------------------------------------------------------------------------------------

diff -ruN linux.vanilla/drivers/media/pci/saa716x/Kconfig linux/drivers/media/pci/saa716x/Kconfig
--- linux.vanilla/drivers/media/pci/saa716x/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/Kconfig	2017-08-11 23:31:25.773389780 +0200
@@ -0,0 +1,63 @@
+menuconfig SAA716X_SUPPORT
+	bool "Support for SAA716x family from NXP/Philips"
+	depends on PCI && I2C
+	help
+	  support for saa716x
+
+if SAA716X_SUPPORT
+config SAA716X_CORE
+	tristate "SAA7160/1/2 PCI Express bridge based devices"
+	depends on PCI && I2C
+
+	help
+	  Support for PCI cards based on the SAA7160/1/2 PCI Express bridge.
+
+	  Say Y if you own such a device and want to use it.
+
+config DVB_SAA716X_BUDGET
+	tristate "SAA7160/1/2 based Budget PCIe cards (DVB only)"
+	depends on SAA716X_CORE && DVB_CORE
+
+	help
+	  Support for the SAA7160/1/2 based Budget PCIe DVB cards
+	  Currently supported devices are:
+
+	  * KNC1 Dual S2 (DVB-S, DVB-S/S2)
+	  * Twinhan/Azurewave VP-1028 (DVB-S)
+	  * Twinhan/Azurewave VP-3071 (DVB-T x2)
+	  * Twinhan/Azurewave VP-6002 (DVB-S)
+
+	  Say Y if you own such a device and want to use it.
+
+config DVB_SAA716X_HYBRID
+	tristate "SAA7160/1/2 based Hybrid PCIe cards (DVB + Analog)"
+	depends on SAA716X_CORE && DVB_CORE
+
+	help
+	  Support for the SAA7160/1/2 based Hybrid PCIe DVB cards
+	  Currently supported devices are:
+
+	  * Avermedia H-788 (DVB-T)
+	  * Avermedia HC-82 (DVB-T)
+	  * NXP Reference (Atlantis) (DVB-T x2)
+	  * NXP Reference (Nemo) (DVB-T)
+	  * Twinhan/Azurewave VP-6090 (DVB-S x2, DVB-T x2)
+
+	  Say Y if you own such a device and want to use it.
+
+config DVB_SAA716X_FF
+	tristate "SAA7160/1/2 based Full Fledged PCIe cards"
+	depends on SAA716X_CORE && DVB_CORE
+	depends on INPUT # IR
+
+	help
+	  Support for the SAA7160/1/2 based  Full fledged PCIe DVB cards
+	  These cards do feature a hardware MPEG decoder and other
+	  peripherals. Also known as Premium cards.
+	  Currently supported devices are:
+
+	  * Technotrend S2 6400 Dual S2 HD (DVB-S/S2 x2)
+
+	  Say Y if you own such a device and want to use it.
+
+endif # SAA716X_SUPPORT
diff -ruN linux.vanilla/drivers/media/pci/saa716x/Makefile linux/drivers/media/pci/saa716x/Makefile
--- linux.vanilla/drivers/media/pci/saa716x/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/Makefile	2017-08-11 23:31:25.773389780 +0200
@@ -0,0 +1,27 @@
+saa716x_core-objs	:= saa716x_pci.o	\
+			   saa716x_i2c.o	\
+			   saa716x_cgu.o	\
+			   saa716x_msi.o	\
+			   saa716x_dma.o	\
+			   saa716x_vip.o	\
+			   saa716x_aip.o	\
+			   saa716x_phi.o	\
+			   saa716x_boot.o	\
+			   saa716x_fgpi.o	\
+			   saa716x_adap.o	\
+			   saa716x_gpio.o	\
+			   saa716x_greg.o	\
+			   saa716x_rom.o	\
+			   saa716x_spi.o
+
+saa716x_ff-objs		:= saa716x_ff_main.o	\
+			   saa716x_ff_cmd.o	\
+			   saa716x_ff_ir.o	\
+			   saa716x_ff_phi.o
+
+obj-$(CONFIG_SAA716X_CORE)	  += saa716x_core.o
+obj-$(CONFIG_DVB_SAA716X_BUDGET)  += saa716x_budget.o
+obj-$(CONFIG_DVB_SAA716X_HYBRID)  += saa716x_hybrid.o
+obj-$(CONFIG_DVB_SAA716X_FF)	  += saa716x_ff.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_adap.c linux/drivers/media/pci/saa716x/saa716x_adap.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_adap.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_adap.c	2017-08-11 23:31:25.773389780 +0200
@@ -0,0 +1,251 @@
+#include <linux/bitops.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+
+#include "saa716x_mod.h"
+#include "saa716x_spi.h"
+#include "saa716x_adap.h"
+#include "saa716x_i2c.h"
+#include "saa716x_gpio.h"
+#include "saa716x_priv.h"
+
+
+#define SAA716X_TS_DMA_BUF_SIZE		(16 * SAA716x_PAGE_SIZE)
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+
+void saa716x_dma_start(struct saa716x_dev *saa716x, u8 adapter)
+{
+	struct fgpi_stream_params params;
+
+	dprintk(SAA716x_DEBUG, 1, "SAA716x Start DMA engine for Adapter:%d", adapter);
+
+	params.bits		= 8;
+	params.samples		= 188;
+	params.lines		= SAA716X_TS_DMA_BUF_SIZE / 188;
+	params.pitch		= 188;
+	params.offset		= 0;
+	params.page_tables	= 0;
+	params.stream_type	= FGPI_TRANSPORT_STREAM;
+	params.stream_flags	= 0;
+
+	saa716x_fgpi_start(saa716x, saa716x->config->adap_config[adapter].ts_port, &params);
+}
+
+void saa716x_dma_stop(struct saa716x_dev *saa716x, u8 adapter)
+{
+	dprintk(SAA716x_DEBUG, 1, "SAA716x Stop DMA engine for Adapter:%d", adapter);
+
+	saa716x_fgpi_stop(saa716x, saa716x->config->adap_config[adapter].ts_port);
+}
+
+static int saa716x_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx		= dvbdmxfeed->demux;
+	struct saa716x_adapter *saa716x_adap	= dvbdmx->priv;
+	struct saa716x_dev *saa716x		= saa716x_adap->saa716x;
+
+	dprintk(SAA716x_DEBUG, 1, "SAA716x DVB Start feed");
+	if (!dvbdmx->dmx.frontend) {
+		dprintk(SAA716x_DEBUG, 1, "no frontend ?");
+		return -EINVAL;
+	}
+	saa716x_adap->feeds++;
+	dprintk(SAA716x_DEBUG, 1, "SAA716x start feed, feeds=%d",
+		saa716x_adap->feeds);
+
+	if (saa716x_adap->feeds == 1) {
+		dprintk(SAA716x_DEBUG, 1, "SAA716x start feed & dma");
+		saa716x_dma_start(saa716x, saa716x_adap->count);
+	}
+
+	return saa716x_adap->feeds;
+}
+
+static int saa716x_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx		= dvbdmxfeed->demux;
+	struct saa716x_adapter *saa716x_adap	= dvbdmx->priv;
+	struct saa716x_dev *saa716x		= saa716x_adap->saa716x;
+
+	dprintk(SAA716x_DEBUG, 1, "SAA716x DVB Stop feed");
+	if (!dvbdmx->dmx.frontend) {
+		dprintk(SAA716x_DEBUG, 1, "no frontend ?");
+		return -EINVAL;
+	}
+	saa716x_adap->feeds--;
+	if (saa716x_adap->feeds == 0) {
+		dprintk(SAA716x_DEBUG, 1, "saa716x stop feed and dma");
+		saa716x_dma_stop(saa716x, saa716x_adap->count);
+	}
+
+	return 0;
+}
+
+int saa716x_dvb_init(struct saa716x_dev *saa716x)
+{
+	struct saa716x_adapter *saa716x_adap = saa716x->saa716x_adap;
+	struct saa716x_config *config = saa716x->config;
+	int result, i;
+
+	mutex_init(&saa716x->adap_lock);
+
+	for (i = 0; i < config->adapters; i++) {
+
+		dprintk(SAA716x_DEBUG, 1, "dvb_register_adapter");
+		if (dvb_register_adapter(&saa716x_adap->dvb_adapter,
+					 "SAA716x dvb adapter",
+					 THIS_MODULE,
+					 &saa716x->pdev->dev,
+					 adapter_nr) < 0) {
+
+			dprintk(SAA716x_ERROR, 1, "Error registering adapter");
+			return -ENODEV;
+		}
+
+		saa716x_adap->count			= i;
+
+		saa716x_adap->dvb_adapter.priv		= saa716x_adap;
+		saa716x_adap->demux.dmx.capabilities	= DMX_TS_FILTERING	|
+							  DMX_SECTION_FILTERING	|
+							  DMX_MEMORY_BASED_FILTERING;
+
+		saa716x_adap->demux.priv		= saa716x_adap;
+		saa716x_adap->demux.filternum		= 256;
+		saa716x_adap->demux.feednum		= 256;
+		saa716x_adap->demux.start_feed		= saa716x_dvb_start_feed;
+		saa716x_adap->demux.stop_feed		= saa716x_dvb_stop_feed;
+		saa716x_adap->demux.write_to_decoder	= NULL;
+
+		dprintk(SAA716x_DEBUG, 1, "dvb_dmx_init");
+		if ((result = dvb_dmx_init(&saa716x_adap->demux)) < 0) {
+			dprintk(SAA716x_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
+			goto err0;
+		}
+
+		saa716x_adap->dmxdev.filternum		= 256;
+		saa716x_adap->dmxdev.demux		= &saa716x_adap->demux.dmx;
+		saa716x_adap->dmxdev.capabilities	= 0;
+
+		dprintk(SAA716x_DEBUG, 1, "dvb_dmxdev_init");
+		if ((result = dvb_dmxdev_init(&saa716x_adap->dmxdev,
+					      &saa716x_adap->dvb_adapter)) < 0) {
+
+			dprintk(SAA716x_ERROR, 1, "dvb_dmxdev_init failed, ERROR=%d", result);
+			goto err1;
+		}
+
+		saa716x_adap->fe_hw.source = DMX_FRONTEND_0;
+
+		if ((result = saa716x_adap->demux.dmx.add_frontend(&saa716x_adap->demux.dmx,
+								   &saa716x_adap->fe_hw)) < 0) {
+
+			dprintk(SAA716x_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
+			goto err2;
+		}
+
+		saa716x_adap->fe_mem.source = DMX_MEMORY_FE;
+
+		if ((result = saa716x_adap->demux.dmx.add_frontend(&saa716x_adap->demux.dmx,
+								   &saa716x_adap->fe_mem)) < 0) {
+			dprintk(SAA716x_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
+			goto err3;
+		}
+
+		if ((result = saa716x_adap->demux.dmx.connect_frontend(&saa716x_adap->demux.dmx,
+								       &saa716x_adap->fe_hw)) < 0) {
+
+			dprintk(SAA716x_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
+			goto err4;
+		}
+
+		dvb_net_init(&saa716x_adap->dvb_adapter, &saa716x_adap->dvb_net, &saa716x_adap->demux.dmx);
+//		tasklet_init(&saa716x_adap->tasklet, saa716x_dma_xfer, (unsigned long) saa716x);
+		dprintk(SAA716x_DEBUG, 1, "Frontend Init");
+		saa716x_adap->saa716x = saa716x;
+
+		if (config->frontend_attach) {
+			result = config->frontend_attach(saa716x_adap, i);
+			if (result < 0)
+				dprintk(SAA716x_ERROR, 1, "SAA716x frontend attach failed");
+
+			if (saa716x_adap->fe == NULL) {
+				dprintk(SAA716x_ERROR, 1, "A frontend driver was not found for [%04x:%04x] subsystem [%04x:%04x]\n",
+					saa716x->pdev->vendor,
+					saa716x->pdev->device,
+					saa716x->pdev->subsystem_vendor,
+					saa716x->pdev->subsystem_device);
+			} else {
+				result = dvb_register_frontend(&saa716x_adap->dvb_adapter, saa716x_adap->fe);
+				if (result < 0) {
+					dprintk(SAA716x_ERROR, 1, "SAA716x register frontend failed");
+					goto err6;
+				}
+			}
+
+		} else {
+			dprintk(SAA716x_ERROR, 1, "Frontend attach = NULL");
+		}
+
+		saa716x_fgpi_init(saa716x, config->adap_config[i].ts_port,
+				  SAA716X_TS_DMA_BUF_SIZE,
+				  config->adap_config[i].worker);
+
+		saa716x_adap++;
+	}
+
+
+	return 0;
+
+	/* Error conditions */
+err6:
+	dvb_frontend_detach(saa716x_adap->fe);
+err4:
+	saa716x_adap->demux.dmx.remove_frontend(&saa716x_adap->demux.dmx, &saa716x_adap->fe_mem);
+err3:
+	saa716x_adap->demux.dmx.remove_frontend(&saa716x_adap->demux.dmx, &saa716x_adap->fe_hw);
+err2:
+	dvb_dmxdev_release(&saa716x_adap->dmxdev);
+err1:
+	dvb_dmx_release(&saa716x_adap->demux);
+err0:
+	dvb_unregister_adapter(&saa716x_adap->dvb_adapter);
+
+	return result;
+}
+EXPORT_SYMBOL(saa716x_dvb_init);
+
+void saa716x_dvb_exit(struct saa716x_dev *saa716x)
+{
+	struct saa716x_adapter *saa716x_adap = saa716x->saa716x_adap;
+	int i;
+
+	for (i = 0; i < saa716x->config->adapters; i++) {
+
+		saa716x_fgpi_exit(saa716x, saa716x->config->adap_config[i].ts_port);
+
+		if (saa716x_adap->fe) {
+			dvb_unregister_frontend(saa716x_adap->fe);
+			dvb_frontend_detach(saa716x_adap->fe);
+		}
+
+//		tasklet_kill(&saa716x->tasklet);
+		dvb_net_release(&saa716x_adap->dvb_net);
+		saa716x_adap->demux.dmx.remove_frontend(&saa716x_adap->demux.dmx, &saa716x_adap->fe_mem);
+		saa716x_adap->demux.dmx.remove_frontend(&saa716x_adap->demux.dmx, &saa716x_adap->fe_hw);
+		dvb_dmxdev_release(&saa716x_adap->dmxdev);
+		dvb_dmx_release(&saa716x_adap->demux);
+
+		dprintk(SAA716x_DEBUG, 1, "dvb_unregister_adapter");
+		dvb_unregister_adapter(&saa716x_adap->dvb_adapter);
+
+		saa716x_adap++;
+	}
+
+	return;
+}
+EXPORT_SYMBOL(saa716x_dvb_exit);
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_adap.h linux/drivers/media/pci/saa716x/saa716x_adap.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_adap.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_adap.h	2017-08-11 23:31:25.785389714 +0200
@@ -0,0 +1,9 @@
+#ifndef __SAA716x_ADAP_H
+#define __SAA716x_ADAP_H
+
+struct saa716x_dev;
+
+extern int saa716x_dvb_init(struct saa716x_dev *saa716x);
+extern void saa716x_dvb_exit(struct saa716x_dev *saa716x);
+
+#endif /* __SAA716x_ADAP_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_aip.c linux/drivers/media/pci/saa716x/saa716x_aip.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_aip.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_aip.c	2017-08-11 23:31:25.785389714 +0200
@@ -0,0 +1,20 @@
+#include <linux/kernel.h>
+
+#include "saa716x_mod.h"
+#include "saa716x_aip_reg.h"
+#include "saa716x_spi.h"
+#include "saa716x_aip.h"
+#include "saa716x_priv.h"
+
+int saa716x_aip_status(struct saa716x_dev *saa716x, u32 dev)
+{
+	return SAA716x_EPRD(dev, AI_CTL) == 0 ? 0 : -1;
+}
+EXPORT_SYMBOL_GPL(saa716x_aip_status);
+
+void saa716x_aip_disable(struct saa716x_dev *saa716x)
+{
+	SAA716x_EPWR(AI0, AI_CTL, 0x00);
+	SAA716x_EPWR(AI1, AI_CTL, 0x00);
+}
+EXPORT_SYMBOL_GPL(saa716x_aip_disable);
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_aip.h linux/drivers/media/pci/saa716x/saa716x_aip.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_aip.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_aip.h	2017-08-11 23:31:25.785389714 +0200
@@ -0,0 +1,9 @@
+#ifndef __SAA716x_AIP_H
+#define __SAA716x_AIP_H
+
+struct saa716x_dev;
+
+extern int saa716x_aip_status(struct saa716x_dev *saa716x, u32 dev);
+extern void saa716x_aip_disable(struct saa716x_dev *saa716x);
+
+#endif /* __SAA716x_AIP_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_aip_reg.h linux/drivers/media/pci/saa716x/saa716x_aip_reg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_aip_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_aip_reg.h	2017-08-11 23:31:25.785389714 +0200
@@ -0,0 +1,62 @@
+#ifndef __SAA716x_AIP_REG_H
+#define __SAA716x_AIP_REG_H
+
+/* -------------- AI Registers ---------------- */
+
+#define AI_STATUS			0x000
+#define AI_BUF1_ACTIVE			(0x00000001 <<  4)
+#define AI_OVERRUN			(0x00000001 <<  3)
+#define AI_HBE				(0x00000001 <<  2)
+#define AI_BUF2_FULL			(0x00000001 <<  1)
+#define AI_BUF1_FULL			(0x00000001 <<  0)
+
+#define AI_CTL				0x004
+#define AI_RESET			(0x00000001 <<  31)
+#define AI_CAP_ENABLE			(0x00000001 <<  30)
+#define AI_CAP_MODE			(0x00000003 <<  28)
+#define AI_SIGN_CONVERT			(0x00000001 <<  27)
+#define AI_EARLYMODE			(0x00000001 <<  26)
+#define AI_DIAGMODE			(0x00000001 <<  25)
+#define AI_RAWMODE			(0x00000001 <<  24)
+#define AI_OVR_INTEN			(0x00000001 <<   7)
+#define AI_HBE_INTEN			(0x00000001 <<   6)
+#define AI_BUF2_INTEN			(0x00000001 <<   5)
+#define AI_BUF1_INTEN			(0x00000001 <<   4)
+#define AI_ACK_OVR			(0x00000001 <<   3)
+#define AI_ACK_HBE			(0x00000001 <<   2)
+#define AI_ACK2				(0x00000001 <<   1)
+#define AI_ACK1				(0x00000001 <<   0)
+
+#define AI_SERIAL			0x008
+#define AI_SER_MASTER			(0x00000001 <<  31)
+#define AI_DATAMODE			(0x00000001 <<  30)
+#define AI_FRAMEMODE			(0x00000003 <<  28)
+#define AI_CLOCK_EDGE			(0x00000001 <<  27)
+#define AI_SSPOS4			(0x00000001 <<  19)
+#define AI_NR_CHAN			(0x00000003 <<  17)
+#define AI_WSDIV			(0x000001ff <<   8)
+#define AI_SCKDIV			(0x000000ff <<   0)
+
+#define AI_FRAMING			0x00c
+#define AI_VALIDPOS			(0x000001ff << 22)
+#define AI_LEFTPOS			(0x000001ff << 13)
+#define AI_RIGHTPOS			(0x000001ff <<  4)
+#define AI_SSPOS_3_0			(0x0000000f <<  0)
+
+#define AI_BASE1			0x014
+#define AI_BASE2			0x018
+#define AI_BASE				(0x03ffffff <<  6)
+
+#define AI_SIZE				0x01c
+#define AI_SAMPLE_SIZE			(0x03ffffff <<  6)
+
+#define AI_INT_ACK			0x020
+#define AI_ACK_OVR			(0x00000001 <<  3)
+#define AI_ACK_HBE			(0x00000001 <<  2)
+#define AI_ACK2				(0x00000001 <<  1)
+#define AI_ACK1				(0x00000001 <<  0)
+
+#define AI_PWR_DOWN			0xff4
+#define AI_PWR_DWN			(0x00000001 <<  0)
+
+#endif /* __SAA716x_AIP_REG_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_boot.c linux/drivers/media/pci/saa716x/saa716x_boot.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_boot.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_boot.c	2017-08-11 23:31:25.785389714 +0200
@@ -0,0 +1,319 @@
+#include <linux/delay.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_greg_reg.h"
+#include "saa716x_cgu_reg.h"
+#include "saa716x_vip_reg.h"
+#include "saa716x_aip_reg.h"
+#include "saa716x_msi_reg.h"
+#include "saa716x_dma_reg.h"
+#include "saa716x_gpio_reg.h"
+#include "saa716x_fgpi_reg.h"
+#include "saa716x_dcs_reg.h"
+
+#include "saa716x_boot.h"
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+
+static int saa716x_ext_boot(struct saa716x_dev *saa716x)
+{
+	/* Write GREG boot_ready to 0
+	 * DW_0 = 0x0001_2018
+	 * DW_1 = 0x0000_0000
+	 */
+	SAA716x_EPWR(GREG, GREG_RSTU_CTRL, 0x00000000);
+
+	/* Clear VI0 interrupt
+	 * DW_2 = 0x0000_0fe8
+	 * DW_3 = 0x0000_03ff
+	 */
+	SAA716x_EPWR(VI0, INT_CLR_STATUS, 0x000003ff);
+
+	/* Clear VI1 interrupt
+	 * DW_4 = 0x0000_1fe8
+	 * DW_5 = 0x0000_03ff
+	 */
+	SAA716x_EPWR(VI1, INT_CLR_STATUS, 0x000003ff);
+
+	/* CLear FGPI0 interrupt
+	 * DW_6 = 0x0000_2fe8
+	 * DW_7 = 0x0000_007f
+	 */
+	SAA716x_EPWR(FGPI0, INT_CLR_STATUS, 0x0000007f);
+
+	/* Clear FGPI1 interrupt
+	 * DW_8 = 0x0000_3fe8
+	 * DW_9 = 0x0000_007f
+	 */
+	SAA716x_EPWR(FGPI1, INT_CLR_STATUS, 0x0000007f);
+
+	/* Clear FGPI2 interrupt
+	 * DW_10 = 0x0000_4fe8
+	 * DW_11 = 0x0000_007f
+	 */
+	SAA716x_EPWR(FGPI2, INT_CLR_STATUS, 0x0000007f);
+
+	/* Clear FGPI3 interrupt
+	 * DW_12 = 0x0000_5fe8
+	 * DW_13 = 0x0000_007f
+	 */
+	SAA716x_EPWR(FGPI3, INT_CLR_STATUS, 0x0000007f);
+
+	/* Clear AI0 interrupt
+	 * DW_14 = 0x0000_6020
+	 * DW_15 = 0x0000_000f
+	 */
+	SAA716x_EPWR(AI0, AI_INT_ACK, 0x0000000f);
+
+	/* Clear AI1 interrupt
+	 * DW_16 = 0x0000_7020
+	 * DW_17 = 0x0000_200f
+	 */
+	SAA716x_EPWR(AI1, AI_INT_ACK, 0x0000000f);
+
+	/* Set GREG boot_ready bit to 1
+	 * DW_18 = 0x0001_2018
+	 * DW_19 = 0x0000_2000
+	 */
+	SAA716x_EPWR(GREG, GREG_RSTU_CTRL, 0x00002000);
+#if 0
+	/* End of Boot script command
+	 * DW_20 = 0x0000_0006
+	 * Where to write this value ??
+	 * This seems very odd an address to trigger the
+	 * Boot Control State Machine !
+	 */
+	SAA716x_EPWR(VI0, 0x00000006, 0xffffffff);
+#endif
+	return 0;
+}
+
+/* Internal Bootscript configuration */
+static void saa716x_int_boot(struct saa716x_dev *saa716x)
+{
+	/* #1 Configure PCI COnfig space
+	 * GREG_JETSTR_CONFIG_0
+	 */
+	SAA716x_EPWR(GREG, GREG_SUBSYS_CONFIG, saa716x->pdev->subsystem_vendor);
+
+	/* GREG_JETSTR_CONFIG_1
+	 * pmcsr_scale:7 = 0x00
+	 * pmcsr_scale:6 = 0x00
+	 * pmcsr_scale:5 = 0x00
+	 * pmcsr_scale:4 = 0x00
+	 * pmcsr_scale:3 = 0x00
+	 * pmcsr_scale:2 = 0x00
+	 * pmcsr_scale:1 = 0x00
+	 * pmcsr_scale:0 = 0x00
+	 * BAR mask = 20 bit
+	 * BAR prefetch = no
+	 * MSI capable = 32 messages
+	 */
+	SAA716x_EPWR(GREG, GREG_MSI_BAR_PMCSR, 0x00001005);
+
+	/* GREG_JETSTR_CONFIG_2
+	 * pmcsr_data:3 = 0x0
+	 * pmcsr_data:2 = 0x0
+	 * pmcsr_data:1 = 0x0
+	 * pmcsr_data:0 = 0x0
+	 */
+	SAA716x_EPWR(GREG, GREG_PMCSR_DATA_1, 0x00000000);
+
+	/* GREG_JETSTR_CONFIG_3
+	 * pmcsr_data:7 = 0x0
+	 * pmcsr_data:6 = 0x0
+	 * pmcsr_data:5 = 0x0
+	 * pmcsr_data:4 = 0x0
+	 */
+	SAA716x_EPWR(GREG, GREG_PMCSR_DATA_2, 0x00000000);
+
+	/* #2 Release GREG resets
+	 * ip_rst_an
+	 * dpa1_rst_an
+	 * jetsream_reset_an
+	 */
+	SAA716x_EPWR(GREG, GREG_RSTU_CTRL, 0x00000e00);
+
+	/* #3 GPIO Setup
+	 * GPIO 25:24 = Output
+	 * GPIO Output "0" after Reset
+	 */
+	SAA716x_EPWR(GPIO, GPIO_OEN, 0xfcffffff);
+
+	/* #4 Custom stuff goes in here */
+
+	/* #5 Disable CGU Clocks
+	 * except for PHY, Jetstream, DPA1, DCS, Boot, GREG
+	 * CGU_PCR_0_3: pss_mmu_clk:0 = 0x0
+	 */
+	SAA716x_EPWR(CGU, CGU_PCR_0_3, 0x00000006);
+
+	/* CGU_PCR_0_4: pss_dtl2mtl_mmu_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_0_4, 0x00000006);
+
+	/* CGU_PCR_0_5: pss_msi_ck:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_0_5, 0x00000006);
+
+	/* CGU_PCR_0_7: pss_gpio_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_0_7, 0x00000006);
+
+	/* CGU_PCR_2_1: spi_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_2_1, 0x00000006);
+
+	/* CGU_PCR_3_2: i2c_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_3_2, 0x00000006);
+
+	/* CGU_PCR_4_1: phi_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_4_1, 0x00000006);
+
+	/* CGU_PCR_5: vip0_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_5, 0x00000006);
+
+	/* CGU_PCR_6: vip1_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_6, 0x00000006);
+
+	/* CGU_PCR_7: fgpi0_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_7, 0x00000006);
+
+	/* CGU_PCR_8: fgpi1_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_8, 0x00000006);
+
+	/* CGU_PCR_9: fgpi2_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_9, 0x00000006);
+
+	/* CGU_PCR_10: fgpi3_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_10, 0x00000006);
+
+	/* CGU_PCR_11: ai0_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_11, 0x00000006);
+
+	/* CGU_PCR_12: ai1_clk:0 = 0x0 */
+	SAA716x_EPWR(CGU, CGU_PCR_12, 0x00000006);
+
+	/* #6 Set GREG boot_ready = 0x1 */
+	SAA716x_EPWR(GREG, GREG_RSTU_CTRL, 0x00002000);
+
+	/* #7 Disable GREG CGU Clock */
+	SAA716x_EPWR(CGU, CGU_PCR_0_6, 0x00000006);
+
+	/* End of Bootscript command ?? */
+}
+
+int saa716x_core_boot(struct saa716x_dev *saa716x)
+{
+	struct saa716x_config *config = saa716x->config;
+
+	switch (config->boot_mode) {
+	case SAA716x_EXT_BOOT:
+		dprintk(SAA716x_DEBUG, 1, "Using External Boot from config");
+		saa716x_ext_boot(saa716x);
+		break;
+	case SAA716x_INT_BOOT:
+		dprintk(SAA716x_DEBUG, 1, "Using Internal Boot from config");
+		saa716x_int_boot(saa716x);
+		break;
+	default:
+		dprintk(SAA716x_ERROR, 1, "Unknown configuration %d", config->boot_mode);
+		break;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_core_boot);
+
+static void saa716x_bus_report(struct pci_dev *pdev, int enable)
+{
+	u32 reg;
+
+	pci_read_config_dword(pdev, 0x04, &reg);
+	if (enable)
+		reg |= 0x00000100; /* enable SERR */
+	else
+		reg &= 0xfffffeff; /* disable SERR */
+	pci_write_config_dword(pdev, 0x04, reg);
+
+	pci_read_config_dword(pdev, 0x58, &reg);
+	reg &= 0xfffffffd;
+	pci_write_config_dword(pdev, 0x58, reg);
+}
+
+int saa716x_jetpack_init(struct saa716x_dev *saa716x)
+{
+	/*
+	 * configure PHY through config space not to report
+	 * non-fatal error messages to avoid problems with
+	 * quirky BIOS'es
+	 */
+	saa716x_bus_report(saa716x->pdev, 0);
+
+	/*
+	 * create time out for blocks that have no clock
+	 * helps with lower bitrates on FGPI
+	 */
+	SAA716x_EPWR(DCS, DCSC_CTRL, ENABLE_TIMEOUT);
+
+	/* Reset all blocks */
+	SAA716x_EPWR(MSI, MSI_SW_RST, MSI_SW_RESET);
+	SAA716x_EPWR(MMU, MMU_SW_RST, MMU_SW_RESET);
+	SAA716x_EPWR(BAM, BAM_SW_RST, BAM_SW_RESET);
+
+	switch (saa716x->pdev->device) {
+	case SAA7162:
+		dprintk(SAA716x_DEBUG, 1, "SAA%02x Decoder disable", saa716x->pdev->device);
+		SAA716x_EPWR(GPIO, GPIO_OEN, 0xfcffffff);
+		SAA716x_EPWR(GPIO, GPIO_WR,  0x00000000); /* Disable decoders */
+		msleep(10);
+		SAA716x_EPWR(GPIO, GPIO_WR,  0x03000000); /* Enable decoders */
+		break;
+	case SAA7161:
+		dprintk(SAA716x_DEBUG, 1, "SAA%02x Decoder disable", saa716x->pdev->device);
+		SAA716x_EPWR(GPIO, GPIO_OEN, 0xfeffffff);
+		SAA716x_EPWR(GPIO, GPIO_WR,  0x00000000); /* Disable decoders */
+		msleep(10);
+		SAA716x_EPWR(GPIO, GPIO_WR,  0x01000000); /* Enable decoder */
+		break;
+	case SAA7160:
+		saa716x->i2c_rate = SAA716x_I2C_RATE_100;
+		break;
+	default:
+		dprintk(SAA716x_ERROR, 1, "Unknown device (0x%02x)", saa716x->pdev->device);
+		return -ENODEV;
+	}
+
+	/* General setup for MMU */
+	SAA716x_EPWR(MMU, MMU_MODE, 0x14);
+	dprintk(SAA716x_DEBUG, 1, "SAA%02x Jetpack Successfully initialized", saa716x->pdev->device);
+
+	return 0;
+}
+EXPORT_SYMBOL(saa716x_jetpack_init);
+
+void saa716x_core_reset(struct saa716x_dev *saa716x)
+{
+	dprintk(SAA716x_DEBUG, 1, "RESET Modules");
+
+	/* VIP */
+	SAA716x_EPWR(VI0, VI_MODE, SOFT_RESET);
+	SAA716x_EPWR(VI1, VI_MODE, SOFT_RESET);
+
+	/* FGPI */
+	SAA716x_EPWR(FGPI0, FGPI_SOFT_RESET, FGPI_SOFTWARE_RESET);
+	SAA716x_EPWR(FGPI1, FGPI_SOFT_RESET, FGPI_SOFTWARE_RESET);
+	SAA716x_EPWR(FGPI2, FGPI_SOFT_RESET, FGPI_SOFTWARE_RESET);
+	SAA716x_EPWR(FGPI3, FGPI_SOFT_RESET, FGPI_SOFTWARE_RESET);
+
+	/* AIP */
+	SAA716x_EPWR(AI0, AI_CTL, AI_RESET);
+	SAA716x_EPWR(AI1, AI_CTL, AI_RESET);
+
+	/* BAM */
+	SAA716x_EPWR(BAM, BAM_SW_RST, BAM_SW_RESET);
+
+	/* MMU */
+	SAA716x_EPWR(MMU, MMU_SW_RST, MMU_SW_RESET);
+
+	/* MSI */
+	SAA716x_EPWR(MSI, MSI_SW_RST, MSI_SW_RESET);
+}
+EXPORT_SYMBOL_GPL(saa716x_core_reset);
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_boot.h linux/drivers/media/pci/saa716x/saa716x_boot.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_boot.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_boot.h	2017-08-11 23:31:25.785389714 +0200
@@ -0,0 +1,18 @@
+#ifndef __SAA716x_BOOT_H
+#define __SAA716x_BOOT_H
+
+#define DISABLE_TIMEOUT		0x17
+#define ENABLE_TIMEOUT		0x16
+
+enum saa716x_boot_mode {
+	SAA716x_EXT_BOOT = 1,
+	SAA716x_INT_BOOT, /* GPIO[31:30] = 0x01 */
+};
+
+struct saa716x_dev;
+
+extern int saa716x_core_boot(struct saa716x_dev *saa716x);
+extern int saa716x_jetpack_init(struct saa716x_dev *saa716x);
+extern void saa716x_core_reset(struct saa716x_dev *saa716x);
+
+#endif /* __SAA716x_BOOT_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_budget.c linux/drivers/media/pci/saa716x/saa716x_budget.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_budget.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_budget.c	2017-08-11 23:31:25.797389647 +0200
@@ -0,0 +1,664 @@
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include <linux/i2c.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_gpio_reg.h"
+#include "saa716x_greg_reg.h"
+#include "saa716x_msi_reg.h"
+
+#include "saa716x_adap.h"
+#include "saa716x_i2c.h"
+#include "saa716x_msi.h"
+#include "saa716x_budget.h"
+#include "saa716x_gpio.h"
+#include "saa716x_rom.h"
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+
+#include "mb86a16.h"
+#include "stv6110x.h"
+#include "stv090x.h"
+
+unsigned int verbose;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
+
+unsigned int int_type;
+module_param(int_type, int, 0644);
+MODULE_PARM_DESC(int_type, "force Interrupt Handler type: 0=INT-A, 1=MSI, 2=MSI-X. default INT-A mode");
+
+#define DRIVER_NAME	"SAA716x Budget"
+
+static int saa716x_budget_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+{
+	struct saa716x_dev *saa716x;
+	int err = 0;
+
+	saa716x = kzalloc(sizeof (struct saa716x_dev), GFP_KERNEL);
+	if (saa716x == NULL) {
+		printk(KERN_ERR "saa716x_budget_pci_probe ERROR: out of memory\n");
+		err = -ENOMEM;
+		goto fail0;
+	}
+
+	saa716x->verbose	= verbose;
+	saa716x->int_type	= int_type;
+	saa716x->pdev		= pdev;
+	saa716x->config		= (struct saa716x_config *) pci_id->driver_data;
+
+	err = saa716x_pci_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x PCI Initialization failed");
+		goto fail1;
+	}
+
+	err = saa716x_cgu_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x CGU Init failed");
+		goto fail1;
+	}
+
+	err = saa716x_core_boot(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x Core Boot failed");
+		goto fail2;
+	}
+	dprintk(SAA716x_DEBUG, 1, "SAA716x Core Boot Success");
+
+	err = saa716x_msi_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x MSI Init failed");
+		goto fail2;
+	}
+
+	err = saa716x_jetpack_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x Jetpack core initialization failed");
+		goto fail1;
+	}
+
+	err = saa716x_i2c_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x I2C Initialization failed");
+		goto fail3;
+	}
+
+	saa716x_gpio_init(saa716x);
+
+	err = saa716x_dump_eeprom(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x EEPROM dump failed");
+	}
+
+	err = saa716x_eeprom_data(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x EEPROM read failed");
+	}
+
+	/* set default port mapping */
+	SAA716x_EPWR(GREG, GREG_VI_CTRL, 0x04080FA9);
+	/* enable FGPI3 and FGPI1 for TS input from Port 2 and 6 */
+	SAA716x_EPWR(GREG, GREG_FGPI_CTRL, 0x321);
+
+	err = saa716x_dvb_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x DVB initialization failed");
+		goto fail4;
+	}
+
+	return 0;
+
+fail4:
+	saa716x_dvb_exit(saa716x);
+fail3:
+	saa716x_i2c_exit(saa716x);
+fail2:
+	saa716x_pci_exit(saa716x);
+fail1:
+	kfree(saa716x);
+fail0:
+	return err;
+}
+
+static void saa716x_budget_pci_remove(struct pci_dev *pdev)
+{
+	struct saa716x_dev *saa716x = pci_get_drvdata(pdev);
+
+	saa716x_dvb_exit(saa716x);
+	saa716x_i2c_exit(saa716x);
+	saa716x_pci_exit(saa716x);
+	kfree(saa716x);
+}
+
+static irqreturn_t saa716x_budget_pci_irq(int irq, void *dev_id)
+{
+	struct saa716x_dev *saa716x	= (struct saa716x_dev *) dev_id;
+
+	u32 stat_h, stat_l, mask_h, mask_l;
+
+	if (unlikely(saa716x == NULL)) {
+		printk("%s: saa716x=NULL", __func__);
+		return IRQ_NONE;
+	}
+
+	stat_l = SAA716x_EPRD(MSI, MSI_INT_STATUS_L);
+	stat_h = SAA716x_EPRD(MSI, MSI_INT_STATUS_H);
+	mask_l = SAA716x_EPRD(MSI, MSI_INT_ENA_L);
+	mask_h = SAA716x_EPRD(MSI, MSI_INT_ENA_H);
+
+	dprintk(SAA716x_DEBUG, 1, "MSI STAT L=<%02x> H=<%02x>, CTL L=<%02x> H=<%02x>",
+		stat_l, stat_h, mask_l, mask_h);
+
+	if (!((stat_l & mask_l) || (stat_h & mask_h)))
+		return IRQ_NONE;
+
+	if (stat_l)
+		SAA716x_EPWR(MSI, MSI_INT_STATUS_CLR_L, stat_l);
+
+	if (stat_h)
+		SAA716x_EPWR(MSI, MSI_INT_STATUS_CLR_H, stat_h);
+
+	saa716x_msi_event(saa716x, stat_l, stat_h);
+#if 0
+	dprintk(SAA716x_DEBUG, 1, "VI STAT 0=<%02x> 1=<%02x>, CTL 1=<%02x> 2=<%02x>",
+		SAA716x_EPRD(VI0, INT_STATUS),
+		SAA716x_EPRD(VI1, INT_STATUS),
+		SAA716x_EPRD(VI0, INT_ENABLE),
+		SAA716x_EPRD(VI1, INT_ENABLE));
+
+	dprintk(SAA716x_DEBUG, 1, "FGPI STAT 0=<%02x> 1=<%02x>, CTL 1=<%02x> 2=<%02x>",
+		SAA716x_EPRD(FGPI0, INT_STATUS),
+		SAA716x_EPRD(FGPI1, INT_STATUS),
+		SAA716x_EPRD(FGPI0, INT_ENABLE),
+		SAA716x_EPRD(FGPI0, INT_ENABLE));
+
+	dprintk(SAA716x_DEBUG, 1, "FGPI STAT 2=<%02x> 3=<%02x>, CTL 2=<%02x> 3=<%02x>",
+		SAA716x_EPRD(FGPI2, INT_STATUS),
+		SAA716x_EPRD(FGPI3, INT_STATUS),
+		SAA716x_EPRD(FGPI2, INT_ENABLE),
+		SAA716x_EPRD(FGPI3, INT_ENABLE));
+
+	dprintk(SAA716x_DEBUG, 1, "AI STAT 0=<%02x> 1=<%02x>, CTL 0=<%02x> 1=<%02x>",
+		SAA716x_EPRD(AI0, AI_STATUS),
+		SAA716x_EPRD(AI1, AI_STATUS),
+		SAA716x_EPRD(AI0, AI_CTL),
+		SAA716x_EPRD(AI1, AI_CTL));
+
+	dprintk(SAA716x_DEBUG, 1, "I2C STAT 0=<%02x> 1=<%02x>, CTL 0=<%02x> 1=<%02x>",
+		SAA716x_EPRD(I2C_A, INT_STATUS),
+		SAA716x_EPRD(I2C_B, INT_STATUS),
+		SAA716x_EPRD(I2C_A, INT_ENABLE),
+		SAA716x_EPRD(I2C_B, INT_ENABLE));
+
+	dprintk(SAA716x_DEBUG, 1, "DCS STAT=<%02x>, CTL=<%02x>",
+		SAA716x_EPRD(DCS, DCSC_INT_STATUS),
+		SAA716x_EPRD(DCS, DCSC_INT_ENABLE));
+#endif
+
+	if (stat_l) {
+		if (stat_l & MSI_INT_TAGACK_FGPI_0) {
+			tasklet_schedule(&saa716x->fgpi[0].tasklet);
+		}
+		if (stat_l & MSI_INT_TAGACK_FGPI_1) {
+			tasklet_schedule(&saa716x->fgpi[1].tasklet);
+		}
+		if (stat_l & MSI_INT_TAGACK_FGPI_2) {
+			tasklet_schedule(&saa716x->fgpi[2].tasklet);
+		}
+		if (stat_l & MSI_INT_TAGACK_FGPI_3) {
+			tasklet_schedule(&saa716x->fgpi[3].tasklet);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void demux_worker(unsigned long data)
+{
+	struct saa716x_fgpi_stream_port *fgpi_entry = (struct saa716x_fgpi_stream_port *)data;
+	struct saa716x_dev *saa716x = fgpi_entry->saa716x;
+	struct dvb_demux *demux;
+	u32 fgpi_index;
+	u32 i;
+	u32 write_index;
+
+	fgpi_index = fgpi_entry->dma_channel - 6;
+	demux = NULL;
+	for (i = 0; i < saa716x->config->adapters; i++) {
+		if (saa716x->config->adap_config[i].ts_port == fgpi_index) {
+			demux = &saa716x->saa716x_adap[i].demux;
+			break;
+		}
+	}
+	if (demux == NULL) {
+		printk(KERN_ERR "%s: unexpected channel %u\n",
+		       __func__, fgpi_entry->dma_channel);
+		return;
+	}
+
+	write_index = saa716x_fgpi_get_write_index(saa716x, fgpi_index);
+	if (write_index < 0)
+		return;
+
+	dprintk(SAA716x_DEBUG, 1, "dma buffer = %d", write_index);
+
+	if (write_index == fgpi_entry->read_index) {
+		printk(KERN_DEBUG "%s: called but nothing to do\n", __func__);
+		return;
+	}
+
+	do {
+		u8 *data = (u8 *)fgpi_entry->dma_buf[fgpi_entry->read_index].mem_virt;
+
+		pci_dma_sync_sg_for_cpu(saa716x->pdev,
+			fgpi_entry->dma_buf[fgpi_entry->read_index].sg_list,
+			fgpi_entry->dma_buf[fgpi_entry->read_index].list_len,
+			PCI_DMA_FROMDEVICE);
+
+		dvb_dmx_swfilter(demux, data, 348 * 188);
+
+		fgpi_entry->read_index = (fgpi_entry->read_index + 1) & 7;
+	} while (write_index != fgpi_entry->read_index);
+}
+
+
+#define SAA716x_MODEL_TWINHAN_VP3071	"Twinhan/Azurewave VP-3071"
+#define SAA716x_DEV_TWINHAN_VP3071	"2x DVB-T"
+
+static int saa716x_vp3071_frontend_attach(struct saa716x_adapter *adapter, int count)
+{
+	struct saa716x_dev *saa716x = adapter->saa716x;
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) SAA716x frontend Init", count);
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) Device ID=%02x", count, saa716x->pdev->subsystem_device);
+
+	return -ENODEV;
+}
+
+static struct saa716x_config saa716x_vp3071_config = {
+	.model_name		= SAA716x_MODEL_TWINHAN_VP3071,
+	.dev_type		= SAA716x_DEV_TWINHAN_VP3071,
+	.boot_mode		= SAA716x_EXT_BOOT,
+	.adapters		= 2,
+	.frontend_attach	= saa716x_vp3071_frontend_attach,
+	.irq_handler		= saa716x_budget_pci_irq,
+	.i2c_rate		= SAA716x_I2C_RATE_100,
+};
+
+
+#define SAA716x_MODEL_TWINHAN_VP1028	"Twinhan/Azurewave VP-1028"
+#define SAA716x_DEV_TWINHAN_VP1028	"DVB-S"
+
+static int vp1028_dvbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct saa716x_dev *saa716x = fe->dvb->priv;
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		dprintk(SAA716x_ERROR, 1, "Polarization=[13V]");
+		break;
+	case SEC_VOLTAGE_18:
+		dprintk(SAA716x_ERROR, 1, "Polarization=[18V]");
+		break;
+	case SEC_VOLTAGE_OFF:
+		dprintk(SAA716x_ERROR, 1, "Frontend (dummy) POWERDOWN");
+		break;
+	default:
+		dprintk(SAA716x_ERROR, 1, "Invalid = (%d)", (u32 ) voltage);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct mb86a16_config vp1028_mb86a16_config = {
+	.demod_address	= 0x08,
+	.set_voltage	= vp1028_dvbs_set_voltage,
+};
+
+static int saa716x_vp1028_frontend_attach(struct saa716x_adapter *adapter, int count)
+{
+	struct saa716x_dev *saa716x = adapter->saa716x;
+	struct saa716x_i2c *i2c = &saa716x->i2c[1];
+
+	if (count == 0) {
+
+		mutex_lock(&saa716x->adap_lock);
+
+		dprintk(SAA716x_DEBUG, 1, "Adapter (%d) Power ON", count);
+		saa716x_gpio_set_output(saa716x, 10);
+		msleep(1);
+
+		/* VP-1028 has inverted power supply control */
+		saa716x_gpio_write(saa716x, 10, 1); /* set to standby */
+		saa716x_gpio_write(saa716x, 10, 0); /* switch it on */
+		msleep(100);
+
+		dprintk(SAA716x_DEBUG, 1, "Adapter (%d) Reset", count);
+		saa716x_gpio_set_output(saa716x, 12);
+		msleep(1);
+
+		/* reset demodulator (Active LOW) */
+		saa716x_gpio_write(saa716x, 12, 1);
+		msleep(100);
+		saa716x_gpio_write(saa716x, 12, 0);
+		msleep(100);
+		saa716x_gpio_write(saa716x, 12, 1);
+		msleep(100);
+
+		mutex_unlock(&saa716x->adap_lock);
+
+		dprintk(SAA716x_ERROR, 1, "Probing for MB86A16 (DVB-S/DSS)");
+		adapter->fe = mb86a16_attach(&vp1028_mb86a16_config, &i2c->i2c_adapter);
+		if (adapter->fe) {
+			dprintk(SAA716x_ERROR, 1, "found MB86A16 DVB-S/DSS frontend @0x%02x",
+				vp1028_mb86a16_config.demod_address);
+
+		} else {
+			goto exit;
+		}
+		dprintk(SAA716x_ERROR, 1, "Done!");
+	}
+
+	return 0;
+exit:
+	dprintk(SAA716x_ERROR, 1, "Frontend attach failed");
+	return -ENODEV;
+}
+
+static struct saa716x_config saa716x_vp1028_config = {
+	.model_name		= SAA716x_MODEL_TWINHAN_VP1028,
+	.dev_type		= SAA716x_DEV_TWINHAN_VP1028,
+	.boot_mode		= SAA716x_EXT_BOOT,
+	.adapters		= 1,
+	.frontend_attach	= saa716x_vp1028_frontend_attach,
+	.irq_handler		= saa716x_budget_pci_irq,
+	.i2c_rate		= SAA716x_I2C_RATE_100,
+};
+
+
+#define SAA716x_MODEL_TWINHAN_VP6002	"Twinhan/Azurewave VP-6002"
+#define SAA716x_DEV_TWINHAN_VP6002	"DVB-S"
+
+static int saa716x_vp6002_frontend_attach(struct saa716x_adapter *adapter, int count)
+{
+	struct saa716x_dev *saa716x = adapter->saa716x;
+
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) SAA716x frontend Init", count);
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) Device ID=%02x", count, saa716x->pdev->subsystem_device);
+
+	return -ENODEV;
+}
+
+static struct saa716x_config saa716x_vp6002_config = {
+	.model_name		= SAA716x_MODEL_TWINHAN_VP6002,
+	.dev_type		= SAA716x_DEV_TWINHAN_VP6002,
+	.boot_mode		= SAA716x_EXT_BOOT,
+	.adapters		= 1,
+	.frontend_attach	= saa716x_vp6002_frontend_attach,
+	.irq_handler		= saa716x_budget_pci_irq,
+	.i2c_rate		= SAA716x_I2C_RATE_100,
+};
+
+
+#define SAA716x_MODEL_KNC1_DUALS2	"KNC One Dual S2"
+#define SAA716x_DEV_KNC1_DUALS2		"1xDVB-S + 1xDVB-S/S2"
+
+static int saa716x_knc1_duals2_frontend_attach(struct saa716x_adapter *adapter, int count)
+{
+	struct saa716x_dev *saa716x = adapter->saa716x;
+
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) SAA716x frontend Init", count);
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) Device ID=%02x", count, saa716x->pdev->subsystem_device);
+
+	return -ENODEV;
+}
+
+static struct saa716x_config saa716x_knc1_duals2_config = {
+	.model_name		= SAA716x_MODEL_KNC1_DUALS2,
+	.dev_type		= SAA716x_DEV_KNC1_DUALS2,
+	.boot_mode		= SAA716x_EXT_BOOT,
+	.adapters		= 2,
+	.frontend_attach	= saa716x_knc1_duals2_frontend_attach,
+	.irq_handler		= saa716x_budget_pci_irq,
+	.i2c_rate		= SAA716x_I2C_RATE_100,
+};
+
+
+#define SAA716x_MODEL_SKYSTAR2_EXPRESS_HD	"SkyStar 2 eXpress HD"
+#define SAA716x_DEV_SKYSTAR2_EXPRESS_HD		"DVB-S/S2"
+
+static struct stv090x_config skystar2_stv090x_config = {
+	.device			= STV0903,
+	.demod_mode		= STV090x_SINGLE,
+	.clk_mode		= STV090x_CLK_EXT,
+
+	.xtal			= 8000000,
+	.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 int skystar2_set_voltage(struct dvb_frontend *fe,
+				enum fe_sec_voltage voltage)
+{
+	int err;
+	u8 en = 0;
+	u8 sel = 0;
+
+	switch (voltage) {
+	case SEC_VOLTAGE_OFF:
+		en = 0;
+		break;
+
+	case SEC_VOLTAGE_13:
+		en = 1;
+		sel = 0;
+		break;
+
+	case SEC_VOLTAGE_18:
+		en = 1;
+		sel = 1;
+		break;
+
+	default:
+		break;
+	}
+
+	err = stv090x_set_gpio(fe, 2, 0, en, 0);
+	if (err < 0)
+		goto exit;
+	err = stv090x_set_gpio(fe, 3, 0, sel, 0);
+	if (err < 0)
+		goto exit;
+
+	return 0;
+exit:
+	return err;
+}
+
+static int skystar2_voltage_boost(struct dvb_frontend *fe, long arg)
+{
+	int err;
+	u8 value;
+
+	if (arg)
+		value = 1;
+	else
+		value = 0;
+
+	err = stv090x_set_gpio(fe, 4, 0, value, 0);
+	if (err < 0)
+		goto exit;
+
+	return 0;
+exit:
+	return err;
+}
+
+static struct stv6110x_config skystar2_stv6110x_config = {
+	.addr			= 0x60,
+	.refclk			= 16000000,
+	.clk_div		= 2,
+};
+
+static int skystar2_express_hd_frontend_attach(struct saa716x_adapter *adapter,
+					       int count)
+{
+	struct saa716x_dev *saa716x = adapter->saa716x;
+	struct saa716x_i2c *i2c = &saa716x->i2c[SAA716x_I2C_BUS_B];
+	struct stv6110x_devctl *ctl;
+
+	if (count < saa716x->config->adapters) {
+		dprintk(SAA716x_DEBUG, 1, "Adapter (%d) SAA716x frontend Init",
+			count);
+		dprintk(SAA716x_DEBUG, 1, "Adapter (%d) Device ID=%02x", count,
+			saa716x->pdev->subsystem_device);
+
+		saa716x_gpio_set_output(saa716x, 26);
+
+		/* Reset the demodulator */
+		saa716x_gpio_write(saa716x, 26, 1);
+		msleep(10);
+		saa716x_gpio_write(saa716x, 26, 0);
+		msleep(10);
+		saa716x_gpio_write(saa716x, 26, 1);
+		msleep(10);
+
+		adapter->fe = dvb_attach(stv090x_attach,
+					 &skystar2_stv090x_config,
+					 &i2c->i2c_adapter,
+					 STV090x_DEMODULATOR_0);
+
+		if (adapter->fe) {
+			dprintk(SAA716x_NOTICE, 1, "found STV0903 @0x%02x",
+				skystar2_stv090x_config.address);
+		} else {
+			goto exit;
+		}
+
+		adapter->fe->ops.set_voltage = skystar2_set_voltage;
+		adapter->fe->ops.enable_high_lnb_voltage = skystar2_voltage_boost;
+
+		ctl = dvb_attach(stv6110x_attach,
+				 adapter->fe,
+				 &skystar2_stv6110x_config,
+				 &i2c->i2c_adapter);
+
+		if (ctl) {
+			dprintk(SAA716x_NOTICE, 1, "found STV6110(A) @0x%02x",
+				skystar2_stv6110x_config.addr);
+
+			skystar2_stv090x_config.tuner_init	    = ctl->tuner_init;
+			skystar2_stv090x_config.tuner_sleep	    = ctl->tuner_sleep;
+			skystar2_stv090x_config.tuner_set_mode	    = ctl->tuner_set_mode;
+			skystar2_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
+			skystar2_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
+			skystar2_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+			skystar2_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+			skystar2_stv090x_config.tuner_set_bbgain    = ctl->tuner_set_bbgain;
+			skystar2_stv090x_config.tuner_get_bbgain    = ctl->tuner_get_bbgain;
+			skystar2_stv090x_config.tuner_set_refclk    = ctl->tuner_set_refclk;
+			skystar2_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 (adapter->fe->ops.init)
+				adapter->fe->ops.init(adapter->fe);
+		} else {
+			goto exit;
+		}
+
+		dprintk(SAA716x_ERROR, 1, "Done!");
+		return 0;
+	}
+exit:
+	dprintk(SAA716x_ERROR, 1, "Frontend attach failed");
+	return -ENODEV;
+}
+
+static struct saa716x_config skystar2_express_hd_config = {
+	.model_name		= SAA716x_MODEL_SKYSTAR2_EXPRESS_HD,
+	.dev_type		= SAA716x_DEV_SKYSTAR2_EXPRESS_HD,
+	.boot_mode		= SAA716x_EXT_BOOT,
+	.adapters		= 1,
+	.frontend_attach	= skystar2_express_hd_frontend_attach,
+	.irq_handler		= saa716x_budget_pci_irq,
+	.i2c_rate		= SAA716x_I2C_RATE_100,
+	.adap_config		= {
+		{
+			/* Adapter 0 */
+			.ts_port = 1, /* using FGPI 1 */
+			.worker = demux_worker
+		}
+	}
+};
+
+
+static struct pci_device_id saa716x_budget_pci_table[] = {
+
+	MAKE_ENTRY(TWINHAN_TECHNOLOGIES, TWINHAN_VP_1028, SAA7160, &saa716x_vp1028_config), /* VP-1028 */
+	MAKE_ENTRY(TWINHAN_TECHNOLOGIES, TWINHAN_VP_3071, SAA7160, &saa716x_vp3071_config), /* VP-3071 */
+	MAKE_ENTRY(TWINHAN_TECHNOLOGIES, TWINHAN_VP_6002, SAA7160, &saa716x_vp6002_config), /* VP-6002 */
+	MAKE_ENTRY(KNC_One, KNC_Dual_S2, SAA7160, &saa716x_knc1_duals2_config),
+	MAKE_ENTRY(TECHNISAT, SKYSTAR2_EXPRESS_HD, SAA7160, &skystar2_express_hd_config),
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, saa716x_budget_pci_table);
+
+static struct pci_driver saa716x_budget_pci_driver = {
+	.name			= DRIVER_NAME,
+	.id_table		= saa716x_budget_pci_table,
+	.probe			= saa716x_budget_pci_probe,
+	.remove			= saa716x_budget_pci_remove,
+};
+
+static int __init saa716x_budget_init(void)
+{
+	return pci_register_driver(&saa716x_budget_pci_driver);
+}
+
+static void __exit saa716x_budget_exit(void)
+{
+	return pci_unregister_driver(&saa716x_budget_pci_driver);
+}
+
+module_init(saa716x_budget_init);
+module_exit(saa716x_budget_exit);
+
+MODULE_DESCRIPTION("SAA716x Budget driver");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_LICENSE("GPL");
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_budget.h linux/drivers/media/pci/saa716x/saa716x_budget.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_budget.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_budget.h	2017-08-11 23:31:25.797389647 +0200
@@ -0,0 +1,15 @@
+#ifndef __SAA716x_BUDGET_H
+#define __SAA716x_BUDGET_H
+
+#define TWINHAN_TECHNOLOGIES	0x1822
+#define TWINHAN_VP_3071		0x0039
+#define TWINHAN_VP_1028		0x0044
+#define TWINHAN_VP_6002		0x0047
+
+#define KNC_One			0x1894
+#define KNC_Dual_S2		0x0110
+
+#define TECHNISAT		0x1AE4
+#define SKYSTAR2_EXPRESS_HD	0x0700
+
+#endif /* __SAA716x_BUDGET_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_cgu.c linux/drivers/media/pci/saa716x/saa716x_cgu.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_cgu.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_cgu.c	2017-08-11 23:31:25.797389647 +0200
@@ -0,0 +1,540 @@
+#include <linux/delay.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_cgu_reg.h"
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+
+u32 cgu_clk[14] = {
+	CGU_FDC_0,
+	CGU_FDC_1,
+	CGU_FDC_2,
+	CGU_FDC_3,
+	CGU_FDC_4,
+	CGU_FDC_5,
+	CGU_FDC_6,
+	CGU_FDC_7,
+	CGU_FDC_8,
+	CGU_FDC_9,
+	CGU_FDC_10,
+	CGU_FDC_11,
+	CGU_FDC_12,
+	CGU_FDC_13
+};
+
+char *clk_desc[14] = {
+	"Clk PSS",
+	"Clk DCS",
+	"Clk SPI",
+	"Clk I2C/Boot",
+	"Clk PHI",
+	"Clk VI0",
+	"Clk VI1",
+	"Clk FGPI0",
+	"Clk FGPI1",
+	"Clk FGPI2",
+	"Clk FGPI3",
+	"Clk AI0",
+	"Clk AI1",
+	"Clk Phy"
+};
+
+int saa716x_getbootscript_setup(struct saa716x_dev *saa716x)
+{
+	struct saa716x_cgu *cgu = &saa716x->cgu;
+
+	u8 i;
+	s8 N = 0;
+	s16 M = 0;
+
+	SAA716x_EPWR(CGU, CGU_PCR_0_6, CGU_PCR_RUN); /* GREG */
+	SAA716x_EPWR(CGU, CGU_PCR_0_3, CGU_PCR_RUN); /* PSS_MMU */
+	SAA716x_EPWR(CGU, CGU_PCR_0_4, CGU_PCR_RUN); /* PSS_DTL2MTL */
+	SAA716x_EPWR(CGU, CGU_PCR_0_5, CGU_PCR_RUN); /* MSI */
+	SAA716x_EPWR(CGU, CGU_PCR_3_2, CGU_PCR_RUN); /* I2C */
+	SAA716x_EPWR(CGU, CGU_PCR_4_1, CGU_PCR_RUN); /* PHI */
+	SAA716x_EPWR(CGU, CGU_PCR_0_7, CGU_PCR_RUN); /* GPIO */
+	SAA716x_EPWR(CGU, CGU_PCR_2_1, CGU_PCR_RUN); /* SPI */
+	SAA716x_EPWR(CGU, CGU_PCR_1_1, CGU_PCR_RUN); /* DCS */
+	SAA716x_EPWR(CGU, CGU_PCR_3_1, CGU_PCR_RUN); /* BOOT */
+
+	/* get all dividers */
+	for (i = 0; i < CGU_CLKS; i++) {
+		cgu->clk_boot_div[i] = SAA716x_EPRD(CGU, cgu_clk[i]);
+		cgu->clk_curr_div[i] = cgu->clk_boot_div[i];
+
+		N = (cgu->clk_boot_div[i] >> 11) & 0xff;
+		N *= -1;
+		M = ((cgu->clk_boot_div[i] >>  3) & 0xff) + N;
+
+		if (M)
+			cgu->clk_freq[i] = (u32 ) N * PLL_FREQ / (u32 ) M;
+		else
+			cgu->clk_freq[i] = 0;
+
+		dprintk(SAA716x_DEBUG, 1, "Domain %d: %s <0x%02x> Divider: 0x%x --> N=%d, M=%d, freq=%d",
+			i, clk_desc[i], cgu_clk[i], cgu->clk_boot_div[i], N, M, cgu->clk_freq[i]);
+	}
+	/* store clock settings */
+	cgu->clk_vi_0[0] = cgu->clk_freq[CLK_DOMAIN_VI0];
+	cgu->clk_vi_0[1] = cgu->clk_freq[CLK_DOMAIN_VI0];
+	cgu->clk_vi_0[2] = cgu->clk_freq[CLK_DOMAIN_VI0];
+	cgu->clk_vi_1[0] = cgu->clk_freq[CLK_DOMAIN_VI1];
+	cgu->clk_vi_1[1] = cgu->clk_freq[CLK_DOMAIN_VI1];
+	cgu->clk_vi_1[2] = cgu->clk_freq[CLK_DOMAIN_VI1];
+
+	return 0;
+}
+
+int saa716x_set_clk_internal(struct saa716x_dev *saa716x, u32 port)
+{
+	struct saa716x_cgu *cgu = &saa716x->cgu;
+
+	u8 delay = 1;
+
+	switch (port) {
+	case PORT_VI0_VIDEO:
+		cgu->clk_int_port[PORT_VI0_VIDEO] = 1;
+
+		if (!cgu->clk_int_port[PORT_VI0_VBI]) {
+			delay = 0;
+			break;
+		}
+
+		SAA716x_CGU_CLKRUN(5);
+		break;
+
+	case PORT_VI0_VBI:
+		cgu->clk_int_port[PORT_VI0_VBI] = 1;
+
+		if (!cgu->clk_int_port[PORT_VI0_VIDEO]) {
+			delay = 0;
+			break;
+		}
+
+		SAA716x_CGU_CLKRUN(5);
+		break;
+
+	case PORT_VI1_VIDEO:
+		cgu->clk_int_port[PORT_VI1_VIDEO] = 1;
+
+		if (!cgu->clk_int_port[PORT_VI1_VBI]) {
+			delay = 0;
+			break;
+		}
+
+		SAA716x_CGU_CLKRUN(6);
+		break;
+
+	case PORT_VI1_VBI:
+		cgu->clk_int_port[PORT_VI1_VBI] = 1;
+
+		if (!cgu->clk_int_port[PORT_VI1_VIDEO]) {
+			delay = 0;
+			break;
+		}
+
+		SAA716x_CGU_CLKRUN(6);
+		break;
+
+	case PORT_FGPI0:
+		cgu->clk_int_port[PORT_FGPI0] = 1;
+		SAA716x_CGU_CLKRUN(7);
+		break;
+
+	case PORT_FGPI1:
+		cgu->clk_int_port[PORT_FGPI1] = 1;
+		SAA716x_CGU_CLKRUN(8);
+		break;
+
+	case PORT_FGPI2:
+		cgu->clk_int_port[PORT_FGPI2] = 1;
+		SAA716x_CGU_CLKRUN(9);
+		break;
+
+	case PORT_FGPI3:
+		cgu->clk_int_port[PORT_FGPI3] = 1;
+		SAA716x_CGU_CLKRUN(10);
+		break;
+
+	case PORT_AI0:
+		cgu->clk_int_port[PORT_AI0] = 1;
+		SAA716x_CGU_CLKRUN(11);
+		break;
+
+	case PORT_AI1:
+		cgu->clk_int_port[PORT_AI1] = 1;
+		SAA716x_CGU_CLKRUN(12);
+		break;
+
+	case PORT_ALL:
+		SAA716x_CGU_CLKRUN(5);
+		SAA716x_CGU_CLKRUN(6);
+		SAA716x_CGU_CLKRUN(7);
+		SAA716x_CGU_CLKRUN(8);
+		SAA716x_CGU_CLKRUN(9);
+		SAA716x_CGU_CLKRUN(10);
+		SAA716x_CGU_CLKRUN(11);
+		SAA716x_CGU_CLKRUN(12);
+
+		cgu->clk_int_port[PORT_VI0_VIDEO] = 1;
+		cgu->clk_int_port[PORT_VI0_VBI] = 1;
+		cgu->clk_int_port[PORT_VI1_VIDEO] = 1;
+		cgu->clk_int_port[PORT_VI1_VBI] = 1;
+		cgu->clk_int_port[PORT_FGPI0] = 1;
+		cgu->clk_int_port[PORT_FGPI1] = 1;
+		cgu->clk_int_port[PORT_FGPI2] = 1;
+		cgu->clk_int_port[PORT_FGPI3] = 1;
+		cgu->clk_int_port[PORT_AI0] = 1;
+		cgu->clk_int_port[PORT_AI1] = 1;
+		break;
+
+	default:
+		dprintk(SAA716x_ERROR, 1, "Unknown port <%02x>", port);
+		delay = 0;
+		break;
+	}
+
+	/* wait for PLL */
+	if (delay)
+		msleep(1);
+
+	return 0;
+}
+
+int saa716x_set_clk_external(struct saa716x_dev *saa716x, u32 port)
+{
+	struct saa716x_cgu *cgu = &saa716x->cgu;
+
+	u8 delay = 1;
+
+	switch (port) {
+	case PORT_VI0_VIDEO:
+		cgu->clk_int_port[PORT_VI0_VIDEO] = 0;
+
+		if (!cgu->clk_int_port[PORT_VI0_VBI]) {
+			delay = 0;
+			break;
+		}
+
+		SAA716x_EPWR(CGU, CGU_FS1_5, 0x2); /* VI 0 clk */
+		SAA716x_EPWR(CGU, CGU_ESR_5, 0x0); /* disable divider */
+		break;
+
+	case PORT_VI0_VBI:
+		cgu->clk_int_port[PORT_VI0_VBI] = 0;
+
+		if (!cgu->clk_int_port[PORT_VI0_VIDEO]) {
+			delay = 0;
+			break;
+		}
+
+		SAA716x_EPWR(CGU, CGU_FS1_5, 0x2); /* VI 0 clk */
+		SAA716x_EPWR(CGU, CGU_ESR_5, 0x0); /* disable divider */
+		break;
+
+	case PORT_VI1_VIDEO:
+		cgu->clk_int_port[PORT_VI1_VIDEO] = 0;
+
+		if (!cgu->clk_int_port[PORT_VI1_VBI]) {
+			delay = 0;
+			break;
+		}
+
+		SAA716x_EPWR(CGU, CGU_FS1_6, 0x3); /* VI 1 clk */
+		SAA716x_EPWR(CGU, CGU_ESR_6, 0x0); /* disable divider */
+		break;
+
+	case PORT_VI1_VBI:
+		cgu->clk_int_port[PORT_VI1_VBI] = 0;
+
+		if (!cgu->clk_int_port[PORT_VI1_VIDEO]) {
+			delay = 0;
+			break;
+		}
+
+		SAA716x_EPWR(CGU, CGU_FS1_6, 0x3); /* VI 1 clk */
+		SAA716x_EPWR(CGU, CGU_ESR_6, 0x0); /* disable divider */
+		break;
+
+	case PORT_FGPI0:
+		cgu->clk_int_port[PORT_FGPI0] = 0;
+
+		SAA716x_EPWR(CGU, CGU_FS1_7, 0x4); /* FGPI 0 clk */
+		SAA716x_EPWR(CGU, CGU_ESR_7, 0x0); /* disable divider */
+		break;
+
+	case PORT_FGPI1:
+		cgu->clk_int_port[PORT_FGPI1] = 0;
+
+		SAA716x_EPWR(CGU, CGU_FS1_8, 0x5); /* FGPI 1 clk */
+		SAA716x_EPWR(CGU, CGU_ESR_8, 0x0); /* disable divider */
+		break;
+
+	case PORT_FGPI2:
+		cgu->clk_int_port[PORT_FGPI2] = 0;
+
+		SAA716x_EPWR(CGU, CGU_FS1_9, 0x6); /* FGPI 2 clk */
+		SAA716x_EPWR(CGU, CGU_ESR_9, 0x0); /* disable divider */
+		break;
+
+	case PORT_FGPI3:
+		cgu->clk_int_port[PORT_FGPI3] = 0;
+
+		SAA716x_EPWR(CGU, CGU_FS1_10, 0x7); /* FGPI 3 clk */
+		SAA716x_EPWR(CGU, CGU_ESR_10, 0x0); /* disable divider */
+		break;
+
+	case PORT_AI0:
+		cgu->clk_int_port[PORT_AI0] = 1;
+
+		SAA716x_EPWR(CGU, CGU_FS1_11, 0x8); /* AI 0 clk */
+		SAA716x_EPWR(CGU, CGU_ESR_11, 0x0); /* disable divider */
+		break;
+
+	case PORT_AI1:
+		cgu->clk_int_port[PORT_AI1] = 1;
+
+		SAA716x_EPWR(CGU, CGU_FS1_12, 0x9); /* AI 1 clk */
+		SAA716x_EPWR(CGU, CGU_ESR_12, 0x0); /* disable divider */
+		break;
+
+	default:
+		dprintk(SAA716x_ERROR, 1, "Unknown port <%02x>", port);
+		delay = 0;
+		break;
+
+	}
+
+	if (delay)
+		msleep(1);
+
+	return 0;
+}
+
+int saa716x_get_clk(struct saa716x_dev *saa716x,
+		    enum saa716x_clk_domain domain,
+		    u32 *frequency)
+{
+	struct saa716x_cgu *cgu = &saa716x->cgu;
+
+	switch (domain) {
+	case CLK_DOMAIN_PSS:
+	case CLK_DOMAIN_DCS:
+	case CLK_DOMAIN_SPI:
+	case CLK_DOMAIN_I2C:
+	case CLK_DOMAIN_PHI:
+	case CLK_DOMAIN_VI0:
+	case CLK_DOMAIN_VI1:
+	case CLK_DOMAIN_FGPI0:
+	case CLK_DOMAIN_FGPI1:
+	case CLK_DOMAIN_FGPI2:
+	case CLK_DOMAIN_FGPI3:
+	case CLK_DOMAIN_AI0:
+	case CLK_DOMAIN_AI1:
+	case CLK_DOMAIN_PHY:
+		*frequency = cgu->clk_freq[domain];
+		break;
+
+	case CLK_DOMAIN_VI0VBI:
+		*frequency = cgu->clk_freq[CLK_DOMAIN_VI0];
+		break;
+
+	case CLK_DOMAIN_VI1VBI:
+		*frequency =cgu->clk_freq[CLK_DOMAIN_VI1];
+		break;
+	default:
+		dprintk(SAA716x_ERROR, 1, "Error Clock domain <%02x>", domain);
+		break;
+	}
+
+	return 0;
+}
+
+int saa716x_set_clk(struct saa716x_dev *saa716x,
+		    enum saa716x_clk_domain domain,
+		    u32 frequency)
+{
+	struct saa716x_cgu *cgu = &saa716x->cgu;
+
+	u32 M = 1, N = 1, reset, i;
+	s8 N_tmp, M_tmp, sub, add, lsb;
+
+
+	if (cgu->clk_freq_min > frequency)
+		frequency = cgu->clk_freq_min;
+
+	if (cgu->clk_freq_max < frequency)
+		frequency = cgu->clk_freq_max;
+
+	switch (domain) {
+	case CLK_DOMAIN_PSS:
+	case CLK_DOMAIN_DCS:
+	case CLK_DOMAIN_SPI:
+	case CLK_DOMAIN_I2C:
+	case CLK_DOMAIN_PHI:
+	case CLK_DOMAIN_FGPI0:
+	case CLK_DOMAIN_FGPI1:
+	case CLK_DOMAIN_FGPI2:
+	case CLK_DOMAIN_FGPI3:
+	case CLK_DOMAIN_AI0:
+	case CLK_DOMAIN_AI1:
+	case CLK_DOMAIN_PHY:
+
+		if (frequency == cgu->clk_freq[domain])
+			return 0; /* same frequency */
+		break;
+
+	case CLK_DOMAIN_VI0:
+
+		if (frequency == cgu->clk_vi_0[1]) {
+			return 0;
+
+		} else if (frequency == cgu->clk_vi_0[0]) {
+			cgu->clk_vi_0[1] = frequency; /* store */
+
+			if (frequency == cgu->clk_vi_0[2])
+				return 0;
+
+		} else {
+			cgu->clk_vi_0[1] = frequency;
+
+			if (frequency != cgu->clk_vi_0[2])
+				return 0;
+
+		}
+		break;
+
+	case CLK_DOMAIN_VI1:
+		if (frequency == cgu->clk_vi_1[1]) {
+			return 0;
+
+		} else if (frequency == cgu->clk_vi_1[0]) {
+			cgu->clk_vi_1[1] = frequency; /* store */
+
+			if (frequency == cgu->clk_vi_1[2])
+				return 0;
+
+		} else {
+			cgu->clk_vi_1[1] = frequency;
+
+			if (frequency != cgu->clk_vi_1[2])
+				return 0;
+
+		}
+		break;
+
+	case CLK_DOMAIN_VI0VBI:
+		if (frequency == cgu->clk_vi_0[2]) {
+			return 0;
+
+		} else if (frequency == cgu->clk_vi_0[0]) {
+			cgu->clk_vi_0[2] = frequency; /* store */
+
+			if (frequency == cgu->clk_vi_0[1])
+				return 0;
+
+		} else {
+			cgu->clk_vi_0[2] = frequency; /* store */
+
+			if (frequency != cgu->clk_vi_0[1])
+				return 0;
+
+		}
+		domain = CLK_DOMAIN_VI0; /* change domain */
+		break;
+
+	case CLK_DOMAIN_VI1VBI:
+		if (frequency == cgu->clk_vi_1[2]) {
+			return 0;
+
+		} else if (frequency == cgu->clk_vi_1[0]) {
+			cgu->clk_vi_1[2] = frequency; /* store */
+
+			if (frequency == cgu->clk_vi_1[1])
+				return 0;
+
+		} else {
+			cgu->clk_vi_1[2] = frequency; /* store */
+
+			if (frequency != cgu->clk_vi_1[1])
+				return 0;
+
+		}
+		domain = CLK_DOMAIN_VI1; /* change domain */
+		break;
+	}
+
+	/* calculate divider */
+	do {
+		M = (N * PLL_FREQ) / frequency;
+		if (M == 0)
+			N++;
+
+	} while (M == 0);
+
+	/* calculate frequency */
+	cgu->clk_freq[domain] = (N * PLL_FREQ) / M;
+
+	N_tmp = N & 0xff;
+	M_tmp = M & 0xff;
+	sub = -N_tmp;
+	add = M_tmp - N_tmp;
+	lsb = 4; /* run */
+
+	if (((10 * N) / M) <= 5)
+		lsb |= 1; /* stretch */
+
+	/* store new divider */
+	cgu->clk_curr_div[domain] = sub & 0xff;
+	cgu->clk_curr_div[domain] <<= 8;
+	cgu->clk_curr_div[domain] |= add & 0xff;
+	cgu->clk_curr_div[domain] <<= 3;
+	cgu->clk_curr_div[domain] |= lsb;
+
+	dprintk(SAA716x_DEBUG, 1, "Domain <0x%02x> Frequency <%d> Set Freq <%d> N=%d M=%d Divider <0x%02x>",
+		domain,
+		frequency,
+		cgu->clk_freq[domain],
+		N,
+		M,
+		cgu->clk_curr_div[domain]);
+
+	reset = 0;
+
+	/* Reset */
+	SAA716x_EPWR(CGU, cgu_clk[domain], cgu->clk_curr_div[domain] | 0x2);
+
+	/* Reset disable */
+	for (i = 0; i < 1000; i++) {
+		udelay(10);
+		reset = SAA716x_EPRD(CGU, cgu_clk[domain]);
+
+		if (cgu->clk_curr_div[domain] == reset)
+			break;
+	}
+
+	if (cgu->clk_curr_div[domain] != reset)
+		SAA716x_EPWR(CGU, cgu_clk[domain], cgu->clk_curr_div[domain]);
+
+	return 0;
+}
+EXPORT_SYMBOL(saa716x_set_clk);
+
+int saa716x_cgu_init(struct saa716x_dev *saa716x)
+{
+	struct saa716x_cgu *cgu = &saa716x->cgu;
+
+	cgu->clk_freq_min = PLL_FREQ / 255;
+	if (PLL_FREQ > (cgu->clk_freq_min * 255))
+		cgu->clk_freq_min++;
+
+	cgu->clk_freq_max = PLL_FREQ;
+
+	saa716x_getbootscript_setup(saa716x);
+	saa716x_set_clk_internal(saa716x, PORT_ALL);
+
+	return 0;
+}
+EXPORT_SYMBOL(saa716x_cgu_init);
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_cgu.h linux/drivers/media/pci/saa716x/saa716x_cgu.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_cgu.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_cgu.h	2017-08-11 23:31:25.797389647 +0200
@@ -0,0 +1,63 @@
+#ifndef __SAA716x_CGU_H
+#define __SAA716x_CGU_H
+
+#define PLL_FREQ	2500
+
+#define SAA716x_CGU_CLKRUN(__reg)  do {						\
+	SAA716x_EPWR(CGU, CGU_PCR_##__reg, CGU_PCR_RUN); /* Run */		\
+	SAA716x_EPWR(CGU, CGU_SCR_##__reg, CGU_SCR_ENF1); /* Switch */		\
+	SAA716x_EPWR(CGU, CGU_FS1_##__reg, 0x00000000); /* PLL Clk */		\
+	SAA716x_EPWR(CGU, CGU_ESR_##__reg, CGU_ESR_FD_EN); /* Frac div */	\
+} while (0)
+
+enum saa716x_clk_domain {
+	CLK_DOMAIN_PSS		= 0,
+	CLK_DOMAIN_DCS		= 1,
+	CLK_DOMAIN_SPI		= 2,
+	CLK_DOMAIN_I2C		= 3,
+	CLK_DOMAIN_PHI		= 4,
+	CLK_DOMAIN_VI0		= 5,
+	CLK_DOMAIN_VI1		= 6,
+	CLK_DOMAIN_FGPI0	= 7,
+	CLK_DOMAIN_FGPI1	= 8,
+	CLK_DOMAIN_FGPI2	= 9,
+	CLK_DOMAIN_FGPI3	= 10,
+	CLK_DOMAIN_AI0		= 11,
+	CLK_DOMAIN_AI1		= 12,
+	CLK_DOMAIN_PHY		= 13,
+	CLK_DOMAIN_VI0VBI	= 14,
+	CLK_DOMAIN_VI1VBI	= 15
+};
+
+#define PORT_VI0_VIDEO		0
+#define PORT_VI0_VBI		2
+#define	PORT_VI1_VIDEO		3
+#define PORT_VI1_VBI		5
+#define PORT_FGPI0		6
+#define	PORT_FGPI1		7
+#define PORT_FGPI2		8
+#define PORT_FGPI3		9
+#define PORT_AI0		10
+#define PORT_AI1		11
+#define PORT_ALL		12
+
+#define CGU_CLKS	14
+
+struct saa716x_cgu {
+	u8	clk_int_port[12];
+	u32	clk_vi_0[3];
+	u32	clk_vi_1[3];
+	u32	clk_boot_div[CGU_CLKS];
+	u32	clk_curr_div[CGU_CLKS];
+	u32	clk_freq[CGU_CLKS];
+	u32	clk_freq_min;
+	u32	clk_freq_max;
+};
+
+extern int saa716x_cgu_init(struct saa716x_dev *saa716x);
+extern int saa716x_set_clk_internal(struct saa716x_dev *saa716x, u32 port);
+extern int saa716x_set_clk_external(struct saa716x_dev *saa716x, u32 port);
+extern int saa716x_set_clk(struct saa716x_dev *saa716x,
+			   enum saa716x_clk_domain domain, u32 frequency);
+
+#endif /* __SAA716x_CGU_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_cgu_reg.h linux/drivers/media/pci/saa716x/saa716x_cgu_reg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_cgu_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_cgu_reg.h	2017-08-11 23:31:25.797389647 +0200
@@ -0,0 +1,178 @@
+#ifndef __SAA716x_CGU_REG_H
+#define __SAA716x_CGU_REG_H
+
+/* -------------- CGU Registers -------------- */
+
+#define CGU_SCR_0			0x000
+#define CGU_SCR_1			0x004
+#define CGU_SCR_2			0x008
+#define CGU_SCR_3			0x00c
+#define CGU_SCR_4			0x010
+#define CGU_SCR_5			0x014
+#define CGU_SCR_6			0x018
+#define CGU_SCR_7			0x01c
+#define CGU_SCR_8			0x020
+#define CGU_SCR_9			0x024
+#define CGU_SCR_10			0x028
+#define CGU_SCR_11			0x02c
+#define CGU_SCR_12			0x030
+#define CGU_SCR_13			0x034
+#define CGU_SCR_STOP			(0x00000001 <<  3)
+#define CGU_SCR_RESET			(0x00000001 <<  2)
+#define CGU_SCR_ENF2			(0x00000001 <<  1)
+#define CGU_SCR_ENF1			(0x00000001 <<  0)
+
+#define CGU_FS1_0			0x038
+#define CGU_FS1_1			0x03c
+#define CGU_FS1_2			0x040
+#define CGU_FS1_3			0x044
+#define CGU_FS1_4			0x048
+#define CGU_FS1_5			0x04c
+#define CGU_FS1_6			0x050
+#define CGU_FS1_7			0x054
+#define CGU_FS1_8			0x058
+#define CGU_FS1_9			0x05c
+#define CGU_FS1_10			0x060
+#define CGU_FS1_11			0x064
+#define CGU_FS1_12			0x068
+#define CGU_FS1_13			0x06c
+#define CGU_FS1_PLL			(0x00000000 <<  0)
+
+
+#define CGU_FS2_0			0x070
+#define CGU_FS2_1			0x074
+#define CGU_FS2_2			0x078
+#define CGU_FS2_3			0x07c
+#define CGU_FS2_4			0x080
+#define CGU_FS2_5			0x084
+#define CGU_FS2_6			0x088
+#define CGU_FS2_7			0x08c
+#define CGU_FS2_8			0x090
+#define CGU_FS2_9			0x094
+#define CGU_FS2_10			0x098
+#define CGU_FS2_11			0x09c
+#define CGU_FS2_12			0x0a0
+#define CGU_FS2_13			0x0a4
+
+#define CGU_SSR_0			0x0a8
+#define CGU_SSR_1			0x0ac
+#define CGU_SSR_2			0x0b0
+#define CGU_SSR_3			0x0b4
+#define CGU_SSR_4			0x0b8
+#define CGU_SSR_5			0x0bc
+#define CGU_SSR_6			0x0c0
+#define CGU_SSR_7			0x0c4
+#define CGU_SSR_8			0x0c8
+#define CGU_SSR_9			0x0cc
+#define CGU_SSR_10			0x0d0
+#define CGU_SSR_11			0x0d4
+#define CGU_SSR_12			0x0d8
+#define CGU_SSR_13			0x0dc
+
+#define CGU_PCR_0_0			0x0e0
+#define CGU_PCR_0_1			0x0e4
+#define CGU_PCR_0_2			0x0e8
+#define CGU_PCR_0_3			0x0ec
+#define CGU_PCR_0_4			0x0f0
+#define CGU_PCR_0_5			0x0f4
+#define CGU_PCR_0_6			0x0f8
+#define CGU_PCR_0_7			0x0fc
+#define CGU_PCR_1_0			0x100
+#define CGU_PCR_1_1			0x104
+#define CGU_PCR_2_0			0x108
+#define CGU_PCR_2_1			0x10c
+#define CGU_PCR_3_0			0x110
+#define CGU_PCR_3_1			0x114
+#define CGU_PCR_3_2			0x118
+#define CGU_PCR_4_0			0x11c
+#define CGU_PCR_4_1			0x120
+#define CGU_PCR_5			0x124
+#define CGU_PCR_6			0x128
+#define CGU_PCR_7			0x12c
+#define CGU_PCR_8			0x130
+#define CGU_PCR_9			0x134
+#define CGU_PCR_10			0x138
+#define CGU_PCR_11			0x13c
+#define CGU_PCR_12			0x140
+#define CGU_PCR_13			0x144
+#define CGU_PCR_WAKE_EN			(0x00000001 <<  2)
+#define CGU_PCR_AUTO			(0x00000001 <<  1)
+#define CGU_PCR_RUN			(0x00000001 <<  0)
+
+
+#define CGU_PSR_0_0			0x148
+#define CGU_PSR_0_1			0x14c
+#define CGU_PSR_0_2			0x150
+#define CGU_PSR_0_3			0x154
+#define CGU_PSR_0_4			0x158
+#define CGU_PSR_0_5			0x15c
+#define CGU_PSR_0_6			0x160
+#define CGU_PSR_0_7			0x164
+#define CGU_PSR_1_0			0x168
+#define CGU_PSR_1_1			0x16c
+#define CGU_PSR_2_0			0x170
+#define CGU_PSR_2_1			0x174
+#define CGU_PSR_3_0			0x178
+#define CGU_PSR_3_1			0x17c
+#define CGU_PSR_3_2			0x180
+#define CGU_PSR_4_0			0x184
+#define CGU_PSR_4_1			0x188
+#define CGU_PSR_5			0x18c
+#define CGU_PSR_6			0x190
+#define CGU_PSR_7			0x194
+#define CGU_PSR_8			0x198
+#define CGU_PSR_9			0x19c
+#define CGU_PSR_10			0x1a0
+#define CGU_PSR_11			0x1a4
+#define CGU_PSR_12			0x1a8
+#define CGU_PSR_13			0x1ac
+
+#define CGU_ESR_0_0			0x1b0
+#define CGU_ESR_0_1			0x1b4
+#define CGU_ESR_0_2			0x1b8
+#define CGU_ESR_0_3			0x1bc
+#define CGU_ESR_0_4			0x1c0
+#define CGU_ESR_0_5			0x1c4
+#define CGU_ESR_0_6			0x1c8
+#define CGU_ESR_0_7			0x1cc
+#define CGU_ESR_1_0			0x1d0
+#define CGU_ESR_1_1			0x1d4
+#define CGU_ESR_2_0			0x1d8
+#define CGU_ESR_2_1			0x1dc
+#define CGU_ESR_3_0			0x1e0
+#define CGU_ESR_3_1			0x1e4
+#define CGU_ESR_3_2			0x1e8
+#define CGU_ESR_4_0			0x1ec
+#define CGU_ESR_4_1			0x1f0
+#define CGU_ESR_5			0x1f4
+#define CGU_ESR_6			0x1f8
+#define CGU_ESR_7			0x1fc
+#define CGU_ESR_8			0x200
+#define CGU_ESR_9			0x204
+#define CGU_ESR_10			0x208
+#define CGU_ESR_11			0x20c
+#define CGU_ESR_12			0x210
+#define CGU_ESR_13			0x214
+#define CGU_ESR_FD_EN			(0x00000001 <<  0)
+
+#define CGU_FDC_0			0x218
+#define CGU_FDC_1			0x21c
+#define CGU_FDC_2			0x220
+#define CGU_FDC_3			0x224
+#define CGU_FDC_4			0x228
+#define CGU_FDC_5			0x22c
+#define CGU_FDC_6			0x230
+#define CGU_FDC_7			0x234
+#define CGU_FDC_8			0x238
+#define CGU_FDC_9			0x23c
+#define CGU_FDC_10			0x240
+#define CGU_FDC_11			0x244
+#define CGU_FDC_12			0x248
+#define CGU_FDC_13			0x24c
+#define CGU_FDC_STRETCH			(0x00000001 <<  0)
+#define CGU_FDC_RESET			(0x00000001 <<  1)
+#define CGU_FDC_RUN1			(0x00000001 <<  2)
+#define CGU_FDC_MADD			(0x000000ff <<  3)
+#define CGU_FDC_MSUB			(0x000000ff << 11)
+
+#endif /* __SAA716x_CGU_REG_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_dcs_reg.h linux/drivers/media/pci/saa716x/saa716x_dcs_reg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_dcs_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_dcs_reg.h	2017-08-11 23:31:25.797389647 +0200
@@ -0,0 +1,56 @@
+#ifndef __SAA716x_DCS_REG_H
+#define __SAA716x_DCS_REG_H
+
+/* -------------- DCS Registers -------------- */
+
+#define DCSC_CTRL			0x000
+#define DCSC_SEL_PLLDI			(0x03ffffff <<  5)
+#define DCSC_TOUT_SEL			(0x0000000f <<  1)
+#define DCSC_TOUT_OFF			(0x00000001 <<  0)
+
+#define DCSC_ADDR			0x00c
+#define DCSC_ERR_TOUT_ADDR		(0x3fffffff <<  2)
+
+#define DCSC_STAT			0x010
+#define DCSC_ERR_TOUT_GNT		(0x0000001f << 24)
+#define DCSC_ERR_TOUT_SEL		(0x0000007f << 10)
+#define DCSC_ERR_TOUT_READ		(0x00000001 <<  8)
+#define DCSC_ERR_TOUT_MASK		(0x0000000f <<  4)
+#define DCSC_ERR_ACK			(0x00000001 <<  1)
+
+#define DCSC_FEATURES			0x040
+#define DCSC_UNIQUE_ID			(0x00000007 << 16)
+#define DCSC_SECURITY			(0x00000001 << 14)
+#define DCSC_NUM_BASE_REGS		(0x00000003 << 11)
+#define DCSC_NUM_TARGETS		(0x0000001f <<  5)
+#define DCSC_NUM_INITIATORS		(0x0000001f <<  0)
+
+#define DCSC_BASE_REG0			0x100
+#define DCSC_BASE_N_REG			(0x00000fff << 20)
+
+#define DCSC_INT_CLR_ENABLE		0xfd8
+#define DCSC_INT_CLR_ENABLE_TOUT	(0x00000001 <<  1)
+#define DCSC_INT_CLR_ENABLE_ERROR	(0x00000001 <<  0)
+
+#define DCSC_INT_SET_ENABLE		0xfdc
+#define DCSC_INT_SET_ENABLE_TOUT	(0x00000001 <<  1)
+#define DCSC_INT_SET_ENABLE_ERROR	(0x00000001 <<  0)
+
+#define DCSC_INT_STATUS			0xfe0
+#define DCSC_INT_STATUS_TOUT		(0x00000001 <<  1)
+#define DCSC_INT_STATUS_ERROR		(0x00000001 <<  0)
+
+#define DCSC_INT_ENABLE			0xfe4
+#define DCSC_INT_ENABLE_TOUT		(0x00000001 <<  1)
+#define DCSC_INT_ENABLE_ERROR		(0x00000001 <<  0)
+
+#define DCSC_INT_CLR_STATUS		0xfe8
+#define DCSC_INT_CLEAR_TOUT		(0x00000001 <<  1)
+#define DCSC_INT_CLEAR_ERROR		(0x00000001 <<  0)
+
+#define DCSC_INT_SET_STATUS		0xfec
+#define DCSC_INT_SET_TOUT		(0x00000001 <<  1)
+#define DCSC_INT_SET_ERROR		(0x00000001 <<  0)
+
+
+#endif /* __SAA716x_DCS_REG_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_dma.c linux/drivers/media/pci/saa716x/saa716x_dma.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_dma.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_dma.c	2017-08-11 23:31:25.797389647 +0200
@@ -0,0 +1,308 @@
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/scatterlist.h>
+#include <linux/vmalloc.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include "saa716x_dma.h"
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+
+/*  Allocates one page of memory, which is stores the data of one
+ *  716x page table. The result gets stored in the passed DMA buffer
+ *  structure.
+ */
+static int saa716x_allocate_ptable(struct saa716x_dmabuf *dmabuf)
+{
+	struct saa716x_dev *saa716x	= dmabuf->saa716x;
+	struct pci_dev *pdev		= saa716x->pdev;
+
+	dprintk(SAA716x_DEBUG, 1, "SG Page table allocate");
+	dmabuf->mem_ptab_virt = (void *) __get_free_page(GFP_KERNEL);
+
+	if (dmabuf->mem_ptab_virt == NULL) {
+		dprintk(SAA716x_ERROR, 1, "ERROR: Out of pages !");
+		return -ENOMEM;
+	}
+
+	dmabuf->mem_ptab_phys = dma_map_single(&pdev->dev,
+						dmabuf->mem_ptab_virt,
+						SAA716x_PAGE_SIZE,
+						DMA_TO_DEVICE);
+
+	if (dmabuf->mem_ptab_phys == 0) {
+		dprintk(SAA716x_ERROR, 1, "ERROR: map memory failed !");
+		return -ENOMEM;
+	}
+
+	BUG_ON(!(((unsigned long) dmabuf->mem_ptab_phys % SAA716x_PAGE_SIZE) == 0));
+
+	return 0;
+}
+
+static void saa716x_free_ptable(struct saa716x_dmabuf *dmabuf)
+{
+	struct saa716x_dev *saa716x	= dmabuf->saa716x;
+	struct pci_dev *pdev		= saa716x->pdev;
+
+	BUG_ON(dmabuf == NULL);
+	dprintk(SAA716x_DEBUG, 1, "SG Page table free");
+
+	/* free physical PCI memory */
+	if (dmabuf->mem_ptab_phys != 0) {
+		dma_unmap_single(&pdev->dev,
+				 dmabuf->mem_ptab_phys,
+				 SAA716x_PAGE_SIZE,
+				 DMA_TO_DEVICE);
+
+		dmabuf->mem_ptab_phys = 0;
+	}
+
+	/* free kernel memory */
+	if (dmabuf->mem_ptab_virt != NULL) {
+		free_page((unsigned long) dmabuf->mem_ptab_virt);
+		dmabuf->mem_ptab_virt = NULL;
+	}
+}
+
+static void saa716x_dmabuf_sgfree(struct saa716x_dmabuf *dmabuf)
+{
+	struct saa716x_dev *saa716x = dmabuf->saa716x;
+
+	BUG_ON(dmabuf == NULL);
+	dprintk(SAA716x_DEBUG, 1, "SG free");
+
+	dmabuf->mem_virt = NULL;
+	if (dmabuf->mem_virt_noalign != NULL) {
+		if (dmabuf->dma_type == SAA716x_DMABUF_INT)
+			vfree(dmabuf->mem_virt_noalign);
+
+		dmabuf->mem_virt_noalign = NULL;
+	}
+
+	if (dmabuf->sg_list != NULL) {
+		kfree(dmabuf->sg_list);
+		dmabuf->sg_list = NULL;
+	}
+}
+
+/*
+ * Create a SG, when an allocated buffer is passed to it,
+ * otherwise the needed memory gets allocated by itself
+ */
+static int saa716x_dmabuf_sgalloc(struct saa716x_dmabuf *dmabuf, void *buf, int size)
+{
+	struct saa716x_dev *saa716x	= dmabuf->saa716x;
+	struct scatterlist *list;
+	struct page *pg;
+
+	int i, pages;
+
+	BUG_ON(!(size > 0));
+	BUG_ON(dmabuf == NULL);
+	dprintk(SAA716x_DEBUG, 1, "SG allocate");
+
+	if ((size % SAA716x_PAGE_SIZE) != 0) /* calculate required pages */
+		pages = size / SAA716x_PAGE_SIZE + 1;
+	else
+		pages = size / SAA716x_PAGE_SIZE;
+
+	/* Allocate memory for SG list */
+	dmabuf->sg_list = kzalloc(sizeof (struct scatterlist) * pages, GFP_KERNEL);
+	if (dmabuf->sg_list == NULL) {
+		dprintk(SAA716x_ERROR, 1, "Failed to allocate memory for scatterlist.");
+		return -ENOMEM;
+	}
+
+	dprintk(SAA716x_DEBUG, 1, "Initializing SG table");
+	sg_init_table(dmabuf->sg_list, pages);
+
+	if (buf == NULL) {
+
+		/* allocate memory, unaligned */
+		dmabuf->mem_virt_noalign = vmalloc((pages + 1) * SAA716x_PAGE_SIZE);
+		if (dmabuf->mem_virt_noalign == NULL) {
+			dprintk(SAA716x_ERROR, 1, "Failed to allocate memory for buffer");
+			return -ENOMEM;
+		}
+		memset(dmabuf->mem_virt_noalign, 0, (pages + 1) * SAA716x_PAGE_SIZE);
+
+		/* align memory to page */
+		dmabuf->mem_virt = (void *) PAGE_ALIGN (((unsigned long) dmabuf->mem_virt_noalign));
+
+		BUG_ON(!((((unsigned long) dmabuf->mem_virt) % SAA716x_PAGE_SIZE) == 0));
+	} else {
+		dmabuf->mem_virt = buf;
+	}
+
+	dmabuf->list_len = pages; /* scatterlist length */
+	list = dmabuf->sg_list;
+
+	dprintk(SAA716x_DEBUG, 1, "Allocating SG pages");
+	for (i = 0; i < pages; i++) {
+		if (buf == NULL)
+			pg = vmalloc_to_page(dmabuf->mem_virt + i * SAA716x_PAGE_SIZE);
+		else
+			pg = virt_to_page(dmabuf->mem_virt + i * SAA716x_PAGE_SIZE);
+
+		BUG_ON(pg == NULL);
+		sg_set_page(&list[i], pg, SAA716x_PAGE_SIZE, 0);
+	}
+
+	dprintk(SAA716x_DEBUG, 1, "Done!");
+	return 0;
+}
+
+/*  Fill the "page table" page with the pointers to the specified SG buffer */
+static void saa716x_dmabuf_sgpagefill(struct saa716x_dmabuf *dmabuf, struct scatterlist *sg_list, int pages, int offset)
+{
+	struct saa716x_dev *saa716x	= dmabuf->saa716x;
+	struct pci_dev *pdev		= saa716x->pdev;
+	struct scatterlist *sg_cur;
+
+	u32 *page;
+	int i, j, k = 0;
+	dma_addr_t addr = 0;
+
+	BUG_ON(dmabuf == NULL);
+	BUG_ON(sg_list == NULL);
+	BUG_ON(pages == 0);
+	dprintk(SAA716x_DEBUG, 1, "SG page fill");
+
+	/* make page writable for the PC */
+	dma_sync_single_for_cpu(&pdev->dev, dmabuf->mem_ptab_phys, SAA716x_PAGE_SIZE, DMA_TO_DEVICE);
+	page = dmabuf->mem_ptab_virt;
+
+	/* create page table */
+	for (i = 0; i < pages; i++) {
+		sg_cur = &sg_list[i];
+		BUG_ON(!(((sg_cur->length + sg_cur->offset) % SAA716x_PAGE_SIZE) == 0));
+
+		if (i == 0)
+			dmabuf->offset = (sg_cur->length + sg_cur->offset) % SAA716x_PAGE_SIZE;
+		else
+			BUG_ON(sg_cur->offset != 0);
+
+		for (j = 0; (j * SAA716x_PAGE_SIZE) < sg_dma_len(sg_cur); j++) {
+
+			if ((offset + sg_cur->offset) >= SAA716x_PAGE_SIZE) {
+				offset -= SAA716x_PAGE_SIZE;
+				continue;
+			}
+
+			addr = ((u64)sg_dma_address(sg_cur)) + (j * SAA716x_PAGE_SIZE) - sg_cur->offset;
+
+			BUG_ON(addr == 0);
+			page[k * 2] = (u32 )addr; /* Low */
+			page[k * 2 + 1] = (u32 )(((u64) addr) >> 32); /* High */
+			BUG_ON(page[k * 2] % SAA716x_PAGE_SIZE);
+			k++;
+		}
+	}
+
+	for (; k < (SAA716x_PAGE_SIZE / 8); k++) {
+		page[k * 2] = (u32 ) addr;
+		page[k * 2 + 1] = (u32 ) (((u64 ) addr) >> 32);
+	}
+
+	/* make "page table" page writable for the PC */
+	dma_sync_single_for_device(&pdev->dev,
+				   dmabuf->mem_ptab_phys,
+				   SAA716x_PAGE_SIZE,
+				   DMA_TO_DEVICE);
+
+}
+
+void saa716x_dmabufsync_dev(struct saa716x_dmabuf *dmabuf)
+{
+	struct saa716x_dev *saa716x	= dmabuf->saa716x;
+	struct pci_dev *pdev		= saa716x->pdev;
+
+	dprintk(SAA716x_DEBUG, 1, "DMABUF sync DEVICE");
+	BUG_ON(dmabuf->sg_list == NULL);
+
+	dma_sync_sg_for_device(&pdev->dev,
+			       dmabuf->sg_list,
+			       dmabuf->list_len,
+			       DMA_FROM_DEVICE);
+
+}
+
+void saa716x_dmabufsync_cpu(struct saa716x_dmabuf *dmabuf)
+{
+	struct saa716x_dev *saa716x	= dmabuf->saa716x;
+	struct pci_dev *pdev		= saa716x->pdev;
+
+	dprintk(SAA716x_DEBUG, 1, "DMABUF sync CPU");
+	BUG_ON(dmabuf->sg_list == NULL);
+
+	dma_sync_sg_for_cpu(&pdev->dev,
+			    dmabuf->sg_list,
+			    dmabuf->list_len,
+			    DMA_FROM_DEVICE);
+}
+
+/* Allocates a DMA buffer for the specified external linear buffer. */
+int saa716x_dmabuf_alloc(struct saa716x_dev *saa716x, struct saa716x_dmabuf *dmabuf, int size)
+{
+	struct pci_dev *pdev		= saa716x->pdev;
+
+	int ret;
+
+	BUG_ON(saa716x == NULL);
+	BUG_ON(dmabuf == NULL);
+	BUG_ON(! (size > 0));
+
+	dmabuf->dma_type		= SAA716x_DMABUF_INT;
+
+	dmabuf->mem_virt_noalign	= NULL;
+	dmabuf->mem_virt		= NULL;
+	dmabuf->mem_ptab_phys		= 0;
+	dmabuf->mem_ptab_virt		= NULL;
+
+	dmabuf->list_len		= 0;
+	dmabuf->saa716x			= saa716x;
+
+	/* Allocate page table */
+	ret = saa716x_allocate_ptable(dmabuf);
+	if (ret < 0) {
+		dprintk(SAA716x_ERROR, 1, "PT alloc failed, Out of memory");
+		goto err1;
+	}
+
+	/* Allocate buffer as SG */
+	ret = saa716x_dmabuf_sgalloc(dmabuf, NULL, size);
+	if (ret < 0) {
+		dprintk(SAA716x_ERROR, 1, "SG alloc failed");
+		goto err2;
+	}
+
+	ret = dma_map_sg(&pdev->dev, dmabuf->sg_list, dmabuf->list_len, DMA_FROM_DEVICE);
+	if (ret < 0) {
+		dprintk(SAA716x_ERROR, 1, "SG map failed");
+		goto err3;
+	}
+
+	saa716x_dmabuf_sgpagefill(dmabuf, dmabuf->sg_list, ret, 0);
+
+	return 0;
+err3:
+	saa716x_dmabuf_sgfree(dmabuf);
+err2:
+	saa716x_free_ptable(dmabuf);
+err1:
+	return ret;
+}
+
+void saa716x_dmabuf_free(struct saa716x_dev *saa716x, struct saa716x_dmabuf *dmabuf)
+{
+	struct pci_dev *pdev		= saa716x->pdev;
+
+	BUG_ON(saa716x == NULL);
+	BUG_ON(dmabuf == NULL);
+
+	dma_unmap_sg(&pdev->dev, dmabuf->sg_list, dmabuf->list_len, DMA_FROM_DEVICE);
+	saa716x_dmabuf_sgfree(dmabuf);
+	saa716x_free_ptable(dmabuf);
+}
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_dma.h linux/drivers/media/pci/saa716x/saa716x_dma.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_dma.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_dma.h	2017-08-11 23:31:25.797389647 +0200
@@ -0,0 +1,61 @@
+#ifndef __SAA716x_DMA_H
+#define __SAA716x_DMA_H
+
+#define SAA716x_PAGE_SIZE	4096
+
+#define PTA_LSB(__mem)		((u32 ) (__mem))
+#define PTA_MSB(__mem)		((u32 ) ((u64)(__mem) >> 32))
+
+#define BAM_DMA_BUF_MODE_BASE		0x00
+#define BAM_DMA_BUF_MODE_OFFSET		0x24
+
+#define BAM_DMA_BUF_MODE(__ch)		(BAM_DMA_BUF_MODE_BASE + (BAM_DMA_BUF_MODE_OFFSET * __ch))
+
+#define BAM_ADDR_OFFSET_BASE		0x04
+#define BAM_ADDR_OFFSET_OFFSET		0x24
+
+#define BAM_ADDR_OFFSET(__ch)		(BAM_ADDR_OFFSET_BASE + (BAM_ADDR_OFFSET_OFFSET * __ch))
+
+#define BAM_ADDR_OFFSET_0(__ch)		(BAM_ADDR_OFFSET(__ch) + 0x00)
+#define BAM_ADDR_OFFSET_1(__ch)		(BAM_ADDR_OFFSET(__ch) + 0x04)
+#define BAM_ADDR_OFFSET_2(__ch)		(BAM_ADDR_OFFSET(__ch) + 0x08)
+#define BAM_ADDR_OFFSET_3(__ch)		(BAM_ADDR_OFFSET(__ch) + 0x0c)
+#define BAM_ADDR_OFFSET_4(__ch)		(BAM_ADDR_OFFSET(__ch) + 0x10)
+#define BAM_ADDR_OFFSET_5(__ch)		(BAM_ADDR_OFFSET(__ch) + 0x14)
+#define BAM_ADDR_OFFSET_6(__ch)		(BAM_ADDR_OFFSET(__ch) + 0x18)
+#define BAM_ADDR_OFFSET_7(__ch)		(BAM_ADDR_OFFSET(__ch) + 0x1c)
+
+
+enum saa716x_dma_type {
+	SAA716x_DMABUF_EXT_LIN, /* Linear external */
+	SAA716x_DMABUF_EXT_SG, /* SG external */
+	SAA716x_DMABUF_INT /* Linear internal */
+};
+
+struct saa716x_dev;
+
+struct saa716x_dmabuf {
+	enum saa716x_dma_type	dma_type;
+
+	void			*mem_virt_noalign;
+	void			*mem_virt; /* page aligned */
+	dma_addr_t		mem_ptab_phys;
+	void			*mem_ptab_virt;
+	void			*sg_list; /* SG list */
+
+	struct saa716x_dev	*saa716x;
+
+	int			list_len; /* buffer len */
+	int			offset; /* page offset */
+};
+
+extern int saa716x_dmabuf_alloc(struct saa716x_dev *saa716x,
+				struct saa716x_dmabuf *dmabuf,
+				int size);
+extern void saa716x_dmabuf_free(struct saa716x_dev *saa716x,
+				struct saa716x_dmabuf *dmabuf);
+
+extern void saa716x_dmabufsync_dev(struct saa716x_dmabuf *dmabuf);
+extern void saa716x_dmabufsync_cpu(struct saa716x_dmabuf *dmabuf);
+
+#endif /* __SAA716x_DMA_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_dma_reg.h linux/drivers/media/pci/saa716x/saa716x_dma_reg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_dma_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_dma_reg.h	2017-08-11 23:31:25.809389580 +0200
@@ -0,0 +1,201 @@
+#ifndef __SAA716x_DMA_REG_H
+#define __SAA716x_DMA_REG_H
+
+/* -------------- BAM Registers -------------- */
+
+#define BAM_VI0_0_DMA_BUF_MODE		0x000
+
+#define BAM_VI0_0_ADDR_OFFST_0		0x004
+#define BAM_VI0_0_ADDR_OFFST_1		0x008
+#define BAM_VI0_0_ADDR_OFFST_2		0x00c
+#define BAM_VI0_0_ADDR_OFFST_3		0x010
+#define BAM_VI0_0_ADDR_OFFST_4		0x014
+#define BAM_VI0_0_ADDR_OFFST_5		0x018
+#define BAM_VI0_0_ADDR_OFFST_6		0x01c
+#define BAM_VI0_0_ADDR_OFFST_7		0x020
+
+#define BAM_VI0_1_DMA_BUF_MODE		0x024
+#define BAM_VI0_1_ADDR_OFFST_0		0x028
+#define BAM_VI0_1_ADDR_OFFST_1		0x02c
+#define BAM_VI0_1_ADDR_OFFST_2		0x030
+#define BAM_VI0_1_ADDR_OFFST_3		0x034
+#define BAM_VI0_1_ADDR_OFFST_4		0x038
+#define BAM_VI0_1_ADDR_OFFST_5		0x03c
+#define BAM_VI0_1_ADDR_OFFST_6		0x040
+#define BAM_VI0_1_ADDR_OFFST_7		0x044
+
+#define BAM_VI0_2_DMA_BUF_MODE		0x048
+#define BAM_VI0_2_ADDR_OFFST_0		0x04c
+#define BAM_VI0_2_ADDR_OFFST_1		0x050
+#define BAM_VI0_2_ADDR_OFFST_2		0x054
+#define BAM_VI0_2_ADDR_OFFST_3		0x058
+#define BAM_VI0_2_ADDR_OFFST_4		0x05c
+#define BAM_VI0_2_ADDR_OFFST_5		0x060
+#define BAM_VI0_2_ADDR_OFFST_6		0x064
+#define BAM_VI0_2_ADDR_OFFST_7		0x068
+
+
+#define BAM_VI1_0_DMA_BUF_MODE		0x06c
+#define BAM_VI1_0_ADDR_OFFST_0		0x070
+#define BAM_VI1_0_ADDR_OFFST_1		0x074
+#define BAM_VI1_0_ADDR_OFFST_2		0x078
+#define BAM_VI1_0_ADDR_OFFST_3		0x07c
+#define BAM_VI1_0_ADDR_OFFST_4		0x080
+#define BAM_VI1_0_ADDR_OFFST_5		0x084
+#define BAM_VI1_0_ADDR_OFFST_6		0x088
+#define BAM_VI1_0_ADDR_OFFST_7		0x08c
+
+#define BAM_VI1_1_DMA_BUF_MODE		0x090
+#define BAM_VI1_1_ADDR_OFFST_0		0x094
+#define BAM_VI1_1_ADDR_OFFST_1		0x098
+#define BAM_VI1_1_ADDR_OFFST_2		0x09c
+#define BAM_VI1_1_ADDR_OFFST_3		0x0a0
+#define BAM_VI1_1_ADDR_OFFST_4		0x0a4
+#define BAM_VI1_1_ADDR_OFFST_5		0x0a8
+#define BAM_VI1_1_ADDR_OFFST_6		0x0ac
+#define BAM_VI1_1_ADDR_OFFST_7		0x0b0
+
+#define BAM_VI1_2_DMA_BUF_MODE		0x0b4
+#define BAM_VI1_2_ADDR_OFFST_0		0x0b8
+#define BAM_VI1_2_ADDR_OFFST_1		0x0bc
+#define BAM_VI1_2_ADDR_OFFST_2		0x0c0
+#define BAM_VI1_2_ADDR_OFFST_3		0x0c4
+#define BAM_VI1_2_ADDR_OFFST_4		0x0c8
+#define BAM_VI1_2_ADDR_OFFST_5		0x0cc
+#define BAM_VI1_2_ADDR_OFFST_6		0x0d0
+#define BAM_VI1_2_ADDR_OFFST_7		0x0d4
+
+
+#define BAM_FGPI0_DMA_BUF_MODE		0x0d8
+#define BAM_FGPI0_ADDR_OFFST_0		0x0dc
+#define BAM_FGPI0_ADDR_OFFST_1		0x0e0
+#define BAM_FGPI0_ADDR_OFFST_2		0x0e4
+#define BAM_FGPI0_ADDR_OFFST_3		0x0e8
+#define BAM_FGPI0_ADDR_OFFST_4		0x0ec
+#define BAM_FGPI0_ADDR_OFFST_5		0x0f0
+#define BAM_FGPI0_ADDR_OFFST_6		0x0f4
+#define BAM_FGPI0_ADDR_OFFST_7		0x0f8
+
+#define BAM_FGPI1_DMA_BUF_MODE		0x0fc
+#define BAM_FGPI1_ADDR_OFFST_0		0x100
+#define BAM_FGPI1_ADDR_OFFST_1		0x104
+#define BAM_FGPI1_ADDR_OFFST_2		0x108
+#define BAM_FGPI1_ADDR_OFFST_3		0x10c
+#define BAM_FGPI1_ADDR_OFFST_4		0x110
+#define BAM_FGPI1_ADDR_OFFST_5		0x114
+#define BAM_FGPI1_ADDR_OFFST_6		0x118
+#define BAM_FGPI1_ADDR_OFFST_7		0x11c
+
+#define BAM_FGPI2_DMA_BUF_MODE		0x120
+#define BAM_FGPI2_ADDR_OFFST_0		0x124
+#define BAM_FGPI2_ADDR_OFFST_1		0x128
+#define BAM_FGPI2_ADDR_OFFST_2		0x12c
+#define BAM_FGPI2_ADDR_OFFST_3		0x130
+#define BAM_FGPI2_ADDR_OFFST_4		0x134
+#define BAM_FGPI2_ADDR_OFFST_5		0x138
+#define BAM_FGPI2_ADDR_OFFST_6		0x13c
+#define BAM_FGPI2_ADDR_OFFST_7		0x140
+
+#define BAM_FGPI3_DMA_BUF_MODE		0x144
+#define BAM_FGPI3_ADDR_OFFST_0		0x148
+#define BAM_FGPI3_ADDR_OFFST_1		0x14c
+#define BAM_FGPI3_ADDR_OFFST_2		0x150
+#define BAM_FGPI3_ADDR_OFFST_3		0x154
+#define BAM_FGPI3_ADDR_OFFST_4		0x158
+#define BAM_FGPI3_ADDR_OFFST_5		0x15c
+#define BAM_FGPI3_ADDR_OFFST_6		0x160
+#define BAM_FGPI3_ADDR_OFFST_7		0x164
+
+
+#define BAM_AI0_DMA_BUF_MODE		0x168
+#define BAM_AI0_ADDR_OFFST_0		0x16c
+#define BAM_AI0_ADDR_OFFST_1		0x170
+#define BAM_AI0_ADDR_OFFST_2		0x174
+#define BAM_AI0_ADDR_OFFST_3		0x178
+#define BAM_AI0_ADDR_OFFST_4		0x17c
+#define BAM_AIO_ADDR_OFFST_5		0x180
+#define BAM_AI0_ADDR_OFFST_6		0x184
+#define BAM_AIO_ADDR_OFFST_7		0x188
+
+#define BAM_AI1_DMA_BUF_MODE		0x18c
+#define BAM_AI1_ADDR_OFFST_0		0x190
+#define BAM_AI1_ADDR_OFFST_1		0x194
+#define BAM_AI1_ADDR_OFFST_2		0x198
+#define BAM_AI1_ADDR_OFFST_3		0x19c
+#define BAM_AI1_ADDR_OFFST_4		0x1a0
+#define BAM_AI1_ADDR_OFFST_5		0x1a4
+#define BAM_AI1_ADDR_OFFST_6		0x1a8
+#define BAM_AI1_ADDR_OFFST_7		0x1ac
+
+#define BAM_SW_RST			0xff0
+#define BAM_SW_RESET			(0x00000001 <<  0)
+
+
+
+
+
+/* -------------- MMU Registers -------------- */
+
+#define MMU_MODE			0x000
+
+#define MMU_DMA_CONFIG0			0x004
+#define MMU_DMA_CONFIG1			0x008
+#define MMU_DMA_CONFIG2			0x00c
+#define MMU_DMA_CONFIG3			0x010
+#define MMU_DMA_CONFIG4			0x014
+#define MMU_DMA_CONFIG5			0x018
+#define MMU_DMA_CONFIG6			0x01c
+#define MMU_DMA_CONFIG7			0x020
+#define MMU_DMA_CONFIG8			0x024
+#define MMU_DMA_CONFIG9			0x028
+#define MMU_DMA_CONFIG10		0x02c
+#define MMU_DMA_CONFIG11		0x030
+#define MMU_DMA_CONFIG12		0x034
+#define MMU_DMA_CONFIG13		0x038
+#define MMU_DMA_CONFIG14		0x03c
+#define MMU_DMA_CONFIG15		0x040
+
+#define MMU_DMA_CONFIG(__ch)		(MMU_DMA_CONFIG0 + (4 * __ch))
+
+#define MMU_SW_RST			0xff0
+#define MMU_SW_RESET			(0x0001 <<  0)
+
+#define MMU_PTA_BASE0			0x044 /* DMA 0 */
+#define MMU_PTA_BASE1			0x084 /* DMA 1 */
+#define MMU_PTA_BASE2			0x0c4 /* DMA 2 */
+#define MMU_PTA_BASE3			0x104 /* DMA 3 */
+#define MMU_PTA_BASE4			0x144 /* DMA 4 */
+#define MMU_PTA_BASE5			0x184 /* DMA 5 */
+#define MMU_PTA_BASE6			0x1c4 /* DMA 6 */
+#define MMU_PTA_BASE7			0x204 /* DMA 7 */
+#define MMU_PTA_BASE8			0x244 /* DMA 8 */
+#define MMU_PTA_BASE9			0x284 /* DMA 9 */
+#define MMU_PTA_BASE10			0x2c4 /* DMA 10 */
+#define MMU_PTA_BASE11			0x304 /* DMA 11 */
+#define MMU_PTA_BASE12			0x344 /* DMA 12 */
+#define MMU_PTA_BASE13			0x384 /* DMA 13 */
+#define MMU_PTA_BASE14			0x3c4 /* DMA 14 */
+#define MMU_PTA_BASE15			0x404 /* DMA 15 */
+
+#define MMU_PTA_OFFSET			0x40
+
+#define MMU_PTA_BASE(__ch)		(MMU_PTA_BASE0 + (MMU_PTA_OFFSET * __ch))
+
+#define MMU_PTA0_LSB(__ch)		MMU_PTA_BASE(__ch) + 0x00
+#define MMU_PTA0_MSB(__ch)		MMU_PTA_BASE(__ch) + 0x04
+#define MMU_PTA1_LSB(__ch)		MMU_PTA_BASE(__ch) + 0x08
+#define MMU_PTA1_MSB(__ch)		MMU_PTA_BASE(__ch) + 0x0c
+#define MMU_PTA2_LSB(__ch)		MMU_PTA_BASE(__ch) + 0x10
+#define MMU_PTA2_MSB(__ch)		MMU_PTA_BASE(__ch) + 0x14
+#define MMU_PTA3_LSB(__ch)		MMU_PTA_BASE(__ch) + 0x18
+#define MMU_PTA3_MSB(__ch)		MMU_PTA_BASE(__ch) + 0x1c
+#define MMU_PTA4_LSB(__ch)		MMU_PTA_BASE(__ch) + 0x20
+#define MMU_PTA4_MSB(__ch)		MMU_PTA_BASE(__ch) + 0x24
+#define MMU_PTA5_LSB(__ch)		MMU_PTA_BASE(__ch) + 0x28
+#define MMU_PTA5_MSB(__ch)		MMU_PTA_BASE(__ch) + 0x2c
+#define MMU_PTA6_LSB(__ch)		MMU_PTA_BASE(__ch) + 0x30
+#define MMU_PTA6_MSB(__ch)		MMU_PTA_BASE(__ch) + 0x34
+#define MMU_PTA7_LSB(__ch)		MMU_PTA_BASE(__ch) + 0x38
+#define MMU_PTA7_MSB(__ch)		MMU_PTA_BASE(__ch) + 0x3c
+
+#endif /* __SAA716x_DMA_REG_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_cmd.c linux/drivers/media/pci/saa716x/saa716x_ff_cmd.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_cmd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_ff_cmd.c	2017-08-11 23:31:25.813389558 +0200
@@ -0,0 +1,433 @@
+#include <linux/types.h>
+
+#include <linux/dvb/video.h>
+#include <linux/dvb/osd.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_priv.h"
+#include "saa716x_ff.h"
+#include "saa716x_ff_cmd.h"
+#include "saa716x_ff_phi.h"
+
+
+int sti7109_cmd_init(struct sti7109_dev *sti7109)
+{
+	mutex_init(&sti7109->cmd_lock);
+	mutex_init(&sti7109->osd_cmd_lock);
+	mutex_init(&sti7109->data_lock);
+
+	init_waitqueue_head(&sti7109->boot_finish_wq);
+	sti7109->boot_finished = 0;
+
+	init_waitqueue_head(&sti7109->cmd_ready_wq);
+	sti7109->cmd_ready = 0;
+
+	init_waitqueue_head(&sti7109->result_avail_wq);
+	sti7109->result_avail = 0;
+
+	init_waitqueue_head(&sti7109->osd_cmd_ready_wq);
+	sti7109->osd_cmd_ready = 0;
+	init_waitqueue_head(&sti7109->osd_result_avail_wq);
+	sti7109->osd_result_avail = 0;
+
+	sti7109->data_handle = 0;
+	sti7109->data_buffer = (u8 *) (sti7109->iobuf + TSOUT_LEN);
+	init_waitqueue_head(&sti7109->data_ready_wq);
+	sti7109->data_ready = 0;
+	init_waitqueue_head(&sti7109->block_done_wq);
+	sti7109->block_done = 0;
+	return 0;
+}
+
+static int sti7109_do_raw_cmd(struct sti7109_dev * sti7109)
+{
+	struct saa716x_dev * saa716x = sti7109->dev;
+	unsigned long timeout;
+
+	timeout = 1 * HZ;
+	timeout = wait_event_interruptible_timeout(sti7109->cmd_ready_wq,
+						   sti7109->cmd_ready == 1,
+						   timeout);
+
+	if (timeout == -ERESTARTSYS || sti7109->cmd_ready == 0) {
+		if (timeout == -ERESTARTSYS) {
+			/* a signal arrived */
+			dprintk(SAA716x_ERROR, 1, "cmd ERESTARTSYS");
+			return -ERESTARTSYS;
+		}
+		dprintk(SAA716x_ERROR, 1,
+			"timed out waiting for command ready");
+	}
+
+	sti7109->cmd_ready = 0;
+	sti7109->result_len = 0;
+	sti7109->result_avail = 0;
+	saa716x_ff_phi_write(saa716x, ADDR_CMD_DATA, sti7109->cmd_data,
+			     sti7109->cmd_len);
+	SAA716x_EPWR(PHI_1, FPGA_ADDR_PHI_ISET, ISR_CMD_MASK);
+
+	if (sti7109->result_max_len > 0) {
+		timeout = 1 * HZ;
+		timeout = wait_event_interruptible_timeout(
+				sti7109->result_avail_wq,
+				sti7109->result_avail == 1,
+				timeout);
+
+		if (timeout == -ERESTARTSYS || sti7109->result_avail == 0) {
+			sti7109->result_len = 0;
+			if (timeout == -ERESTARTSYS) {
+				/* a signal arrived */
+				dprintk(SAA716x_ERROR, 1, "result ERESTARTSYS");
+				return -ERESTARTSYS;
+			}
+			dprintk(SAA716x_ERROR, 1,
+				"timed out waiting for command result");
+			return -EIO;
+		}
+
+		if (sti7109->result_len > sti7109->result_max_len) {
+			sti7109->result_len = sti7109->result_max_len;
+			dprintk(SAA716x_NOTICE, 1,
+				"not enough space in result buffer");
+		}
+	}
+
+	return 0;
+}
+
+int sti7109_raw_cmd(struct sti7109_dev * sti7109, osd_raw_cmd_t * cmd)
+{
+	struct saa716x_dev * saa716x = sti7109->dev;
+	int err;
+
+	if (cmd->cmd_len > SIZE_CMD_DATA) {
+		dprintk(SAA716x_ERROR, 1, "command too long");
+		return -EFAULT;
+	}
+
+	mutex_lock(&sti7109->cmd_lock);
+
+	err = -EFAULT;
+	if (copy_from_user(sti7109->cmd_data, (void __user *)cmd->cmd_data,
+			   cmd->cmd_len))
+		goto out;
+
+	sti7109->cmd_len = cmd->cmd_len;
+	sti7109->result_max_len = cmd->result_len;
+
+	err = sti7109_do_raw_cmd(sti7109);
+	if (err)
+		goto out;
+
+	cmd->result_len = sti7109->result_len;
+	if (sti7109->result_len > 0) {
+		if (copy_to_user((void __user *)cmd->result_data,
+				 sti7109->result_data,
+				 sti7109->result_len))
+			err = -EFAULT;
+	}
+
+out:
+	mutex_unlock(&sti7109->cmd_lock);
+	return err;
+}
+
+static int sti7109_do_raw_osd_cmd(struct sti7109_dev * sti7109)
+{
+	struct saa716x_dev * saa716x = sti7109->dev;
+	unsigned long timeout;
+
+	timeout = 1 * HZ;
+	timeout = wait_event_interruptible_timeout(sti7109->osd_cmd_ready_wq,
+						   sti7109->osd_cmd_ready == 1,
+						   timeout);
+
+	if (timeout == -ERESTARTSYS || sti7109->osd_cmd_ready == 0) {
+		if (timeout == -ERESTARTSYS) {
+			/* a signal arrived */
+			dprintk(SAA716x_ERROR, 1, "osd cmd ERESTARTSYS");
+			return -ERESTARTSYS;
+		}
+		dprintk(SAA716x_ERROR, 1,
+			"timed out waiting for osd command ready");
+	}
+
+	sti7109->osd_cmd_ready = 0;
+	sti7109->osd_result_len = 0;
+	sti7109->osd_result_avail = 0;
+	saa716x_ff_phi_write(saa716x, ADDR_OSD_CMD_DATA, sti7109->osd_cmd_data,
+			     sti7109->osd_cmd_len);
+	SAA716x_EPWR(PHI_1, FPGA_ADDR_PHI_ISET, ISR_OSD_CMD_MASK);
+
+	if (sti7109->osd_result_max_len > 0) {
+		timeout = 1 * HZ;
+		timeout = wait_event_interruptible_timeout(
+				sti7109->osd_result_avail_wq,
+				sti7109->osd_result_avail == 1,
+				timeout);
+
+		if (timeout == -ERESTARTSYS || sti7109->osd_result_avail == 0) {
+			sti7109->osd_result_len = 0;
+			if (timeout == -ERESTARTSYS) {
+				/* a signal arrived */
+				dprintk(SAA716x_ERROR, 1,
+					"osd result ERESTARTSYS");
+				return -ERESTARTSYS;
+			}
+			dprintk(SAA716x_ERROR, 1,
+				"timed out waiting for osd command result");
+			return -EIO;
+		}
+
+		if (sti7109->osd_result_len > sti7109->osd_result_max_len) {
+			sti7109->osd_result_len = sti7109->osd_result_max_len;
+			dprintk(SAA716x_NOTICE, 1,
+				"not enough space in result buffer");
+		}
+	}
+
+	return 0;
+}
+
+int sti7109_raw_osd_cmd(struct sti7109_dev * sti7109, osd_raw_cmd_t * cmd)
+{
+	struct saa716x_dev * saa716x = sti7109->dev;
+	int err;
+
+	if (cmd->cmd_len > SIZE_OSD_CMD_DATA) {
+		dprintk(SAA716x_ERROR, 1, "command too long");
+		return -EFAULT;
+	}
+
+	mutex_lock(&sti7109->osd_cmd_lock);
+
+	err = -EFAULT;
+	if (copy_from_user(sti7109->osd_cmd_data, (void __user *)cmd->cmd_data,
+			   cmd->cmd_len))
+		goto out;
+
+	sti7109->osd_cmd_len = cmd->cmd_len;
+	sti7109->osd_result_max_len = cmd->result_len;
+
+	err = sti7109_do_raw_osd_cmd(sti7109);
+	if (err)
+		goto out;
+
+	cmd->result_len = sti7109->osd_result_len;
+	if (sti7109->osd_result_len > 0) {
+		if (copy_to_user((void __user *)cmd->result_data,
+				 sti7109->osd_result_data,
+				 sti7109->osd_result_len))
+			err = -EFAULT;
+	}
+
+out:
+	mutex_unlock(&sti7109->osd_cmd_lock);
+	return err;
+}
+
+static int sti7109_do_raw_data(struct sti7109_dev * sti7109, osd_raw_data_t * data)
+{
+	struct saa716x_dev * saa716x = sti7109->dev;
+	unsigned long timeout;
+	u16 blockSize;
+	u16 lastBlockSize;
+	u16 numBlocks;
+	u16 blockIndex;
+	u8 blockHeader[SIZE_BLOCK_HEADER];
+	u8 * blockPtr;
+	int activeBlock;
+	u16 blockOffset;
+
+	timeout = 1 * HZ;
+	timeout = wait_event_interruptible_timeout(sti7109->data_ready_wq,
+						   sti7109->data_ready == 1,
+						   timeout);
+
+	if (timeout == -ERESTARTSYS || sti7109->data_ready == 0) {
+		if (timeout == -ERESTARTSYS) {
+			/* a signal arrived */
+			dprintk(SAA716x_ERROR, 1, "data ERESTARTSYS");
+			return -ERESTARTSYS;
+		}
+		dprintk(SAA716x_ERROR, 1, "timed out waiting for data ready");
+		return -EIO;
+	}
+
+	sti7109->data_ready = 0;
+
+	/* For firmware 0.5.0 and up the block data should be 32 byte aligned
+	 * to be able to use special DMA transfers which are a lot faster than
+	 * the usual ones.
+	 */
+	if (sti7109->fw_version >= 0x00000500)
+		blockOffset = 32;
+	else
+		blockOffset = 8;
+
+	/*
+	 * 8 bytes is the size of the block header. Block header structure is:
+	 * 16 bit - block index
+	 * 16 bit - number of blocks
+	 * 16 bit - current block data size
+	 * 16 bit - block handle. This is used to reference the data in the
+	 *          command that uses it.
+	 */
+	blockSize = (SIZE_BLOCK_DATA / 2) - blockOffset;
+	numBlocks = data->data_length / blockSize;
+	lastBlockSize = data->data_length % blockSize;
+	if (lastBlockSize > 0)
+		numBlocks++;
+
+	blockHeader[2] = (u8) (numBlocks >> 8);
+	blockHeader[3] = (u8) numBlocks;
+	blockHeader[6] = (u8) (sti7109->data_handle >> 8);
+	blockHeader[7] = (u8) sti7109->data_handle;
+	blockPtr = sti7109->data_buffer;
+	activeBlock = 0;
+	for (blockIndex = 0; blockIndex < numBlocks; blockIndex++) {
+		u32 addr;
+
+		if (lastBlockSize && (blockIndex == (numBlocks - 1)))
+			blockSize = lastBlockSize;
+
+		blockHeader[0] = (uint8_t) (blockIndex >> 8);
+		blockHeader[1] = (uint8_t) blockIndex;
+		blockHeader[4] = (uint8_t) (blockSize >> 8);
+		blockHeader[5] = (uint8_t) blockSize;
+		/* For firmware 0.5.0 and up set the highest bit of the block
+		 * size to signal that the block data is 32 byte aligned.
+		 */
+		if (sti7109->fw_version >= 0x00000500)
+			blockHeader[4] |= 0x80;
+
+		addr = ADDR_BLOCK_DATA + activeBlock * (SIZE_BLOCK_DATA / 2);
+		saa716x_ff_phi_write(saa716x, addr, blockHeader,
+				     SIZE_BLOCK_HEADER);
+		saa716x_ff_phi_write(saa716x, addr + blockOffset,
+				     blockPtr, blockSize);
+		activeBlock = (activeBlock + 1) & 1;
+		if (blockIndex > 0) {
+			timeout = 1 * HZ;
+			timeout = wait_event_timeout(sti7109->block_done_wq,
+						     sti7109->block_done == 1,
+						     timeout);
+
+			if (sti7109->block_done == 0) {
+				dprintk(SAA716x_ERROR, 1,
+					"timed out waiting for block done");
+				/* send a data interrupt to cancel the transfer
+				   and reset the data handling */
+				SAA716x_EPWR(PHI_1, FPGA_ADDR_PHI_ISET,
+						ISR_DATA_MASK);
+				return -EIO;
+			}
+		}
+		sti7109->block_done = 0;
+		SAA716x_EPWR(PHI_1, FPGA_ADDR_PHI_ISET, ISR_BLOCK_MASK);
+		blockPtr += blockSize;
+	}
+	timeout = 1 * HZ;
+	timeout = wait_event_timeout(sti7109->block_done_wq,
+				     sti7109->block_done == 1,
+				     timeout);
+
+	if (sti7109->block_done == 0) {
+		dprintk(SAA716x_ERROR, 1, "timed out waiting for block done");
+		/* send a data interrupt to cancel the transfer and reset the
+		   data handling */
+		SAA716x_EPWR(PHI_1, FPGA_ADDR_PHI_ISET, ISR_DATA_MASK);
+		return -EIO;
+	}
+	sti7109->block_done = 0;
+
+	data->data_handle = sti7109->data_handle;
+	sti7109->data_handle++;
+	return 0;
+}
+
+int sti7109_raw_data(struct sti7109_dev * sti7109, osd_raw_data_t * data)
+{
+	struct saa716x_dev * saa716x = sti7109->dev;
+	int err;
+
+	if (data->data_length > MAX_DATA_LEN) {
+		dprintk(SAA716x_ERROR, 1, "data too big");
+		return -EFAULT;
+	}
+
+	mutex_lock(&sti7109->data_lock);
+
+	err = -EFAULT;
+	if (copy_from_user(sti7109->data_buffer,
+			   (void __user *)data->data_buffer,
+			   data->data_length))
+		goto out;
+
+	err = sti7109_do_raw_data(sti7109, data);
+	if (err)
+		goto out;
+
+out:
+	mutex_unlock(&sti7109->data_lock);
+	return err;
+}
+
+int sti7109_cmd_get_fw_version(struct sti7109_dev *sti7109, u32 *fw_version)
+{
+	int ret_val = -EINVAL;
+
+	mutex_lock(&sti7109->cmd_lock);
+
+	sti7109->cmd_data[0] = 0x00;
+	sti7109->cmd_data[1] = 0x04;
+	sti7109->cmd_data[2] = 0x00;
+	sti7109->cmd_data[3] = 0x00;
+	sti7109->cmd_data[4] = 0x00;
+	sti7109->cmd_data[5] = 0x00;
+	sti7109->cmd_len = 6;
+	sti7109->result_max_len = MAX_RESULT_LEN;
+
+	ret_val = sti7109_do_raw_cmd(sti7109);
+	if (ret_val == 0) {
+		*fw_version = (sti7109->result_data[6] << 16)
+			    | (sti7109->result_data[7] << 8)
+			    | sti7109->result_data[8];
+	}
+
+	mutex_unlock(&sti7109->cmd_lock);
+
+	return ret_val;
+}
+
+int sti7109_cmd_get_video_format(struct sti7109_dev *sti7109, video_size_t *vs)
+{
+	int ret_val = -EINVAL;
+
+	mutex_lock(&sti7109->cmd_lock);
+
+	sti7109->cmd_data[0] = 0x00;
+	sti7109->cmd_data[1] = 0x05; /* command length */
+	sti7109->cmd_data[2] = 0x00;
+	sti7109->cmd_data[3] = 0x01; /* A/V decoder command group */
+	sti7109->cmd_data[4] = 0x00;
+	sti7109->cmd_data[5] = 0x10; /* get video format info command */
+	sti7109->cmd_data[6] = 0x00; /* decoder index 0 */
+	sti7109->cmd_len = 7;
+	sti7109->result_max_len = MAX_RESULT_LEN;
+
+	ret_val = sti7109_do_raw_cmd(sti7109);
+	if (ret_val == 0) {
+		vs->w = (sti7109->result_data[7] << 8)
+		      | sti7109->result_data[8];
+		vs->h = (sti7109->result_data[9] << 8)
+		      | sti7109->result_data[10];
+		vs->aspect_ratio = sti7109->result_data[11] >> 4;
+	}
+
+	mutex_unlock(&sti7109->cmd_lock);
+
+	return ret_val;
+}
+
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_cmd.h linux/drivers/media/pci/saa716x/saa716x_ff_cmd.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_cmd.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_ff_cmd.h	2017-08-11 23:31:25.813389558 +0200
@@ -0,0 +1,16 @@
+#ifndef __SAA716x_FF_CMD_H
+#define __SAA716x_FF_CMD_H
+
+extern int sti7109_cmd_init(struct sti7109_dev *sti7109);
+extern int sti7109_raw_cmd(struct sti7109_dev * sti7109,
+			   osd_raw_cmd_t * cmd);
+extern int sti7109_raw_osd_cmd(struct sti7109_dev * sti7109,
+			       osd_raw_cmd_t * cmd);
+extern int sti7109_raw_data(struct sti7109_dev * sti7109,
+			    osd_raw_data_t * data);
+extern int sti7109_cmd_get_fw_version(struct sti7109_dev *sti7109,
+				      u32 *fw_version);
+extern int sti7109_cmd_get_video_format(struct sti7109_dev *sti7109,
+					video_size_t *vs);
+
+#endif /* __SAA716x_FF_CMD_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_ff.h linux/drivers/media/pci/saa716x/saa716x_ff.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_ff.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_ff.h	2017-08-11 23:31:25.813389558 +0200
@@ -0,0 +1,187 @@
+#ifndef __SAA716x_FF_H
+#define __SAA716x_FF_H
+
+#include "dvb_filter.h"
+#include "dvb_ringbuffer.h"
+#include <linux/version.h>
+#include <linux/workqueue.h>
+
+#define TECHNOTREND			0x13c2
+#define S2_6400_DUAL_S2_PREMIUM_DEVEL	0x3009
+#define S2_6400_DUAL_S2_PREMIUM_PROD	0x300A
+
+#define TT_PREMIUM_GPIO_POWER_ENABLE	27
+#define TT_PREMIUM_GPIO_RESET_BACKEND	26
+#define TT_PREMIUM_GPIO_FPGA_CS2	18
+#define TT_PREMIUM_GPIO_FPGA_CS1	17
+#define TT_PREMIUM_GPIO_FPGA_CS0	16
+#define TT_PREMIUM_GPIO_FPGA_PROGRAMN	15
+#define TT_PREMIUM_GPIO_FPGA_DONE	14
+#define TT_PREMIUM_GPIO_FPGA_INITN	13
+
+/* fpga interrupt register addresses */
+#define FPGA_ADDR_PHI_ICTRL	0x8000 /* PHI General control of the PC => STB interrupt controller */
+#define FPGA_ADDR_PHI_ISR	0x8010 /* PHI Interrupt Status Register */
+#define FPGA_ADDR_PHI_ISET	0x8020 /* PHI Interrupt Set Register */
+#define FPGA_ADDR_PHI_ICLR	0x8030 /* PHI Interrupt Clear Register */
+#define FPGA_ADDR_EMI_ICTRL	0x8100 /* EMI General control of the STB => PC interrupt controller */
+#define FPGA_ADDR_EMI_ISR	0x8110 /* EMI Interrupt Status Register */
+#define FPGA_ADDR_EMI_ISET	0x8120 /* EMI Interrupt Set Register */
+#define FPGA_ADDR_EMI_ICLR	0x8130 /* EMI Interrupt Clear Register */
+
+/* fpga TS router register addresses */
+#define FPGA_ADDR_TSR_CTRL	0x8200 /* TS router control register */
+#define FPGA_ADDR_TSR_MUX1	0x8210 /* TS multiplexer 1 selection register */
+#define FPGA_ADDR_TSR_MUX2	0x8220 /* TS multiplexer 2 selection register */
+#define FPGA_ADDR_TSR_MUX3	0x8230 /* TS multiplexer 3 selection register */
+#define FPGA_ADDR_TSR_MUXCI1	0x8240 /* TS multiplexer CI 1 selection register */
+#define FPGA_ADDR_TSR_MUXCI2	0x8250 /* TS multiplexer CI 2 selection register */
+
+#define FPGA_ADDR_TSR_BRFE1	0x8280 /* bit rate for TS coming from frontend 1 */
+#define FPGA_ADDR_TSR_BRFE2	0x8284 /* bit rate for TS coming from frontend 2 */
+#define FPGA_ADDR_TSR_BRFF1	0x828C /* bit rate for TS coming from FIFO 1 */
+#define FPGA_ADDR_TSR_BRO1	0x8294 /* bit rate for TS going to output 1 */
+#define FPGA_ADDR_TSR_BRO2	0x8298 /* bit rate for TS going to output 2 */
+#define FPGA_ADDR_TSR_BRO3	0x829C /* bit rate for TS going to output 3 */
+
+/* fpga TS FIFO register addresses */
+#define FPGA_ADDR_FIFO_CTRL	0x8300 /* FIFO control register */
+#define FPGA_ADDR_FIFO_STAT	0x8310 /* FIFO status register */
+
+#define FPGA_ADDR_VERSION	0x80F0 /* FPGA bitstream version register */
+
+#define FPGA_ADDR_PIO_CTRL	0x8500 /* FPGA GPIO control register */
+
+#define FPGA_FIFO_CTRL_RESET	0x0001 /* FIFO control register, reset bit */
+#define FPGA_FIFO_CTRL_RUN	0x0002 /* FIFO control register, run bit */
+#define FPGA_FIFO_CTRL_IE	0x0004 /* FIFO control register, interrupt enable bit */
+
+#define ISR_CMD_MASK		0x0001 /* interrupt source for normal cmds (osd, fre, av, ...) */
+#define ISR_READY_MASK		0x0002 /* interrupt source for command acknowledge */
+#define ISR_BLOCK_MASK		0x0004 /* interrupt source for single block transfers and acknowledge */
+#define ISR_DATA_MASK		0x0008 /* interrupt source for data transfer acknowledge */
+#define ISR_BOOT_FINISH_MASK	0x0010 /* interrupt source for boot finish indication */
+#define ISR_AUDIO_PTS_MASK	0x0020 /* interrupt source for audio PTS */
+#define ISR_VIDEO_PTS_MASK	0x0040 /* interrupt source for video PTS */
+#define ISR_CURRENT_STC_MASK	0x0080 /* interrupt source for current system clock */
+#define ISR_REMOTE_EVENT_MASK	0x0100 /* interrupt source for remote events */
+#define ISR_DVO_FORMAT_MASK	0x0200 /* interrupt source for DVO format change */
+#define ISR_OSD_CMD_MASK	0x0400 /* interrupt source for OSD cmds */
+#define ISR_OSD_READY_MASK	0x0800 /* interrupt source for OSD command acknowledge */
+#define ISR_FE_CMD_MASK		0x1000 /* interrupt source for frontend cmds */
+#define ISR_FE_READY_MASK	0x2000 /* interrupt source for frontend command acknowledge */
+#define ISR_LOG_MESSAGE_MASK	0x4000 /* interrupt source for log messages */
+#define ISR_FIFO1_EMPTY_MASK	0x8000 /* interrupt source for FIFO1 empty */
+
+#define ADDR_CMD_DATA		0x0000 /* address for cmd data in fpga dpram */
+#define ADDR_OSD_CMD_DATA	0x01A0 /* address for OSD cmd data */
+#define ADDR_FE_CMD_DATA	0x05C0 /* address for frontend cmd data */
+#define ADDR_BLOCK_DATA		0x0600 /* address for block data */
+#define ADDR_AUDIO_PTS		0x3E00 /* address for audio PTS (64 Bits) */
+#define ADDR_VIDEO_PTS		0x3E08 /* address for video PTS (64 Bits) */
+#define ADDR_CURRENT_STC	0x3E10 /* address for system clock (64 Bits) */
+#define ADDR_DVO_FORMAT		0x3E18 /* address for DVO format 32 Bits) */
+#define ADDR_REMOTE_EVENT	0x3F00 /* address for remote events (32 Bits) */
+#define ADDR_LOG_MESSAGE	0x3F80 /* address for log messages */
+
+#define SIZE_CMD_DATA		0x01A0 /* maximum size for command data (416 Bytes) */
+#define SIZE_OSD_CMD_DATA	0x0420 /* maximum size for OSD command data (1056 Bytes) */
+#define SIZE_FE_CMD_DATA	0x0040 /* maximum size for frontend command data (64 Bytes) */
+#define SIZE_BLOCK_DATA		0x3800 /* maximum size for block data (14 kB) */
+#define SIZE_LOG_MESSAGE_DATA	0x0080 /* maximum size for log message data (128 Bytes) */
+
+#define SIZE_BLOCK_HEADER	8      /* block header size */
+
+#define MAX_RESULT_LEN		256
+#define MAX_DATA_LEN		(1024 * 1024)
+
+#define TSOUT_LEN		(1024 * TS_SIZE)
+#define TSOUT_LEVEL_FILL	(350 * TS_SIZE)
+#define TSOUT_LEVEL_HIGH	(30 * TS_SIZE)
+#define TSOUT_LEVEL_LOW 	(20 * TS_SIZE)
+
+#define TSOUT_STAT_RESET	0
+#define TSOUT_STAT_FILL 	1
+#define TSOUT_STAT_RUN  	2
+#define TSOUT_STAT_WAIT  	3
+
+#define VIDEO_CAPTURE_OFF	0
+#define VIDEO_CAPTURE_ONE_SHOT	1
+
+
+/* place to store all the necessary device information */
+struct sti7109_dev {
+	struct saa716x_dev	*dev;
+	struct dvb_device	*osd_dev;
+	struct dvb_device	*video_dev;
+	struct dvb_device	*audio_dev;
+
+	void __iomem		*mmio_uc; /* uncached PHI1 regions */
+	void __iomem		*mmio_wc; /* write-combining PHI1 regions */
+	int			phi_mode;
+
+	void			*iobuf;  /* memory for all buffers */
+	struct dvb_ringbuffer	tsout;   /* buffer for TS output */
+	u32			tsout_stat;
+
+	struct workqueue_struct *fifo_workq;
+	struct work_struct	fifo_work;
+
+	wait_queue_head_t	boot_finish_wq;
+	int			boot_finished;
+
+	wait_queue_head_t	cmd_ready_wq;
+	int			cmd_ready;
+	u8			cmd_data[SIZE_CMD_DATA];
+	u32			cmd_len;
+
+	wait_queue_head_t	result_avail_wq;
+	int			result_avail;
+	u8			result_data[MAX_RESULT_LEN];
+	u32			result_len;
+	u32			result_max_len;
+
+	wait_queue_head_t	osd_cmd_ready_wq;
+	int			osd_cmd_ready;
+	u8			osd_cmd_data[SIZE_OSD_CMD_DATA];
+	u32			osd_cmd_len;
+
+	wait_queue_head_t	osd_result_avail_wq;
+	int			osd_result_avail;
+	u8			osd_result_data[MAX_RESULT_LEN];
+	u32			osd_result_len;
+	u32			osd_result_max_len;
+
+	u16			data_handle;
+	u8			*data_buffer; /* raw data transfer buffer */
+	wait_queue_head_t	data_ready_wq;
+	int			data_ready;
+	wait_queue_head_t	block_done_wq;
+	int			block_done;
+
+	struct mutex		cmd_lock;
+	struct mutex		osd_cmd_lock;
+	struct mutex		data_lock;
+	struct mutex		video_lock;
+
+	u64			audio_pts;
+	u64			video_pts;
+	u64			current_stc;
+
+	u32			video_capture;
+	u32			video_format;
+
+	u32			int_count_enable;
+	u32			total_int_count;
+	u32			vi_int_count[2];
+	u32			fgpi_int_count[4];
+	u32			i2c_int_count[2];
+	u32			ext_int_total_count;
+	u32			ext_int_source_count[16];
+	u32			last_int_ticks;
+
+	u16			fpga_version;
+	u32			fw_version;
+};
+
+#endif /* __SAA716x_FF_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_ir.c linux/drivers/media/pci/saa716x/saa716x_ff_ir.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_ir.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_ff_ir.c	2017-08-11 23:31:25.813389558 +0200
@@ -0,0 +1,265 @@
+/*
+ * Driver for the remote control of the TT6400 DVB-S2 card
+ *
+ * Copyright (C) 2010 Oliver Endriss <o.endriss@gmx.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/input.h>
+
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+#include "saa716x_ff.h"
+
+
+/* infrared remote control */
+struct infrared {
+	u16			key_map[128];
+	struct input_dev	*input_dev;
+	char			input_phys[32];
+	struct timer_list	keyup_timer;
+	struct tasklet_struct	tasklet;
+	u32			command;
+	u32			device_mask;
+	u8			protocol;
+	u16			last_key;
+	u16			last_toggle;
+	bool			delay_timer_finished;
+};
+
+#define IR_RC5		0
+#define UP_TIMEOUT	(HZ*7/25)
+
+
+/* key-up timer */
+static void ir_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 ir_emit_key(unsigned long parm)
+{
+	struct saa716x_dev *saa716x = (struct saa716x_dev *) parm;
+	struct infrared *ir = saa716x->ir_priv;
+	u32 ircom = ir->command;
+	u8 data;
+	u8 addr;
+	u16 toggle;
+	u16 keycode;
+
+	/* extract device address and data */
+	if (ircom & 0x80000000) { /* CEC remote command */
+		addr = 0;
+		data = ircom & 0x7F;
+		toggle = 0;
+	} else {
+		switch (ir->protocol) {
+		case IR_RC5: /* extended RC5: 5 bits device address, 7 bits data */
+			addr = (ircom >> 6) & 0x1f;
+			/* data bits 1..6 */
+			data = ircom & 0x3f;
+			/* data bit 7 (inverted) */
+			if (!(ircom & 0x1000))
+				data |= 0x40;
+			toggle = ircom & 0x0800;
+			break;
+
+		default:
+			printk(KERN_ERR "%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(SAA716x_DEBUG, 0,
+		"%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(KERN_WARNING "%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 = false;
+			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 = false;
+		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 ir_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 ir_repeat_key(unsigned long parm)
+{
+	struct infrared *ir = (struct infrared *) parm;
+
+	ir->delay_timer_finished = true;
+}
+
+
+/* interrupt handler */
+void saa716x_ir_handler(struct saa716x_dev *saa716x, u32 ir_cmd)
+{
+	struct infrared *ir = saa716x->ir_priv;
+
+	if (!ir)
+		return;
+
+	ir->command = ir_cmd;
+	tasklet_schedule(&ir->tasklet);
+}
+
+
+int saa716x_ir_init(struct saa716x_dev *saa716x)
+{
+	struct input_dev *input_dev;
+	struct infrared *ir;
+	int rc;
+	int i;
+
+	if (!saa716x)
+		return -ENOMEM;
+
+	ir = kzalloc(sizeof(struct infrared), GFP_KERNEL);
+	if (!ir)
+		return -ENOMEM;
+
+	init_timer(&ir->keyup_timer);
+	ir->keyup_timer.function = ir_emit_keyup;
+	ir->keyup_timer.data = (unsigned long) ir;
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		goto err;
+
+	ir->input_dev = input_dev;
+	input_dev->name = "TT6400 DVB IR receiver";
+	snprintf(ir->input_phys, sizeof(ir->input_phys),
+		"pci-%s/ir0", pci_name(saa716x->pdev));
+	input_dev->phys = ir->input_phys;
+	input_dev->id.bustype = BUS_PCI;
+	input_dev->id.version = 1;
+	input_dev->id.vendor = saa716x->pdev->subsystem_vendor;
+	input_dev->id.product = saa716x->pdev->subsystem_device;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+	input_dev->dev.parent = &saa716x->pdev->dev;
+#else
+	input_dev->cdev.dev = &saa716x->pdev->dev;
+#endif
+	rc = input_register_device(input_dev);
+	if (rc)
+		goto err;
+
+	/* TODO: fix setup/keymap */
+	ir->protocol = IR_RC5;
+	ir->device_mask = 0xffffffff;
+	for (i = 0; i < ARRAY_SIZE(ir->key_map); i++)
+		ir->key_map[i] = i+1;
+	ir_register_keys(ir);
+
+	/* override repeat timer */
+	input_dev->timer.function = ir_repeat_key;
+	input_dev->timer.data = (unsigned long) ir;
+
+	tasklet_init(&ir->tasklet, ir_emit_key, (unsigned long) saa716x);
+	saa716x->ir_priv = ir;
+
+	return 0;
+
+err:
+	if (ir->input_dev)
+		input_free_device(ir->input_dev);
+	kfree(ir);
+	return -ENOMEM;
+}
+
+
+void saa716x_ir_exit(struct saa716x_dev *saa716x)
+{
+	struct infrared *ir = saa716x->ir_priv;
+
+	saa716x->ir_priv = NULL;
+	tasklet_kill(&ir->tasklet);
+	del_timer_sync(&ir->keyup_timer);
+	input_unregister_device(ir->input_dev);
+	kfree(ir);
+}
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_main.c linux/drivers/media/pci/saa716x/saa716x_ff_main.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_main.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_ff_main.c	2017-08-11 23:31:25.829389469 +0200
@@ -0,0 +1,1859 @@
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/byteorder.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include <linux/i2c.h>
+
+#include <linux/videodev2.h>
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <linux/dvb/osd.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_dma_reg.h"
+#include "saa716x_fgpi_reg.h"
+#include "saa716x_greg_reg.h"
+#include "saa716x_phi_reg.h"
+#include "saa716x_spi_reg.h"
+#include "saa716x_msi_reg.h"
+
+#include "saa716x_msi.h"
+#include "saa716x_adap.h"
+#include "saa716x_gpio.h"
+#include "saa716x_phi.h"
+#include "saa716x_rom.h"
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+
+#include "saa716x_ff.h"
+#include "saa716x_ff_cmd.h"
+#include "saa716x_ff_phi.h"
+
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "isl6423.h"
+
+unsigned int verbose;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
+
+unsigned int int_type;
+module_param(int_type, int, 0644);
+MODULE_PARM_DESC(int_type, "force Interrupt Handler type: 0=INT-A, 1=MSI, 2=MSI-X. default INT-A mode");
+
+unsigned int int_count_enable;
+module_param(int_count_enable, int, 0644);
+MODULE_PARM_DESC(int_count_enable, "enable counting of interrupts");
+
+unsigned int video_capture;
+module_param(video_capture, int, 0644);
+MODULE_PARM_DESC(video_capture, "capture digital video coming from STi7109: 0=off, 1=one-shot. default off");
+
+#define DRIVER_NAME	"SAA716x FF"
+
+static int saa716x_ff_fpga_init(struct saa716x_dev *saa716x)
+{
+	struct sti7109_dev *sti7109 = saa716x->priv;
+	int fpgaInit;
+	int fpgaDone;
+	int rounds;
+	int ret;
+	const struct firmware *fw;
+
+	/* request the FPGA firmware, this will block until someone uploads it */
+	ret = request_firmware(&fw, "dvb-ttpremium-fpga-01.fw", &saa716x->pdev->dev);
+	if (ret) {
+		if (ret == -ENOENT) {
+			printk(KERN_ERR "dvb-ttpremium: could not load FPGA firmware,"
+			       " file not found: dvb-ttpremium-fpga-01.fw\n");
+			printk(KERN_ERR "dvb-ttpremium: usually this should be in "
+			       "/usr/lib/hotplug/firmware or /lib/firmware\n");
+		} else
+			printk(KERN_ERR "dvb-ttpremium: cannot request firmware"
+			       " (error %i)\n", ret);
+		return -EINVAL;
+	}
+
+	/* set FPGA PROGRAMN high */
+	saa716x_gpio_write(saa716x, TT_PREMIUM_GPIO_FPGA_PROGRAMN, 1);
+	msleep(10);
+
+	/* set FPGA PROGRAMN low to set it into configuration mode */
+	saa716x_gpio_write(saa716x, TT_PREMIUM_GPIO_FPGA_PROGRAMN, 0);
+	msleep(10);
+
+	/* set FPGA PROGRAMN high to start configuration process */
+	saa716x_gpio_write(saa716x, TT_PREMIUM_GPIO_FPGA_PROGRAMN, 1);
+
+	rounds = 0;
+	fpgaInit = saa716x_gpio_read(saa716x, TT_PREMIUM_GPIO_FPGA_INITN);
+	while (fpgaInit == 0 && rounds < 5000) {
+		//msleep(1);
+		fpgaInit = saa716x_gpio_read(saa716x, TT_PREMIUM_GPIO_FPGA_INITN);
+		rounds++;
+	}
+	dprintk(SAA716x_INFO, 1, "SAA716x FF FPGA INITN=%d, rounds=%d",
+		fpgaInit, rounds);
+
+	SAA716x_EPWR(SPI, SPI_CLOCK_COUNTER, 0x08);
+	SAA716x_EPWR(SPI, SPI_CONTROL_REG, SPI_MODE_SELECT);
+
+	msleep(10);
+
+	fpgaDone = saa716x_gpio_read(saa716x, TT_PREMIUM_GPIO_FPGA_DONE);
+	dprintk(SAA716x_INFO, 1, "SAA716x FF FPGA DONE=%d", fpgaDone);
+	dprintk(SAA716x_INFO, 1, "SAA716x FF FPGA write bitstream");
+	saa716x_spi_write(saa716x, fw->data, fw->size);
+	dprintk(SAA716x_INFO, 1, "SAA716x FF FPGA write bitstream done");
+	fpgaDone = saa716x_gpio_read(saa716x, TT_PREMIUM_GPIO_FPGA_DONE);
+	dprintk(SAA716x_INFO, 1, "SAA716x FF FPGA DONE=%d", fpgaDone);
+
+	msleep(10);
+
+	release_firmware(fw);
+
+	if (!fpgaDone) {
+		printk(KERN_ERR "SAA716x FF FPGA is not responding, did you "
+				"connect the power supply?\n");
+		return -EINVAL;
+	}
+
+	sti7109->fpga_version = SAA716x_EPRD(PHI_1, FPGA_ADDR_VERSION);
+	printk(KERN_INFO "SAA716x FF FPGA version %X.%02X\n",
+		sti7109->fpga_version >> 8, sti7109->fpga_version & 0xFF);
+
+	return 0;
+}
+
+static int saa716x_ff_st7109_init(struct saa716x_dev *saa716x)
+{
+	struct sti7109_dev *sti7109 = saa716x->priv;
+	int i;
+	int length;
+	u32 requestedBlock;
+	u32 writtenBlock;
+	u32 numBlocks;
+	u32 blockSize;
+	u32 lastBlockSize;
+	u64 startTime;
+	u64 currentTime;
+	u64 waitTime;
+	int ret;
+	const struct firmware *fw;
+	u32 loaderVersion;
+	u32 options;
+
+	/* request the st7109 loader, this will block until someone uploads it */
+	ret = request_firmware(&fw, "dvb-ttpremium-loader-01.fw", &saa716x->pdev->dev);
+	if (ret) {
+		if (ret == -ENOENT) {
+			printk(KERN_ERR "dvb-ttpremium: could not load ST7109 loader,"
+			       " file not found: dvb-ttpremium-loader-01.fw\n");
+			printk(KERN_ERR "dvb-ttpremium: usually this should be in "
+			       "/usr/lib/hotplug/firmware or /lib/firmware\n");
+		} else
+			printk(KERN_ERR "dvb-ttpremium: cannot request firmware"
+			       " (error %i)\n", ret);
+		return -EINVAL;
+	}
+	loaderVersion = (fw->data[0x1385] << 8) | fw->data[0x1384];
+	printk(KERN_INFO "SAA716x FF loader version %X.%02X\n",
+		loaderVersion >> 8, loaderVersion & 0xFF);
+
+	saa716x_ff_phi_write(saa716x, 0, fw->data, fw->size);
+	msleep(10);
+
+	release_firmware(fw);
+
+	/* take ST out of reset */
+	saa716x_gpio_write(saa716x, TT_PREMIUM_GPIO_RESET_BACKEND, 1);
+
+	startTime = jiffies;
+	waitTime = 0;
+	do {
+		requestedBlock = SAA716x_EPRD(PHI_1, 0x3ffc);
+		if (requestedBlock == 1)
+			break;
+
+		currentTime = jiffies;
+		waitTime = currentTime - startTime;
+	} while (waitTime < (1 * HZ));
+
+	if (waitTime >= 1 * HZ) {
+		dprintk(SAA716x_ERROR, 1, "STi7109 seems to be DEAD!");
+		return -1;
+	}
+	dprintk(SAA716x_INFO, 1, "STi7109 ready after %llu ticks", waitTime);
+
+	/* request the st7109 firmware, this will block until someone uploads it */
+	ret = request_firmware(&fw, "dvb-ttpremium-st7109-01.fw", &saa716x->pdev->dev);
+	if (ret) {
+		if (ret == -ENOENT) {
+			printk(KERN_ERR "dvb-ttpremium: could not load ST7109 firmware,"
+			       " file not found: dvb-ttpremium-st7109-01.fw\n");
+			printk(KERN_ERR "dvb-ttpremium: usually this should be in "
+			       "/usr/lib/hotplug/firmware or /lib/firmware\n");
+		} else
+			printk(KERN_ERR "dvb-ttpremium: cannot request firmware"
+			       " (error %i)\n", ret);
+		return -EINVAL;
+	}
+
+	dprintk(SAA716x_INFO, 1, "SAA716x FF download ST7109 firmware");
+	writtenBlock = 0;
+	blockSize = 0x3c00;
+	length = fw->size;
+	numBlocks = length / blockSize;
+	lastBlockSize = length % blockSize;
+	for (i = 0; i < length; i += blockSize) {
+		writtenBlock++;
+		/* write one block (last may differ from blockSize) */
+		if (lastBlockSize && writtenBlock == (numBlocks + 1))
+			saa716x_ff_phi_write(saa716x, 0, &fw->data[i],
+					     lastBlockSize);
+		else
+			saa716x_ff_phi_write(saa716x, 0, &fw->data[i],
+					     blockSize);
+
+		SAA716x_EPWR(PHI_1, 0x3ff8, writtenBlock);
+		startTime = jiffies;
+		waitTime = 0;
+		do {
+			requestedBlock = SAA716x_EPRD(PHI_1, 0x3ffc);
+			if (requestedBlock == (writtenBlock + 1))
+				break;
+
+			currentTime = jiffies;
+			waitTime = currentTime - startTime;
+		} while (waitTime < (1 * HZ));
+
+		if (waitTime >= 1 * HZ) {
+			dprintk(SAA716x_ERROR, 1, "STi7109 seems to be DEAD!");
+			release_firmware(fw);
+			return -1;
+		}
+	}
+
+	/* disable frontend support through ST firmware */
+	options = 0x00000001;
+	/* enable faster EMI timings in ST firmware for FPGA 1.10 and later */
+	if (sti7109->fpga_version >= 0x110)
+		options |= 0x00000002;
+	SAA716x_EPWR(PHI_1, 0x3ff4, options);
+
+	/* indicate end of transfer */
+	writtenBlock++;
+	writtenBlock |= 0x80000000;
+	SAA716x_EPWR(PHI_1, 0x3ff8, writtenBlock);
+
+	dprintk(SAA716x_INFO, 1, "SAA716x FF download ST7109 firmware done");
+
+	release_firmware(fw);
+
+	return 0;
+}
+
+static int saa716x_usercopy(struct dvb_device *dvbdev,
+			    unsigned int cmd, unsigned long arg,
+			    int (*func)(struct dvb_device *dvbdev,
+			    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 */
+	if ((err = func(dvbdev, cmd, parg)) == -ENOIOCTLCMD)
+		err = -EINVAL;
+
+	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;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) && !defined(EXPERIMENTAL_TREE)
+static int dvb_osd_ioctl(struct inode *inode, struct file *file,
+#else
+static long dvb_osd_ioctl(struct file *file,
+#endif
+			 unsigned int cmd, unsigned long arg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct sti7109_dev *sti7109 = dvbdev->priv;
+	int err = -EINVAL;
+
+	if (!dvbdev)
+		return -ENODEV;
+
+	if (cmd == OSD_RAW_CMD) {
+		osd_raw_cmd_t raw_cmd;
+		u8 hdr[4];
+
+		err = -EFAULT;
+		if (copy_from_user(&raw_cmd, (void __user *)arg,
+				   _IOC_SIZE(cmd)))
+			goto out;
+
+		if (copy_from_user(hdr, (void __user *)raw_cmd.cmd_data, 4))
+			goto out;
+
+		if (hdr[3] == 4)
+			err = sti7109_raw_osd_cmd(sti7109, &raw_cmd);
+		else
+			err = sti7109_raw_cmd(sti7109, &raw_cmd);
+
+		if (err)
+			goto out;
+
+		if (copy_to_user((void __user *)arg, &raw_cmd, _IOC_SIZE(cmd)))
+			err = -EFAULT;
+	}
+	else if (cmd == OSD_RAW_DATA) {
+		osd_raw_data_t raw_data;
+
+		err = -EFAULT;
+		if (copy_from_user(&raw_data, (void __user *)arg,
+				   _IOC_SIZE(cmd)))
+			goto out;
+
+		err = sti7109_raw_data(sti7109, &raw_data);
+		if (err)
+			goto out;
+
+		if (copy_to_user((void __user *)arg, &raw_data, _IOC_SIZE(cmd)))
+			err = -EFAULT;
+	}
+
+out:
+	return err;
+}
+
+static struct file_operations dvb_osd_fops = {
+	.owner		= THIS_MODULE,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) && !defined(EXPERIMENTAL_TREE)
+	.ioctl		= dvb_osd_ioctl,
+#else
+	.unlocked_ioctl	= dvb_osd_ioctl,
+#endif
+	.open		= dvb_generic_open,
+	.release	= dvb_generic_release,
+};
+
+static struct dvb_device dvbdev_osd = {
+	.priv		= NULL,
+	.users		= 2,
+	.writers	= 2,
+	.fops		= &dvb_osd_fops,
+	.kernel_ioctl	= NULL,
+};
+
+static int saa716x_ff_osd_exit(struct saa716x_dev *saa716x)
+{
+	struct sti7109_dev *sti7109 = saa716x->priv;
+
+	dvb_unregister_device(sti7109->osd_dev);
+	return 0;
+}
+
+static int saa716x_ff_osd_init(struct saa716x_dev *saa716x)
+{
+	struct saa716x_adapter *saa716x_adap	= saa716x->saa716x_adap;
+	struct sti7109_dev *sti7109		= saa716x->priv;
+
+	dvb_register_device(&saa716x_adap->dvb_adapter,
+			    &sti7109->osd_dev,
+			    &dvbdev_osd,
+			    sti7109,
+			    DVB_DEVICE_OSD
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
+			    , 0
+#endif
+			    );
+
+	return 0;
+}
+
+static int do_dvb_audio_ioctl(struct dvb_device *dvbdev,
+			      unsigned int cmd, void *parg)
+{
+	struct sti7109_dev *sti7109	= dvbdev->priv;
+	//struct saa716x_dev *saa716x	= sti7109->dev;
+	int ret = 0;
+
+	switch (cmd) {
+	case AUDIO_GET_PTS:
+	{
+		*(u64 *)parg = sti7109->audio_pts;
+		break;
+	}
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+	return ret;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) && !defined(EXPERIMENTAL_TREE)
+static int dvb_audio_ioctl(struct inode *inode, struct file *file,
+#else
+static long dvb_audio_ioctl(struct file *file,
+#endif
+			   unsigned int cmd, unsigned long arg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+
+	if (!dvbdev)
+		return -ENODEV;
+
+	return saa716x_usercopy (dvbdev, cmd, arg, do_dvb_audio_ioctl);
+}
+
+static struct file_operations dvb_audio_fops = {
+	.owner		= THIS_MODULE,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) && !defined(EXPERIMENTAL_TREE)
+	.ioctl		= dvb_audio_ioctl,
+#else
+	.unlocked_ioctl	= dvb_audio_ioctl,
+#endif
+	.open		= dvb_generic_open,
+	.release	= dvb_generic_release,
+};
+
+static struct dvb_device dvbdev_audio = {
+	.priv		= NULL,
+	.users		= 1,
+	.writers	= 1,
+	.fops		= &dvb_audio_fops,
+	.kernel_ioctl	= NULL,
+};
+
+static int saa716x_ff_audio_exit(struct saa716x_dev *saa716x)
+{
+	struct sti7109_dev *sti7109 = saa716x->priv;
+
+	dvb_unregister_device(sti7109->audio_dev);
+	return 0;
+}
+
+static int saa716x_ff_audio_init(struct saa716x_dev *saa716x)
+{
+	struct saa716x_adapter *saa716x_adap	= saa716x->saa716x_adap;
+	struct sti7109_dev *sti7109		= saa716x->priv;
+
+	dvb_register_device(&saa716x_adap->dvb_adapter,
+			    &sti7109->audio_dev,
+			    &dvbdev_audio,
+			    sti7109,
+			    DVB_DEVICE_AUDIO
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
+			    , 0
+#endif
+			    );
+
+	return 0;
+}
+
+static ssize_t ringbuffer_write_user(struct dvb_ringbuffer *rbuf, const u8 __user *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) {
+        	if (copy_from_user(rbuf->data+rbuf->pwrite, buf, split)) {
+			return -EFAULT;
+		}
+		buf += split;
+		todo -= split;
+		rbuf->pwrite = 0;
+	}
+	if (copy_from_user(rbuf->data+rbuf->pwrite, buf, todo)) {
+		return -EFAULT;
+	}
+	rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
+
+	return len;
+}
+
+static void ringbuffer_read_tofifo(struct dvb_ringbuffer *rbuf,
+				   struct saa716x_dev *saa716x, int len)
+{
+	size_t todo = len;
+	size_t split;
+
+	split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
+	if (split > 0) {
+		saa716x_ff_phi_write_fifo(saa716x, rbuf->data + rbuf->pread,
+					  split);
+		todo -= split;
+		rbuf->pread = 0;
+	}
+	saa716x_ff_phi_write_fifo(saa716x, rbuf->data + rbuf->pread, todo);
+
+	rbuf->pread = (rbuf->pread + todo) % rbuf->size;
+}
+
+static void fifo_worker(struct work_struct *work)
+{
+	struct sti7109_dev *sti7109 = container_of(work, struct sti7109_dev, fifo_work);
+	struct saa716x_dev *saa716x = sti7109->dev;
+	u32 fifoStat;
+	u16 fifoSize;
+	u16 fifoUsage;
+	u16 fifoFree;
+	int ringbuffer_avail;
+	int len;
+
+	if (sti7109->tsout_stat == TSOUT_STAT_RESET)
+		return;
+
+	fifoStat = SAA716x_EPRD(PHI_1, FPGA_ADDR_FIFO_STAT);
+	fifoSize = (u16) (fifoStat >> 16);
+	fifoUsage = (u16) fifoStat;
+	fifoFree = fifoSize - fifoUsage;
+	ringbuffer_avail = dvb_ringbuffer_avail(&sti7109->tsout);
+
+	len = (ringbuffer_avail < fifoFree) ? ringbuffer_avail : fifoFree;
+	len = (len / 32) * 32;
+	if (len) {
+		ringbuffer_read_tofifo(&sti7109->tsout, saa716x, len);
+		wake_up(&sti7109->tsout.queue);
+	}
+
+	ringbuffer_avail = dvb_ringbuffer_avail(&sti7109->tsout);
+	if (ringbuffer_avail < TSOUT_LEVEL_LOW) {
+		spin_lock(&sti7109->tsout.lock);
+		if (sti7109->tsout_stat != TSOUT_STAT_RESET)
+			sti7109->tsout_stat = TSOUT_STAT_WAIT;
+		spin_unlock(&sti7109->tsout.lock);
+		return;
+	}
+
+	spin_lock(&sti7109->tsout.lock);
+	if (sti7109->tsout_stat != TSOUT_STAT_RESET) {
+		/* reenable fifo interrupt */
+		sti7109->tsout_stat = TSOUT_STAT_RUN;
+		SAA716x_EPWR(PHI_1, FPGA_ADDR_FIFO_CTRL, FPGA_FIFO_CTRL_IE | FPGA_FIFO_CTRL_RUN);
+	}
+	spin_unlock(&sti7109->tsout.lock);
+}
+
+static void video_vip_worker(unsigned long data)
+{
+	struct saa716x_vip_stream_port *vip_entry = (struct saa716x_vip_stream_port *)data;
+	struct saa716x_dev *saa716x = vip_entry->saa716x;
+	u32 vip_index;
+	u32 write_index;
+
+	vip_index = vip_entry->dma_channel[0];
+	if (vip_index != 0) {
+		printk(KERN_ERR "%s: unexpected channel %u\n",
+		       __func__, vip_entry->dma_channel[0]);
+		return;
+	}
+
+	write_index = saa716x_vip_get_write_index(saa716x, vip_index);
+	if (write_index < 0)
+		return;
+
+	dprintk(SAA716x_DEBUG, 1, "dma buffer = %d", write_index);
+
+	if (write_index == vip_entry->read_index) {
+		printk(KERN_DEBUG "%s: called but nothing to do\n", __func__);
+		return;
+	}
+
+	do {
+		pci_dma_sync_sg_for_cpu(saa716x->pdev,
+			vip_entry->dma_buf[0][vip_entry->read_index].sg_list,
+			vip_entry->dma_buf[0][vip_entry->read_index].list_len,
+			PCI_DMA_FROMDEVICE);
+		if (vip_entry->dual_channel) {
+			pci_dma_sync_sg_for_cpu(saa716x->pdev,
+				vip_entry->dma_buf[1][vip_entry->read_index].sg_list,
+				vip_entry->dma_buf[1][vip_entry->read_index].list_len,
+				PCI_DMA_FROMDEVICE);
+		}
+
+		vip_entry->read_index = (vip_entry->read_index + 1) & 7;
+	} while (write_index != vip_entry->read_index);
+}
+
+static int video_vip_get_stream_params(struct vip_stream_params * params,
+				       u32 mode)
+{
+	switch (mode)
+	{
+		case 4:  /* 1280x720p60 */
+		case 19: /* 1280x720p50 */
+			params->bits		= 16;
+			params->samples		= 1280;
+			params->lines		= 720;
+			params->pitch		= 1280 * 2;
+			params->offset_x	= 32;
+			params->offset_y	= 30;
+			params->stream_flags	= VIP_HD;
+			break;
+
+		case 5:  /* 1920x1080i60 */
+		case 20: /* 1920x1080i50 */
+			params->bits		= 16;
+			params->samples		= 1920;
+			params->lines		= 1080;
+			params->pitch		= 1920 * 2;
+			params->offset_x	= 0;
+			params->offset_y	= 20;
+			params->stream_flags	= VIP_ODD_FIELD
+						| VIP_EVEN_FIELD
+						| VIP_INTERLACED
+						| VIP_HD
+						| VIP_NO_SCALER;
+			break;
+
+		case 32: /* 1920x1080p24 */
+		case 33: /* 1920x1080p25 */
+		case 34: /* 1920x1080p30 */
+			params->bits		= 16;
+			params->samples		= 1920;
+			params->lines		= 1080;
+			params->pitch		= 1920 * 2;
+			params->offset_x	= 0;
+			params->offset_y	= 0;
+			params->stream_flags	= VIP_HD;
+			break;
+
+		default:
+			return -1;
+	}
+	return 0;
+}
+
+static ssize_t video_vip_read(struct sti7109_dev *sti7109,
+			      struct vip_stream_params * stream_params,
+			      char __user *buf, size_t count)
+{
+	struct saa716x_dev *saa716x = sti7109->dev;
+	struct v4l2_pix_format pix_format;
+	int one_shot = 0;
+	size_t num_bytes;
+	size_t copy_bytes;
+	u32 read_index;
+	u8 *data;
+	int err = 0;
+
+	if (sti7109->video_capture == VIDEO_CAPTURE_ONE_SHOT)
+		one_shot = 1;
+
+	/* put a v4l2_pix_format header at the beginning of the returned data */
+	memset(&pix_format, 0, sizeof(pix_format));
+	pix_format.width	= stream_params->samples;
+	pix_format.height	= stream_params->lines;
+	pix_format.pixelformat	= V4L2_PIX_FMT_UYVY;
+	pix_format.bytesperline	= stream_params->pitch;
+	pix_format.sizeimage	= stream_params->lines * stream_params->pitch;
+
+	if (count > (sizeof(pix_format) + pix_format.sizeimage))
+		count = sizeof(pix_format) + pix_format.sizeimage;
+
+	if (count < sizeof(pix_format)) {
+		err = -EFAULT;
+		goto out;
+	}
+
+	saa716x_vip_start(saa716x, 0, one_shot, stream_params);
+	/* Sleep long enough to be sure to capture at least one frame.
+	   TODO: Change this in a way that it just waits the required time. */
+	msleep(100);
+	saa716x_vip_stop(saa716x, 0);
+
+	read_index = saa716x->vip[0].read_index;
+	if ((stream_params->stream_flags & VIP_INTERLACED) &&
+	    (stream_params->stream_flags & VIP_ODD_FIELD) &&
+	    (stream_params->stream_flags & VIP_EVEN_FIELD)) {
+		read_index = read_index & ~1;
+		read_index = (read_index + 7) & 7;
+		read_index = read_index / 2;
+	} else {
+		read_index = (read_index + 7) & 7;
+	}
+
+	if (copy_to_user((void __user *)buf, &pix_format, sizeof(pix_format))) {
+		err = -EFAULT;
+		goto out;
+	}
+	num_bytes = sizeof(pix_format);
+
+	copy_bytes = count - num_bytes;
+	if (copy_bytes > (SAA716x_PAGE_SIZE / 8 * SAA716x_PAGE_SIZE))
+		copy_bytes = SAA716x_PAGE_SIZE / 8 * SAA716x_PAGE_SIZE;
+	data = (u8 *)saa716x->vip[0].dma_buf[0][read_index].mem_virt;
+	if (copy_to_user((void __user *)(buf + num_bytes), data, copy_bytes)) {
+		err = -EFAULT;
+		goto out;
+	}
+	num_bytes += copy_bytes;
+	if (saa716x->vip[0].dual_channel &&
+	    count - num_bytes > 0) {
+		copy_bytes = count - num_bytes;
+		if (copy_bytes > (SAA716x_PAGE_SIZE / 8 * SAA716x_PAGE_SIZE))
+			copy_bytes = SAA716x_PAGE_SIZE / 8 * SAA716x_PAGE_SIZE;
+		data = (u8 *)saa716x->vip[0].dma_buf[1][read_index].mem_virt;
+		if (copy_to_user((void __user *)(buf + num_bytes), data,
+				 copy_bytes)) {
+			err = -EFAULT;
+			goto out;
+		}
+		num_bytes += copy_bytes;
+	}
+
+	return num_bytes;
+
+out:
+	return err;
+}
+
+static ssize_t dvb_video_read(struct file *file, char __user *buf,
+			      size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev	= file->private_data;
+	struct sti7109_dev *sti7109	= dvbdev->priv;
+	struct vip_stream_params stream_params;
+	ssize_t ret = -ENODATA;
+
+	if ((file->f_flags & O_ACCMODE) == O_WRONLY)
+		return -EPERM;
+
+	mutex_lock(&sti7109->video_lock);
+
+	if (sti7109->video_capture != VIDEO_CAPTURE_OFF) {
+		if (video_vip_get_stream_params(&stream_params,
+						sti7109->video_format) == 0) {
+			ret = video_vip_read(sti7109, &stream_params,
+					     buf, count);
+		}
+	}
+
+	mutex_unlock(&sti7109->video_lock);
+	return ret;
+}
+
+#define FREE_COND_TS (dvb_ringbuffer_free(&sti7109->tsout) >= TS_SIZE)
+
+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 sti7109_dev *sti7109	= dvbdev->priv;
+	struct saa716x_dev *saa716x	= sti7109->dev;
+	unsigned long todo = count;
+	int ringbuffer_avail;
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+		return -EPERM;
+/*
+	if (av7110->videostate.stream_source != VIDEO_SOURCE_MEMORY)
+		return -EPERM;
+*/
+	if (sti7109->tsout_stat == TSOUT_STAT_RESET)
+		return count;
+
+	if ((file->f_flags & O_NONBLOCK) && !FREE_COND_TS)
+		return -EWOULDBLOCK;
+
+	while (todo >= TS_SIZE) {
+		if (!FREE_COND_TS) {
+			if (file->f_flags & O_NONBLOCK)
+				break;
+			if (wait_event_interruptible(sti7109->tsout.queue, FREE_COND_TS))
+				break;
+		}
+		ringbuffer_write_user(&sti7109->tsout, buf, TS_SIZE);
+		todo -= TS_SIZE;
+		buf += TS_SIZE;
+	}
+
+	ringbuffer_avail = dvb_ringbuffer_avail(&sti7109->tsout);
+	spin_lock(&sti7109->tsout.lock);
+	if ((sti7109->tsout_stat == TSOUT_STAT_FILL) &&
+	    (ringbuffer_avail > TSOUT_LEVEL_FILL)) {
+		sti7109->tsout_stat = TSOUT_STAT_RUN;
+		SAA716x_EPWR(PHI_1, FPGA_ADDR_FIFO_CTRL, FPGA_FIFO_CTRL_IE | FPGA_FIFO_CTRL_RUN);
+	}
+	else if ((sti7109->tsout_stat == TSOUT_STAT_WAIT) &&
+	         (ringbuffer_avail > TSOUT_LEVEL_HIGH)) {
+		sti7109->tsout_stat = TSOUT_STAT_RUN;
+		queue_work(sti7109->fifo_workq, &sti7109->fifo_work);
+	}
+	spin_unlock(&sti7109->tsout.lock);
+
+	return count - todo;
+}
+
+static unsigned int dvb_video_poll(struct file *file, poll_table *wait)
+{
+	struct dvb_device *dvbdev	= file->private_data;
+	struct sti7109_dev *sti7109	= dvbdev->priv;
+	unsigned int mask = 0;
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+		poll_wait(file, &sti7109->tsout.queue, wait);
+
+		if (FREE_COND_TS)
+			mask |= (POLLOUT | POLLWRNORM);
+	}
+
+	return mask;
+}
+
+static int do_dvb_video_ioctl(struct dvb_device *dvbdev,
+			      unsigned int cmd, void *parg)
+{
+	struct sti7109_dev *sti7109	= dvbdev->priv;
+	struct saa716x_dev *saa716x	= sti7109->dev;
+	int ret = 0;
+
+	switch (cmd) {
+	case VIDEO_SELECT_SOURCE:
+	{
+		video_stream_source_t stream_source;
+
+		stream_source = (video_stream_source_t) parg;
+		if (stream_source == VIDEO_SOURCE_DEMUX) {
+			/* stop and reset FIFO 1 */
+			spin_lock(&sti7109->tsout.lock);
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_FIFO_CTRL, FPGA_FIFO_CTRL_RESET);
+			sti7109->tsout_stat = TSOUT_STAT_RESET;
+			spin_unlock(&sti7109->tsout.lock);
+			break;
+		}
+		/* fall through */
+	}
+	case VIDEO_CLEAR_BUFFER:
+	{
+		/* reset FIFO 1 */
+		spin_lock(&sti7109->tsout.lock);
+		SAA716x_EPWR(PHI_1, FPGA_ADDR_FIFO_CTRL, FPGA_FIFO_CTRL_RESET);
+		sti7109->tsout_stat = TSOUT_STAT_RESET;
+		spin_unlock(&sti7109->tsout.lock);
+		msleep(50);
+		cancel_work_sync(&sti7109->fifo_work);
+		/* start FIFO 1 */
+		spin_lock(&sti7109->tsout.lock);
+		SAA716x_EPWR(PHI_1, FPGA_ADDR_FIFO_CTRL, 0);
+		sti7109->tsout.pread = sti7109->tsout.pwrite = 0;//dvb_ringbuffer_reset(&sti7109->tsout);
+        	sti7109->tsout_stat = TSOUT_STAT_FILL;
+		spin_unlock(&sti7109->tsout.lock);
+		wake_up(&sti7109->tsout.queue);
+		break;
+	}
+	case VIDEO_GET_PTS:
+	{
+		*(u64 *)parg = sti7109->video_pts;
+		break;
+	}
+	case VIDEO_GET_SIZE:
+	{
+		ret = sti7109_cmd_get_video_format(sti7109, (video_size_t *) parg);
+		break;
+	}
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+	return ret;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) && !defined(EXPERIMENTAL_TREE)
+static int dvb_video_ioctl(struct inode *inode, struct file *file,
+#else
+static long dvb_video_ioctl(struct file *file,
+#endif
+			   unsigned int cmd, unsigned long arg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+
+	if (!dvbdev)
+		return -ENODEV;
+
+	return saa716x_usercopy (dvbdev, cmd, arg, do_dvb_video_ioctl);
+}
+
+static struct file_operations dvb_video_fops = {
+	.owner		= THIS_MODULE,
+	.read		= dvb_video_read,
+	.write		= dvb_video_write,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) && !defined(EXPERIMENTAL_TREE)
+	.ioctl		= dvb_video_ioctl,
+#else
+	.unlocked_ioctl	= dvb_video_ioctl,
+#endif
+	.open		= dvb_generic_open,
+	.release	= dvb_generic_release,
+	.poll		= dvb_video_poll,
+};
+
+static struct dvb_device dvbdev_video = {
+	.priv		= NULL,
+	.users		= 5,
+	.readers	= 5,
+	.writers	= 1,
+	.fops		= &dvb_video_fops,
+	.kernel_ioctl	= NULL,
+};
+
+static int saa716x_ff_video_exit(struct saa716x_dev *saa716x)
+{
+	struct sti7109_dev *sti7109 = saa716x->priv;
+
+	if (sti7109->video_capture != VIDEO_CAPTURE_OFF)
+		saa716x_vip_exit(saa716x, 0);
+
+	cancel_work_sync(&sti7109->fifo_work);
+	destroy_workqueue(sti7109->fifo_workq);
+	dvb_unregister_device(sti7109->video_dev);
+	return 0;
+}
+
+static int saa716x_ff_video_init(struct saa716x_dev *saa716x)
+{
+	struct saa716x_adapter *saa716x_adap	= saa716x->saa716x_adap;
+	struct sti7109_dev *sti7109		= saa716x->priv;
+
+	dvb_ringbuffer_init(&sti7109->tsout, sti7109->iobuf, TSOUT_LEN);
+
+	dvb_register_device(&saa716x_adap->dvb_adapter,
+			    &sti7109->video_dev,
+			    &dvbdev_video,
+			    sti7109,
+			    DVB_DEVICE_VIDEO
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
+			    , 0
+#endif
+			    );
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+	sti7109->fifo_workq = create_singlethread_workqueue("saa716x_fifo_wq");
+#else
+	sti7109->fifo_workq = alloc_workqueue("saa716x_fifo_wq%d", WQ_UNBOUND, 1, SAA716x_DEV);
+#endif
+	INIT_WORK(&sti7109->fifo_work, fifo_worker);
+
+	if (sti7109->video_capture != VIDEO_CAPTURE_OFF)
+		saa716x_vip_init(saa716x, 0, video_vip_worker);
+
+	return 0;
+}
+
+static int saa716x_ff_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+{
+	struct saa716x_dev *saa716x;
+	struct sti7109_dev *sti7109;
+	int err = 0;
+	u32 value;
+	unsigned long timeout;
+
+	saa716x = kzalloc(sizeof (struct saa716x_dev), GFP_KERNEL);
+	if (saa716x == NULL) {
+		printk(KERN_ERR "saa716x_budget_pci_probe ERROR: out of memory\n");
+		err = -ENOMEM;
+		goto fail0;
+	}
+
+	saa716x->verbose	= verbose;
+	saa716x->int_type	= int_type;
+	saa716x->pdev		= pdev;
+	saa716x->config		= (struct saa716x_config *) pci_id->driver_data;
+
+	err = saa716x_pci_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x PCI Initialization failed");
+		goto fail1;
+	}
+
+	err = saa716x_cgu_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x CGU Init failed");
+		goto fail1;
+	}
+
+	err = saa716x_core_boot(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x Core Boot failed");
+		goto fail2;
+	}
+	dprintk(SAA716x_DEBUG, 1, "SAA716x Core Boot Success");
+
+	err = saa716x_msi_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x MSI Init failed");
+		goto fail2;
+	}
+
+	err = saa716x_jetpack_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x Jetpack core initialization failed");
+		goto fail2;
+	}
+
+	err = saa716x_i2c_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x I2C Initialization failed");
+		goto fail3;
+	}
+
+	saa716x_gpio_init(saa716x);
+
+	/* prepare the sti7109 device struct */
+	sti7109 = kzalloc(sizeof(struct sti7109_dev), GFP_KERNEL);
+	if (!sti7109) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x: out of memory");
+		goto fail3;
+	}
+
+	sti7109->dev = saa716x;
+	saa716x->priv = sti7109;
+
+	sti7109->iobuf = vmalloc(TSOUT_LEN + MAX_DATA_LEN);
+	if (!sti7109->iobuf)
+		goto fail4;
+
+	err = saa716x_ff_phi_init(saa716x);
+	if (err)
+		goto fail4;
+
+	sti7109_cmd_init(sti7109);
+
+	sti7109->video_capture = video_capture;
+	mutex_init(&sti7109->video_lock);
+
+	sti7109->int_count_enable = int_count_enable;
+	sti7109->total_int_count = 0;
+	memset(sti7109->vi_int_count, 0, sizeof(sti7109->vi_int_count));
+	memset(sti7109->fgpi_int_count, 0, sizeof(sti7109->fgpi_int_count));
+	memset(sti7109->i2c_int_count, 0, sizeof(sti7109->i2c_int_count));
+	sti7109->ext_int_total_count = 0;
+	memset(sti7109->ext_int_source_count, 0, sizeof(sti7109->ext_int_source_count));
+	sti7109->last_int_ticks = jiffies;
+
+	saa716x_gpio_set_output(saa716x, TT_PREMIUM_GPIO_POWER_ENABLE);
+	saa716x_gpio_set_output(saa716x, TT_PREMIUM_GPIO_RESET_BACKEND);
+	saa716x_gpio_set_output(saa716x, TT_PREMIUM_GPIO_FPGA_CS0);
+	saa716x_gpio_set_mode(saa716x, TT_PREMIUM_GPIO_FPGA_CS0, 1);
+	saa716x_gpio_set_output(saa716x, TT_PREMIUM_GPIO_FPGA_CS1);
+	saa716x_gpio_set_mode(saa716x, TT_PREMIUM_GPIO_FPGA_CS1, 1);
+	saa716x_gpio_set_output(saa716x, TT_PREMIUM_GPIO_FPGA_CS2);
+	saa716x_gpio_set_mode(saa716x, TT_PREMIUM_GPIO_FPGA_CS2, 1);
+	saa716x_gpio_set_output(saa716x, TT_PREMIUM_GPIO_FPGA_PROGRAMN);
+	saa716x_gpio_set_input(saa716x, TT_PREMIUM_GPIO_FPGA_DONE);
+	saa716x_gpio_set_input(saa716x, TT_PREMIUM_GPIO_FPGA_INITN);
+
+	/* hold ST in reset */
+	saa716x_gpio_write(saa716x, TT_PREMIUM_GPIO_RESET_BACKEND, 0);
+
+	/* enable board power */
+	saa716x_gpio_write(saa716x, TT_PREMIUM_GPIO_POWER_ENABLE, 1);
+	msleep(100);
+
+	err = saa716x_ff_fpga_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x FF FPGA Initialization failed");
+		goto fail5;
+	}
+
+	/* reconfigure PHI based on FPGA version and phi_mode */
+	saa716x_ff_phi_config(saa716x);
+
+	/* configure TS muxer */
+	if (sti7109->fpga_version < 0x110) {
+		/* select FIFO 1 for TS mux 3 */
+		SAA716x_EPWR(PHI_1, FPGA_ADDR_TSR_MUX3, 4);
+	} else {
+		/* select FIFO 1 for TS mux 3 */
+		SAA716x_EPWR(PHI_1, FPGA_ADDR_TSR_MUX3, 1);
+	}
+
+	/* enable interrupts from ST7109 -> PC */
+	SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICTRL, 0x3);
+
+	value = SAA716x_EPRD(MSI, MSI_CONFIG33);
+	value &= 0xFCFFFFFF;
+	value |= MSI_INT_POL_EDGE_FALL;
+	SAA716x_EPWR(MSI, MSI_CONFIG33, value);
+	SAA716x_EPWR(MSI, MSI_INT_ENA_SET_H, MSI_INT_EXTINT_0);
+
+	/* enable tuner reset */
+	SAA716x_EPWR(PHI_1, FPGA_ADDR_PIO_CTRL, 0);
+	msleep(50);
+	/* disable tuner reset */
+	SAA716x_EPWR(PHI_1, FPGA_ADDR_PIO_CTRL, 1);
+
+	err = saa716x_ff_st7109_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x FF STi7109 initialization failed");
+		goto fail5;
+	}
+
+	err = saa716x_dump_eeprom(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x EEPROM dump failed");
+	}
+
+	err = saa716x_eeprom_data(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x EEPROM dump failed");
+	}
+
+	/* enable FGPI2 and FGPI3 for TS inputs */
+	SAA716x_EPWR(GREG, GREG_VI_CTRL, 0x0689F04);
+	SAA716x_EPWR(GREG, GREG_FGPI_CTRL, 0x280);
+	SAA716x_EPWR(GREG, GREG_VIDEO_IN_CTRL, 0xC0);
+
+	err = saa716x_dvb_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x DVB initialization failed");
+		goto fail6;
+	}
+
+	/* wait a maximum of 10 seconds for the STi7109 to boot */
+	timeout = 10 * HZ;
+	timeout = wait_event_interruptible_timeout(sti7109->boot_finish_wq,
+						   sti7109->boot_finished == 1,
+						   timeout);
+
+	if (timeout == -ERESTARTSYS || sti7109->boot_finished == 0) {
+		if (timeout == -ERESTARTSYS) {
+			/* a signal arrived */
+			goto fail6;
+		}
+		dprintk(SAA716x_ERROR, 1, "timed out waiting for boot finish");
+		err = -1;
+		goto fail6;
+	}
+	dprintk(SAA716x_INFO, 1, "STi7109 finished booting");
+
+	err = saa716x_ff_video_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x FF VIDEO initialization failed");
+		goto fail7;
+	}
+
+	err = saa716x_ff_audio_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x FF AUDIO initialization failed");
+		goto fail8;
+	}
+
+	err = saa716x_ff_osd_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x FF OSD initialization failed");
+		goto fail9;
+	}
+
+	err = sti7109_cmd_get_fw_version(sti7109, &sti7109->fw_version);
+	if (!err) {
+		printk(KERN_INFO "SAA716x FF firmware version %d.%d.%d\n",
+			(sti7109->fw_version >> 16) & 0xFF,
+			(sti7109->fw_version >> 8) & 0xFF,
+			sti7109->fw_version & 0xFF);
+	}
+
+	err = saa716x_ir_init(saa716x);
+	if (err)
+		goto fail9;
+
+	return 0;
+
+fail9:
+	saa716x_ff_osd_exit(saa716x);
+fail8:
+	saa716x_ff_audio_exit(saa716x);
+fail7:
+	saa716x_ff_video_exit(saa716x);
+fail6:
+	saa716x_dvb_exit(saa716x);
+fail5:
+	SAA716x_EPWR(MSI, MSI_INT_ENA_CLR_H, MSI_INT_EXTINT_0);
+
+	/* disable board power */
+	saa716x_gpio_write(saa716x, TT_PREMIUM_GPIO_POWER_ENABLE, 0);
+
+	vfree(sti7109->iobuf);
+fail4:
+	saa716x_ff_phi_exit(saa716x);
+
+	kfree(sti7109);
+fail3:
+	saa716x_i2c_exit(saa716x);
+fail2:
+	saa716x_pci_exit(saa716x);
+fail1:
+	kfree(saa716x);
+fail0:
+	return err;
+}
+
+static void saa716x_ff_pci_remove(struct pci_dev *pdev)
+{
+	struct saa716x_dev *saa716x = pci_get_drvdata(pdev);
+	struct sti7109_dev *sti7109 = saa716x->priv;
+
+	saa716x_ir_exit(saa716x);
+
+	saa716x_ff_osd_exit(saa716x);
+
+	saa716x_ff_audio_exit(saa716x);
+
+	saa716x_ff_video_exit(saa716x);
+
+	saa716x_dvb_exit(saa716x);
+
+	SAA716x_EPWR(MSI, MSI_INT_ENA_CLR_H, MSI_INT_EXTINT_0);
+
+	/* disable board power */
+	saa716x_gpio_write(saa716x, TT_PREMIUM_GPIO_POWER_ENABLE, 0);
+
+	vfree(sti7109->iobuf);
+
+	saa716x_ff_phi_exit(saa716x);
+
+	saa716x->priv = NULL;
+	kfree(sti7109);
+
+	saa716x_i2c_exit(saa716x);
+	saa716x_pci_exit(saa716x);
+	kfree(saa716x);
+}
+
+static void demux_worker(unsigned long data)
+{
+	struct saa716x_fgpi_stream_port *fgpi_entry = (struct saa716x_fgpi_stream_port *)data;
+	struct saa716x_dev *saa716x = fgpi_entry->saa716x;
+	struct dvb_demux *demux;
+	u32 fgpi_index;
+	u32 i;
+	u32 write_index;
+
+	fgpi_index = fgpi_entry->dma_channel - 6;
+	demux = NULL;
+	for (i = 0; i < saa716x->config->adapters; i++) {
+		if (saa716x->config->adap_config[i].ts_port == fgpi_index) {
+			demux = &saa716x->saa716x_adap[i].demux;
+			break;
+		}
+	}
+	if (demux == NULL) {
+		printk(KERN_ERR "%s: unexpected channel %u\n",
+		       __func__, fgpi_entry->dma_channel);
+		return;
+	}
+
+	write_index = saa716x_fgpi_get_write_index(saa716x, fgpi_index);
+	if (write_index < 0)
+		return;
+
+	dprintk(SAA716x_DEBUG, 1, "dma buffer = %d", write_index);
+
+	if (write_index == fgpi_entry->read_index) {
+		printk(KERN_DEBUG "%s: called but nothing to do\n", __func__);
+		return;
+	}
+
+	do {
+		u8 *data = (u8 *)fgpi_entry->dma_buf[fgpi_entry->read_index].mem_virt;
+
+		pci_dma_sync_sg_for_cpu(saa716x->pdev,
+			fgpi_entry->dma_buf[fgpi_entry->read_index].sg_list,
+			fgpi_entry->dma_buf[fgpi_entry->read_index].list_len,
+			PCI_DMA_FROMDEVICE);
+
+		dvb_dmx_swfilter(demux, data, 348 * 188);
+
+		fgpi_entry->read_index = (fgpi_entry->read_index + 1) & 7;
+	} while (write_index != fgpi_entry->read_index);
+}
+
+static irqreturn_t saa716x_ff_pci_irq(int irq, void *dev_id)
+{
+	struct saa716x_dev *saa716x	= (struct saa716x_dev *) dev_id;
+	struct sti7109_dev *sti7109;
+	u32 msiStatusL;
+	u32 msiStatusH;
+	u32 phiISR;
+
+	if (unlikely(saa716x == NULL)) {
+		printk("%s: saa716x=NULL", __func__);
+		return IRQ_NONE;
+	}
+	sti7109 = saa716x->priv;
+	if (unlikely(sti7109 == NULL)) {
+		printk("%s: sti7109=NULL", __func__);
+		return IRQ_NONE;
+	}
+	if (sti7109->int_count_enable)
+		sti7109->total_int_count++;
+#if 0
+	dprintk(SAA716x_DEBUG, 1, "VI STAT 0=<%02x> 1=<%02x>, CTL 1=<%02x> 2=<%02x>",
+		SAA716x_EPRD(VI0, INT_STATUS),
+		SAA716x_EPRD(VI1, INT_STATUS),
+		SAA716x_EPRD(VI0, INT_ENABLE),
+		SAA716x_EPRD(VI1, INT_ENABLE));
+
+	dprintk(SAA716x_DEBUG, 1, "FGPI STAT 0=<%02x> 1=<%02x>, CTL 1=<%02x> 2=<%02x>",
+		SAA716x_EPRD(FGPI0, INT_STATUS),
+		SAA716x_EPRD(FGPI1, INT_STATUS),
+		SAA716x_EPRD(FGPI0, INT_ENABLE),
+		SAA716x_EPRD(FGPI0, INT_ENABLE));
+
+	dprintk(SAA716x_DEBUG, 1, "FGPI STAT 2=<%02x> 3=<%02x>, CTL 2=<%02x> 3=<%02x>",
+		SAA716x_EPRD(FGPI2, INT_STATUS),
+		SAA716x_EPRD(FGPI3, INT_STATUS),
+		SAA716x_EPRD(FGPI2, INT_ENABLE),
+		SAA716x_EPRD(FGPI3, INT_ENABLE));
+
+	dprintk(SAA716x_DEBUG, 1, "AI STAT 0=<%02x> 1=<%02x>, CTL 0=<%02x> 1=<%02x>",
+		SAA716x_EPRD(AI0, AI_STATUS),
+		SAA716x_EPRD(AI1, AI_STATUS),
+		SAA716x_EPRD(AI0, AI_CTL),
+		SAA716x_EPRD(AI1, AI_CTL));
+
+	dprintk(SAA716x_DEBUG, 1, "MSI STAT L=<%02x> H=<%02x>, CTL L=<%02x> H=<%02x>",
+		SAA716x_EPRD(MSI, MSI_INT_STATUS_L),
+		SAA716x_EPRD(MSI, MSI_INT_STATUS_H),
+		SAA716x_EPRD(MSI, MSI_INT_ENA_L),
+		SAA716x_EPRD(MSI, MSI_INT_ENA_H));
+
+	dprintk(SAA716x_DEBUG, 1, "I2C STAT 0=<%02x> 1=<%02x>, CTL 0=<%02x> 1=<%02x>",
+		SAA716x_EPRD(I2C_A, INT_STATUS),
+		SAA716x_EPRD(I2C_B, INT_STATUS),
+		SAA716x_EPRD(I2C_A, INT_ENABLE),
+		SAA716x_EPRD(I2C_B, INT_ENABLE));
+
+	dprintk(SAA716x_DEBUG, 1, "DCS STAT=<%02x>, CTL=<%02x>",
+		SAA716x_EPRD(DCS, DCSC_INT_STATUS),
+		SAA716x_EPRD(DCS, DCSC_INT_ENABLE));
+#endif
+	msiStatusL = SAA716x_EPRD(MSI, MSI_INT_STATUS_L);
+	SAA716x_EPWR(MSI, MSI_INT_STATUS_CLR_L, msiStatusL);
+	msiStatusH = SAA716x_EPRD(MSI, MSI_INT_STATUS_H);
+	SAA716x_EPWR(MSI, MSI_INT_STATUS_CLR_H, msiStatusH);
+
+	if (msiStatusL) {
+		if (msiStatusL & MSI_INT_TAGACK_VI0_0) {
+			if (sti7109->int_count_enable)
+				sti7109->vi_int_count[0]++;
+			tasklet_schedule(&saa716x->vip[0].tasklet);
+		}
+		if (msiStatusL & MSI_INT_TAGACK_FGPI_2) {
+			if (sti7109->int_count_enable)
+				sti7109->fgpi_int_count[2]++;
+			tasklet_schedule(&saa716x->fgpi[2].tasklet);
+		}
+		if (msiStatusL & MSI_INT_TAGACK_FGPI_3) {
+			if (sti7109->int_count_enable)
+				sti7109->fgpi_int_count[3]++;
+			tasklet_schedule(&saa716x->fgpi[3].tasklet);
+		}
+	}
+	if (msiStatusH) {
+		//dprintk(SAA716x_INFO, 1, "msiStatusH: %08X", msiStatusH);
+	}
+
+	if (msiStatusH & MSI_INT_I2CINT_0) {
+		if (sti7109->int_count_enable)
+			sti7109->i2c_int_count[0]++;
+		saa716x->i2c[0].i2c_op = 0;
+		wake_up(&saa716x->i2c[0].i2c_wq);
+	}
+	if (msiStatusH & MSI_INT_I2CINT_1) {
+		if (sti7109->int_count_enable)
+			sti7109->i2c_int_count[1]++;
+		saa716x->i2c[1].i2c_op = 0;
+		wake_up(&saa716x->i2c[1].i2c_wq);
+	}
+
+	if (msiStatusH & MSI_INT_EXTINT_0) {
+
+		phiISR = SAA716x_EPRD(PHI_1, FPGA_ADDR_EMI_ISR);
+		//dprintk(SAA716x_INFO, 1, "interrupt status register: %08X", phiISR);
+
+		if (sti7109->int_count_enable) {
+			int i;
+			sti7109->ext_int_total_count++;
+			for (i = 0; i < 16; i++)
+				if (phiISR & (1 << i))
+					sti7109->ext_int_source_count[i]++;
+		}
+
+		if (phiISR & ISR_CMD_MASK) {
+
+			u32 value;
+			u32 length;
+			/*dprintk(SAA716x_INFO, 1, "CMD interrupt source");*/
+
+			value = SAA716x_EPRD(PHI_1, ADDR_CMD_DATA);
+			value = __cpu_to_be32(value);
+			length = (value >> 16) + 2;
+
+			/*dprintk(SAA716x_INFO, 1, "CMD length: %d", length);*/
+
+			if (length > MAX_RESULT_LEN) {
+				dprintk(SAA716x_ERROR, 1, "CMD length %d > %d", length, MAX_RESULT_LEN);
+				length = MAX_RESULT_LEN;
+			}
+
+			saa716x_ff_phi_read(saa716x, ADDR_CMD_DATA, sti7109->result_data, length);
+			sti7109->result_len = length;
+			sti7109->result_avail = 1;
+			wake_up(&sti7109->result_avail_wq);
+
+			phiISR &= ~ISR_CMD_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_CMD_MASK);
+		}
+
+		if (phiISR & ISR_READY_MASK) {
+			/*dprintk(SAA716x_INFO, 1, "READY interrupt source");*/
+			sti7109->cmd_ready = 1;
+			wake_up(&sti7109->cmd_ready_wq);
+			phiISR &= ~ISR_READY_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_READY_MASK);
+		}
+
+		if (phiISR & ISR_OSD_CMD_MASK) {
+
+			u32 value;
+			u32 length;
+			/*dprintk(SAA716x_INFO, 1, "OSD CMD interrupt source");*/
+
+			value = SAA716x_EPRD(PHI_1, ADDR_OSD_CMD_DATA);
+			value = __cpu_to_be32(value);
+			length = (value >> 16) + 2;
+
+			/*dprintk(SAA716x_INFO, 1, "OSD CMD length: %d", length);*/
+
+			if (length > MAX_RESULT_LEN) {
+				dprintk(SAA716x_ERROR, 1, "OSD CMD length %d > %d", length, MAX_RESULT_LEN);
+				length = MAX_RESULT_LEN;
+			}
+
+			saa716x_ff_phi_read(saa716x, ADDR_OSD_CMD_DATA, sti7109->osd_result_data, length);
+			sti7109->osd_result_len = length;
+			sti7109->osd_result_avail = 1;
+			wake_up(&sti7109->osd_result_avail_wq);
+
+			phiISR &= ~ISR_OSD_CMD_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_OSD_CMD_MASK);
+		}
+
+		if (phiISR & ISR_OSD_READY_MASK) {
+			/*dprintk(SAA716x_INFO, 1, "OSD_READY interrupt source");*/
+			sti7109->osd_cmd_ready = 1;
+			wake_up(&sti7109->osd_cmd_ready_wq);
+			phiISR &= ~ISR_OSD_READY_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_OSD_READY_MASK);
+		}
+
+		if (phiISR & ISR_BLOCK_MASK) {
+			/*dprintk(SAA716x_INFO, 1, "BLOCK interrupt source");*/
+			sti7109->block_done = 1;
+			wake_up(&sti7109->block_done_wq);
+			phiISR &= ~ISR_BLOCK_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_BLOCK_MASK);
+		}
+
+		if (phiISR & ISR_DATA_MASK) {
+			/*dprintk(SAA716x_INFO, 1, "DATA interrupt source");*/
+			sti7109->data_ready = 1;
+			wake_up(&sti7109->data_ready_wq);
+			phiISR &= ~ISR_DATA_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_DATA_MASK);
+		}
+
+		if (phiISR & ISR_BOOT_FINISH_MASK) {
+			/*dprintk(SAA716x_INFO, 1, "BOOT FINISH interrupt source");*/
+			sti7109->boot_finished = 1;
+			wake_up(&sti7109->boot_finish_wq);
+			phiISR &= ~ISR_BOOT_FINISH_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_BOOT_FINISH_MASK);
+		}
+
+		if (phiISR & ISR_AUDIO_PTS_MASK) {
+			u8 data[8];
+
+			saa716x_ff_phi_read(saa716x, ADDR_AUDIO_PTS, data, 8);
+			sti7109->audio_pts = (((u64) data[3] & 0x01) << 32)
+					    | ((u64) data[4] << 24)
+					    | ((u64) data[5] << 16)
+					    | ((u64) data[6] << 8)
+					    | ((u64) data[7]);
+
+			phiISR &= ~ISR_AUDIO_PTS_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_AUDIO_PTS_MASK);
+
+			/*dprintk(SAA716x_INFO, 1, "AUDIO PTS: %llX", sti7109->audio_pts);*/
+		}
+
+		if (phiISR & ISR_VIDEO_PTS_MASK) {
+			u8 data[8];
+
+			saa716x_ff_phi_read(saa716x, ADDR_VIDEO_PTS, data, 8);
+			sti7109->video_pts = (((u64) data[3] & 0x01) << 32)
+					    | ((u64) data[4] << 24)
+					    | ((u64) data[5] << 16)
+					    | ((u64) data[6] << 8)
+					    | ((u64) data[7]);
+
+			phiISR &= ~ISR_VIDEO_PTS_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_VIDEO_PTS_MASK);
+
+			/*dprintk(SAA716x_INFO, 1, "VIDEO PTS: %llX", sti7109->video_pts);*/
+		}
+
+		if (phiISR & ISR_CURRENT_STC_MASK) {
+			u8 data[8];
+
+			saa716x_ff_phi_read(saa716x, ADDR_CURRENT_STC, data, 8);
+			sti7109->current_stc = (((u64) data[3] & 0x01) << 32)
+					      | ((u64) data[4] << 24)
+					      | ((u64) data[5] << 16)
+					      | ((u64) data[6] << 8)
+					      | ((u64) data[7]);
+
+			phiISR &= ~ISR_CURRENT_STC_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_CURRENT_STC_MASK);
+
+			/*dprintk(SAA716x_INFO, 1, "CURRENT STC: %llu", sti7109->current_stc);*/
+		}
+
+		if (phiISR & ISR_REMOTE_EVENT_MASK) {
+			u8 data[4];
+			u32 remote_event;
+
+			saa716x_ff_phi_read(saa716x, ADDR_REMOTE_EVENT, data, 4);
+			remote_event = (data[3] << 24)
+				     | (data[2] << 16)
+				     | (data[1] << 8)
+				     | (data[0]);
+			memset(data, 0, sizeof(data));
+			saa716x_ff_phi_write(saa716x, ADDR_REMOTE_EVENT, data, 4);
+
+			phiISR &= ~ISR_REMOTE_EVENT_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_REMOTE_EVENT_MASK);
+
+			if (remote_event == 0) {
+				dprintk(SAA716x_ERROR, 1, "REMOTE EVENT: %X ignored", remote_event);
+			} else {
+				dprintk(SAA716x_INFO, 1, "REMOTE EVENT: %X", remote_event);
+				saa716x_ir_handler(saa716x, remote_event);
+			}
+		}
+
+		if (phiISR & ISR_DVO_FORMAT_MASK) {
+			u8 data[4];
+			u32 format;
+
+			saa716x_ff_phi_read(saa716x, ADDR_DVO_FORMAT, data, 4);
+			format = (data[0] << 24)
+			       | (data[1] << 16)
+			       | (data[2] << 8)
+			       | (data[3]);
+
+			phiISR &= ~ISR_DVO_FORMAT_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_DVO_FORMAT_MASK);
+
+			dprintk(SAA716x_INFO, 1, "DVO FORMAT CHANGE: %u", format);
+			sti7109->video_format = format;
+		}
+
+		if (phiISR & ISR_LOG_MESSAGE_MASK) {
+			char message[SIZE_LOG_MESSAGE_DATA];
+
+			saa716x_ff_phi_read(saa716x, ADDR_LOG_MESSAGE, message,
+					 SIZE_LOG_MESSAGE_DATA);
+
+			phiISR &= ~ISR_LOG_MESSAGE_MASK;
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, ISR_LOG_MESSAGE_MASK);
+
+			dprintk(SAA716x_INFO, 1, "LOG MESSAGE: %.*s",
+				SIZE_LOG_MESSAGE_DATA, message);
+		}
+
+		if (phiISR & ISR_FIFO1_EMPTY_MASK) {
+			/*dprintk(SAA716x_INFO, 1, "FIFO EMPTY interrupt source");*/
+			/* clear FPGA_FIFO_CTRL_IE */
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_FIFO_CTRL, FPGA_FIFO_CTRL_RUN);
+			queue_work(sti7109->fifo_workq, &sti7109->fifo_work);
+			phiISR &= ~ISR_FIFO1_EMPTY_MASK;
+		}
+
+		if (phiISR) {
+			dprintk(SAA716x_INFO, 1, "unknown interrupt source");
+			SAA716x_EPWR(PHI_1, FPGA_ADDR_EMI_ICLR, phiISR);
+		}
+	}
+
+	if (sti7109->int_count_enable) {
+		if (jiffies - sti7109->last_int_ticks >= HZ) {
+			dprintk(SAA716x_INFO, 1,
+				"int count: t: %d, v: %d %d, f:%d %d %d %d, i:%d %d,"
+				"e: %d (%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d)",
+				sti7109->total_int_count,
+				sti7109->vi_int_count[0],
+				sti7109->vi_int_count[1],
+				sti7109->fgpi_int_count[0],
+				sti7109->fgpi_int_count[1],
+				sti7109->fgpi_int_count[2],
+				sti7109->fgpi_int_count[3],
+				sti7109->i2c_int_count[0],
+				sti7109->i2c_int_count[1],
+				sti7109->ext_int_total_count,
+				sti7109->ext_int_source_count[0],
+				sti7109->ext_int_source_count[1],
+				sti7109->ext_int_source_count[2],
+				sti7109->ext_int_source_count[3],
+				sti7109->ext_int_source_count[4],
+				sti7109->ext_int_source_count[5],
+				sti7109->ext_int_source_count[6],
+				sti7109->ext_int_source_count[7],
+				sti7109->ext_int_source_count[8],
+				sti7109->ext_int_source_count[9],
+				sti7109->ext_int_source_count[10],
+				sti7109->ext_int_source_count[11],
+				sti7109->ext_int_source_count[12],
+				sti7109->ext_int_source_count[13],
+				sti7109->ext_int_source_count[14],
+				sti7109->ext_int_source_count[15]);
+			sti7109->total_int_count = 0;
+			memset(sti7109->vi_int_count, 0, sizeof(sti7109->vi_int_count));
+			memset(sti7109->fgpi_int_count, 0, sizeof(sti7109->fgpi_int_count));
+			memset(sti7109->i2c_int_count, 0, sizeof(sti7109->i2c_int_count));
+			sti7109->ext_int_total_count = 0;
+			memset(sti7109->ext_int_source_count, 0, sizeof(sti7109->ext_int_source_count));
+			sti7109->last_int_ticks = jiffies;
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+#define SAA716x_MODEL_S2_6400_DUAL	"Technotrend S2 6400 Dual S2 Premium"
+#define SAA716x_DEV_S2_6400_DUAL	"2x DVB-S/S2 + Hardware decode"
+
+static struct stv090x_config tt6400_stv090x_config = {
+	.device			= STV0900,
+	.demod_mode		= STV090x_DUAL,
+	.clk_mode		= STV090x_CLK_EXT,
+
+	.xtal			= 13500000,
+	.address		= 0x68,
+
+	.ts1_mode		= STV090x_TSMODE_SERIAL_CONTINUOUS,
+	.ts2_mode		= STV090x_TSMODE_SERIAL_CONTINUOUS,
+	.ts1_clk		= 135000000,
+	.ts2_clk		= 135000000,
+
+	.repeater_level		= STV090x_RPTLEVEL_16,
+
+	.tuner_init		= 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 tt6400_stv6110x_config = {
+	.addr			= 0x60,
+	.refclk			= 27000000,
+	.clk_div		= 2,
+};
+
+static struct isl6423_config tt6400_isl6423_config[2] = {
+	{
+		.current_max		= SEC_CURRENT_515m,
+		.curlim			= SEC_CURRENT_LIM_ON,
+		.mod_extern		= 1,
+		.addr			= 0x09,
+	},
+	{
+		.current_max		= SEC_CURRENT_515m,
+		.curlim			= SEC_CURRENT_LIM_ON,
+		.mod_extern		= 1,
+		.addr			= 0x08,
+	}
+};
+
+
+static int saa716x_s26400_frontend_attach(struct saa716x_adapter *adapter, int count)
+{
+	struct saa716x_dev *saa716x	= adapter->saa716x;
+	struct saa716x_i2c *i2c		= saa716x->i2c;
+	struct i2c_adapter *i2c_adapter	= &i2c[SAA716x_I2C_BUS_A].i2c_adapter;
+
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) SAA716x frontend Init", count);
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) Device ID=%02x", count, saa716x->pdev->subsystem_device);
+
+	if (count == 0 || count == 1) {
+		adapter->fe = dvb_attach(stv090x_attach,
+					 &tt6400_stv090x_config,
+					 i2c_adapter,
+					 STV090x_DEMODULATOR_0 + count);
+
+		if (adapter->fe) {
+			struct stv6110x_devctl *ctl;
+			ctl = dvb_attach(stv6110x_attach,
+					 adapter->fe,
+					 &tt6400_stv6110x_config,
+					 i2c_adapter);
+
+			tt6400_stv090x_config.tuner_init	  = ctl->tuner_init;
+			tt6400_stv090x_config.tuner_sleep	  = ctl->tuner_sleep;
+			tt6400_stv090x_config.tuner_set_mode	  = ctl->tuner_set_mode;
+			tt6400_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
+			tt6400_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
+			tt6400_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+			tt6400_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+			tt6400_stv090x_config.tuner_set_bbgain	  = ctl->tuner_set_bbgain;
+			tt6400_stv090x_config.tuner_get_bbgain	  = ctl->tuner_get_bbgain;
+			tt6400_stv090x_config.tuner_set_refclk	  = ctl->tuner_set_refclk;
+			tt6400_stv090x_config.tuner_get_status	  = ctl->tuner_get_status;
+
+			if (count == 1) {
+				/* call the init function once to initialize
+				   tuner's clock output divider and demod's
+				   master clock */
+				/* The second tuner drives the STV0900 so
+				   call it only for adapter 1 */
+				if (adapter->fe->ops.init)
+					adapter->fe->ops.init(adapter->fe);
+			}
+
+			dvb_attach(isl6423_attach,
+				   adapter->fe,
+				   i2c_adapter,
+				   &tt6400_isl6423_config[count]);
+
+			if (adapter->fe->ops.sleep)
+				adapter->fe->ops.sleep(adapter->fe);
+		}
+	}
+	return 0;
+}
+
+static struct saa716x_config saa716x_s26400_config = {
+	.model_name		= SAA716x_MODEL_S2_6400_DUAL,
+	.dev_type		= SAA716x_DEV_S2_6400_DUAL,
+	.boot_mode		= SAA716x_EXT_BOOT,
+	.adapters		= 2,
+	.frontend_attach	= saa716x_s26400_frontend_attach,
+	.irq_handler		= saa716x_ff_pci_irq,
+	.i2c_rate		= SAA716x_I2C_RATE_100,
+	.i2c_mode		= SAA716x_I2C_MODE_IRQ_BUFFERED,
+
+	.adap_config		= {
+		{
+			/* Adapter 0 */
+			.ts_port = 2,
+			.worker = demux_worker
+		},{
+			/* Adapter 1 */
+			.ts_port = 3,
+			.worker = demux_worker
+		}
+	}
+};
+
+
+static struct pci_device_id saa716x_ff_pci_table[] = {
+
+	MAKE_ENTRY(TECHNOTREND, S2_6400_DUAL_S2_PREMIUM_DEVEL, SAA7160, &saa716x_s26400_config),  /* S2 6400 Dual development version */
+	MAKE_ENTRY(TECHNOTREND, S2_6400_DUAL_S2_PREMIUM_PROD, SAA7160, &saa716x_s26400_config), /* S2 6400 Dual production version */
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, saa716x_ff_pci_table);
+
+static struct pci_driver saa716x_ff_pci_driver = {
+	.name			= DRIVER_NAME,
+	.id_table		= saa716x_ff_pci_table,
+	.probe			= saa716x_ff_pci_probe,
+	.remove			= saa716x_ff_pci_remove,
+};
+
+static int __init saa716x_ff_init(void)
+{
+	return pci_register_driver(&saa716x_ff_pci_driver);
+}
+
+static void __exit saa716x_ff_exit(void)
+{
+	return pci_unregister_driver(&saa716x_ff_pci_driver);
+}
+
+module_init(saa716x_ff_init);
+module_exit(saa716x_ff_exit);
+
+MODULE_DESCRIPTION("SAA716x FF driver");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_LICENSE("GPL");
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_phi.c linux/drivers/media/pci/saa716x/saa716x_ff_phi.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_phi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_ff_phi.c	2017-08-11 23:31:25.829389469 +0200
@@ -0,0 +1,229 @@
+#include <linux/types.h>
+
+#include "saa716x_mod.h"
+#include "saa716x_phi.h"
+#include "saa716x_phi_reg.h"
+#include "saa716x_priv.h"
+
+#include "saa716x_ff.h"
+
+
+unsigned int phi_mode;
+module_param(phi_mode, int, 0644);
+MODULE_PARM_DESC(phi_mode, "phi access mode: 0 - default, slow single word accesses;"
+                                           " 1 - faster phi clock;"
+                                           " 2 - fastest mode, use write-combining");
+
+
+/* phi config register values: chip_select mask, ready mask, strobe time, cycle time */
+#define PHI_CONFIG(__cs, __ready, __strobe, __cycle) \
+	((__cs) + ((__ready) << 8) + ((__strobe) << 12) +  ((__cycle) << 20))
+
+#define PHI_0_0 (saa716x->mmio + PHI_0 + PHI_0_0_RW_0)
+#define PHI_1_0 (saa716x->mmio + PHI_1 + PHI_1_0_RW_0)
+#define PHI_1_1 (sti7109->mmio_uc)
+#define PHI_1_2 (sti7109->mmio_uc + 0x10000)
+#define PHI_1_3 (sti7109->mmio_uc + 0x20000)
+#define PHI_1_4 (sti7109->mmio_wc)
+#define PHI_1_5 (sti7109->mmio_wc + 0x10000)
+
+int saa716x_ff_phi_init(struct saa716x_dev *saa716x)
+{
+	struct pci_dev *pdev = saa716x->pdev;
+	struct sti7109_dev *sti7109 = saa716x->priv;
+	resource_size_t phi1_start = pci_resource_start(pdev, 0) + PHI_1;
+	int err;
+
+	if (pci_resource_len(pdev, 0) < 0x80000) {
+		dprintk(SAA716x_ERROR, 1, "wrong BAR0 length");
+		err = -ENODEV;
+		goto fail0;
+	}
+
+	/* skip first PHI window as it is already mapped */
+	sti7109->mmio_uc = ioremap_nocache(phi1_start + 0x10000, 0x30000);
+	if (!sti7109->mmio_uc) {
+		dprintk(SAA716x_ERROR, 1, "Mem PHI1 remap failed");
+		err = -ENODEV;
+		goto fail0;
+	}
+
+	sti7109->mmio_wc = ioremap_wc(phi1_start + 0x40000, 0x20000);
+	if (!sti7109->mmio_wc) {
+		dprintk(SAA716x_ERROR, 1, "Mem PHI1 WC remap failed");
+		err = -ENODEV;
+		goto fail0;
+	}
+
+	err = saa716x_phi_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x PHI Initialization failed");
+		goto fail1;
+	}
+
+	/* init PHI 0 to FIFO mode */
+	SAA716x_EPWR(PHI_0, PHI_0_MODE, PHI_FIFO_MODE);
+
+	/* init PHI 1 to SRAM mode, auto increment on */
+	SAA716x_EPWR(PHI_0, PHI_1_MODE, PHI_AUTO_INCREMENT);
+
+	/* ALE is active high */
+	SAA716x_EPWR(PHI_0, PHI_POLARITY, PHI_ALE_POL);
+	SAA716x_EPWR(PHI_0, PHI_TIMEOUT, 0x2a);
+
+	/* start with PHI settings that should work on all versions of the FPGA
+	   firmware */
+	if (phi_mode) {
+		/* fast PHI clock */
+		saa716x_set_clk(saa716x, CLK_DOMAIN_PHI, PLL_FREQ);
+
+		SAA716x_EPWR(PHI_0, PHI_0_0_CONFIG, PHI_CONFIG(0x02, 0, 6, 12));
+		SAA716x_EPWR(PHI_0, PHI_1_0_CONFIG, PHI_CONFIG(0x01, 0, 6, 10));
+	} else {
+		/* slow PHI clock */
+		saa716x_set_clk(saa716x, CLK_DOMAIN_PHI, PLL_FREQ / 2);
+
+		SAA716x_EPWR(PHI_0, PHI_0_0_CONFIG, PHI_CONFIG(0x02, 0, 3, 6));
+		SAA716x_EPWR(PHI_0, PHI_1_0_CONFIG, PHI_CONFIG(0x01, 0, 3, 5));
+	}
+
+	/* for the actual access use PHI mode 0 until saa716x_ff_phi_config
+	   gets called */
+	sti7109->phi_mode = 0;
+
+	return 0;
+
+fail1:
+	if (sti7109->mmio_wc)
+		iounmap(sti7109->mmio_wc);
+	if (sti7109->mmio_uc)
+		iounmap(sti7109->mmio_uc);
+fail0:
+	return err;
+}
+
+void saa716x_ff_phi_exit(struct saa716x_dev *saa716x)
+{
+	struct sti7109_dev *sti7109 = saa716x->priv;
+
+	if (sti7109->mmio_wc)
+		iounmap(sti7109->mmio_wc);
+	if (sti7109->mmio_uc)
+		iounmap(sti7109->mmio_uc);
+}
+
+void saa716x_ff_phi_config(struct saa716x_dev *saa716x)
+{
+	struct sti7109_dev *sti7109 = saa716x->priv;
+
+	if (sti7109->fpga_version < 0x110)
+		return;
+
+	if (phi_mode) {
+		/* 8k fifo window */
+		SAA716x_EPWR(PHI_0, PHI_0_0_CONFIG, PHI_CONFIG(0x02, 0, 3, 5));
+		SAA716x_EPWR(PHI_0, PHI_0_1_CONFIG, PHI_CONFIG(0x02, 0, 3, 5));
+		SAA716x_EPWR(PHI_0, PHI_0_2_CONFIG, PHI_CONFIG(0x02, 0, 3, 5));
+		SAA716x_EPWR(PHI_0, PHI_0_3_CONFIG, PHI_CONFIG(0x02, 0, 3, 5));
+
+		/* noncached slow read/write window, for single-word accesses */
+		SAA716x_EPWR(PHI_0, PHI_1_0_CONFIG, PHI_CONFIG(0x01, 0, 6, 10));
+		/* slower noncached read window */
+		SAA716x_EPWR(PHI_0, PHI_1_1_CONFIG, PHI_CONFIG(0x05, 0, 3, 8));
+		/* fast noncached read window */
+		SAA716x_EPWR(PHI_0, PHI_1_2_CONFIG, PHI_CONFIG(0x05, 0, 4, 6));
+		/* noncached write window */
+		SAA716x_EPWR(PHI_0, PHI_1_3_CONFIG, PHI_CONFIG(0x05, 0, 3, 5));
+		/* write-combining dpram window */
+		SAA716x_EPWR(PHI_0, PHI_1_4_CONFIG, PHI_CONFIG(0x05, 0, 3, 5));
+		/* write-combining fifo window */
+		SAA716x_EPWR(PHI_0, PHI_1_5_CONFIG, PHI_CONFIG(0x06, 0, 3, 5));
+	} else {
+		/* 8k fifo window */
+		SAA716x_EPWR(PHI_0, PHI_0_0_CONFIG, PHI_CONFIG(0x02, 0, 3, 6));
+		SAA716x_EPWR(PHI_0, PHI_0_1_CONFIG, PHI_CONFIG(0x02, 0, 3, 6));
+		SAA716x_EPWR(PHI_0, PHI_0_2_CONFIG, PHI_CONFIG(0x02, 0, 3, 6));
+		SAA716x_EPWR(PHI_0, PHI_0_3_CONFIG, PHI_CONFIG(0x02, 0, 3, 6));
+
+		/* noncached read/write windows */
+		SAA716x_EPWR(PHI_0, PHI_1_0_CONFIG, PHI_CONFIG(0x01, 0, 3, 5));
+		SAA716x_EPWR(PHI_0, PHI_1_1_CONFIG, PHI_CONFIG(0x01, 0, 3, 5));
+		SAA716x_EPWR(PHI_0, PHI_1_2_CONFIG, PHI_CONFIG(0x01, 0, 3, 5));
+		SAA716x_EPWR(PHI_0, PHI_1_3_CONFIG, PHI_CONFIG(0x05, 0, 3, 4));
+		/* write-combining dpram window */
+		SAA716x_EPWR(PHI_0, PHI_1_4_CONFIG, PHI_CONFIG(0x05, 0, 3, 4));
+		/* write-combining fifo window */
+		SAA716x_EPWR(PHI_0, PHI_1_5_CONFIG, PHI_CONFIG(0x06, 0, 3, 4));
+	}
+
+	sti7109->phi_mode = phi_mode;
+}
+
+void saa716x_ff_phi_write(struct saa716x_dev *saa716x,
+			  u32 address, const u8 * data, int length)
+{
+	struct sti7109_dev *sti7109 = saa716x->priv;
+	int i;
+
+	switch (sti7109->phi_mode) {
+	case 2:
+		memcpy(PHI_1_4 + address, data, (length+3) & ~3);
+		break;
+	case 1:
+		memcpy_toio(PHI_1_3 + address, data, (length+3) & ~3);
+		break;
+	default:
+		for (i = 0; i < length; i += 4) {
+			SAA716x_EPWR(PHI_1, address, *((u32 *) &data[i]));
+			address += 4;
+		}
+		break;
+	}
+}
+
+void saa716x_ff_phi_read(struct saa716x_dev *saa716x,
+			 u32 address, u8 * data, int length)
+{
+	struct sti7109_dev *sti7109 = saa716x->priv;
+	int i;
+
+	switch (sti7109->phi_mode) {
+	case 2:
+		memcpy(data, PHI_1_2 + address, (length+3) & ~3);
+		break;
+	case 1:
+		memcpy_fromio(data, PHI_1_1 + address, (length+3) & ~3);
+		break;
+	default:
+		for (i = 0; i < length; i += 4) {
+			*((u32 *) &data[i]) = SAA716x_EPRD(PHI_1, address);
+			address += 4;
+		}
+		break;
+	}
+}
+
+void saa716x_ff_phi_write_fifo(struct saa716x_dev *saa716x,
+			       const u8 * data, int length)
+{
+	struct sti7109_dev *sti7109 = saa716x->priv;
+	int i;
+
+	switch (sti7109->phi_mode) {
+	case 2:
+		/* special fifo access                                        */
+		/* first write data in arbitrary order, then commit fifo data */
+		memcpy(PHI_1_5, data, length);
+		wmb();
+		SAA716x_EPWR(PHI_1, FPGA_ADDR_FIFO_STAT, 0);
+		break;
+	case 1:
+		iowrite32_rep(PHI_0_0, data, length/4);
+		break;
+	default:
+		for (i = 0; i < length; i += 4) {
+			SAA716x_EPWR(PHI_0, PHI_0_0_RW_0, *((u32 *) &data[i]));
+		}
+		break;
+	}
+}
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_phi.h linux/drivers/media/pci/saa716x/saa716x_ff_phi.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_phi.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_ff_phi.h	2017-08-11 23:31:25.829389469 +0200
@@ -0,0 +1,14 @@
+#ifndef __SAA716x_FF_PHI_H
+#define __SAA716x_FF_PHI_H
+
+extern int saa716x_ff_phi_init(struct saa716x_dev *saa716x);
+extern void saa716x_ff_phi_exit(struct saa716x_dev *saa716x);
+extern void saa716x_ff_phi_config(struct saa716x_dev *saa716x);
+extern void saa716x_ff_phi_write(struct saa716x_dev *saa716x,
+				 u32 address, const u8 * data, int length);
+extern void saa716x_ff_phi_read(struct saa716x_dev *saa716x,
+				u32 address, u8 * data, int length);
+extern void saa716x_ff_phi_write_fifo(struct saa716x_dev *saa716x,
+				      const u8 * data, int length);
+
+#endif /* __SAA716x_FF_PHI_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_fgpi.c linux/drivers/media/pci/saa716x/saa716x_fgpi.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_fgpi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_fgpi.c	2017-08-11 23:31:25.829389469 +0200
@@ -0,0 +1,386 @@
+#include <linux/kernel.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_fgpi_reg.h"
+#include "saa716x_dma_reg.h"
+#include "saa716x_msi_reg.h"
+
+#include "saa716x_dma.h"
+#include "saa716x_fgpi.h"
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+
+static const u32 fgpi_ch[] = {
+	FGPI0,
+	FGPI1,
+	FGPI2,
+	FGPI3
+};
+
+static const u32 bamdma_bufmode[] = {
+	BAM_FGPI0_DMA_BUF_MODE,
+	BAM_FGPI1_DMA_BUF_MODE,
+	BAM_FGPI2_DMA_BUF_MODE,
+	BAM_FGPI3_DMA_BUF_MODE
+};
+
+static const u32 msi_int_tagack[] = {
+	MSI_INT_TAGACK_FGPI_0,
+	MSI_INT_TAGACK_FGPI_1,
+	MSI_INT_TAGACK_FGPI_2,
+	MSI_INT_TAGACK_FGPI_3
+};
+
+static const u32 msi_int_ovrflw[] = {
+	MSI_INT_OVRFLW_FGPI_0,
+	MSI_INT_OVRFLW_FGPI_1,
+	MSI_INT_OVRFLW_FGPI_2,
+	MSI_INT_OVRFLW_FGPI_3
+};
+
+static const u32 msi_int_avint[] = {
+	MSI_INT_AVINT_FGPI_0,
+	MSI_INT_AVINT_FGPI_1,
+	MSI_INT_AVINT_FGPI_2,
+	MSI_INT_AVINT_FGPI_3
+};
+
+void saa716x_fgpiint_disable(struct saa716x_dmabuf *dmabuf, int channel)
+{
+	struct saa716x_dev *saa716x = dmabuf->saa716x;
+
+	u32 fgpi_port;
+
+	fgpi_port = fgpi_ch[channel];
+
+	SAA716x_EPWR(fgpi_port, INT_ENABLE, 0); /* disable FGPI IRQ */
+	SAA716x_EPWR(fgpi_port, INT_CLR_STATUS, 0x7f); /* clear status */
+}
+EXPORT_SYMBOL_GPL(saa716x_fgpiint_disable);
+
+int saa716x_fgpi_get_write_index(struct saa716x_dev *saa716x, u32 fgpi_index)
+{
+	u32 fgpi_base;
+	u32 buf_mode_reg;
+	u32 buf_mode;
+
+ 	switch (fgpi_index) {
+	case 0: /* FGPI_0 */
+		fgpi_base = FGPI0;
+		buf_mode_reg = BAM_FGPI0_DMA_BUF_MODE;
+		break;
+
+	case 1: /* FGPI_1 */
+		fgpi_base = FGPI1;
+		buf_mode_reg = BAM_FGPI1_DMA_BUF_MODE;
+		break;
+
+	case 2: /* FGPI_2 */
+		fgpi_base = FGPI2;
+		buf_mode_reg = BAM_FGPI2_DMA_BUF_MODE;
+		break;
+
+	case 3: /* FGPI_3 */
+		fgpi_base = FGPI3;
+		buf_mode_reg = BAM_FGPI3_DMA_BUF_MODE;
+		break;
+
+	default:
+		printk(KERN_ERR "%s: unexpected fgpi %u\n",
+		       __func__, fgpi_index);
+		return -1;
+	}
+
+	buf_mode = SAA716x_EPRD(BAM, buf_mode_reg);
+	if (saa716x->revision < 2) {
+		/* workaround for revision 1: restore buffer numbers on BAM */
+		SAA716x_EPWR(fgpi_base, INT_CLR_STATUS, 0x7F);
+		SAA716x_EPWR(BAM, buf_mode_reg, buf_mode | 7);
+	}
+	return (buf_mode >> 3) & 0x7;
+}
+EXPORT_SYMBOL_GPL(saa716x_fgpi_get_write_index);
+
+static u32 saa716x_init_ptables(struct saa716x_dmabuf *dmabuf, int channel,
+				struct fgpi_stream_params *stream_params)
+{
+	struct saa716x_dev *saa716x = dmabuf->saa716x;
+
+	u32 config, i;
+
+	for (i = 0; i < FGPI_BUFFERS; i++)
+		BUG_ON((dmabuf[i].mem_ptab_phys == 0));
+
+	config = MMU_DMA_CONFIG(channel); /* DMACONFIGx */
+
+	SAA716x_EPWR(MMU, config, (FGPI_BUFFERS - 1));
+
+	if ((stream_params->stream_flags & FGPI_INTERLACED) &&
+	    (stream_params->stream_flags & FGPI_ODD_FIELD) &&
+	    (stream_params->stream_flags & FGPI_EVEN_FIELD)) {
+		/* In interlaced mode the same buffer is written twice, once
+		   the odd field and once the even field */
+		SAA716x_EPWR(MMU, MMU_PTA0_LSB(channel), PTA_LSB(dmabuf[0].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA0_MSB(channel), PTA_MSB(dmabuf[0].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA1_LSB(channel), PTA_LSB(dmabuf[0].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA1_MSB(channel), PTA_MSB(dmabuf[0].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA2_LSB(channel), PTA_LSB(dmabuf[1].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA2_MSB(channel), PTA_MSB(dmabuf[1].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA3_LSB(channel), PTA_LSB(dmabuf[1].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA3_MSB(channel), PTA_MSB(dmabuf[1].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA4_LSB(channel), PTA_LSB(dmabuf[2].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA4_MSB(channel), PTA_MSB(dmabuf[2].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA5_LSB(channel), PTA_LSB(dmabuf[2].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA5_MSB(channel), PTA_MSB(dmabuf[2].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA6_LSB(channel), PTA_LSB(dmabuf[3].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA6_MSB(channel), PTA_MSB(dmabuf[3].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA7_LSB(channel), PTA_LSB(dmabuf[3].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA7_MSB(channel), PTA_MSB(dmabuf[3].mem_ptab_phys)); /* High */
+	} else {
+		SAA716x_EPWR(MMU, MMU_PTA0_LSB(channel), PTA_LSB(dmabuf[0].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA0_MSB(channel), PTA_MSB(dmabuf[0].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA1_LSB(channel), PTA_LSB(dmabuf[1].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA1_MSB(channel), PTA_MSB(dmabuf[1].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA2_LSB(channel), PTA_LSB(dmabuf[2].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA2_MSB(channel), PTA_MSB(dmabuf[2].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA3_LSB(channel), PTA_LSB(dmabuf[3].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA3_MSB(channel), PTA_MSB(dmabuf[3].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA4_LSB(channel), PTA_LSB(dmabuf[4].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA4_MSB(channel), PTA_MSB(dmabuf[4].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA5_LSB(channel), PTA_LSB(dmabuf[5].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA5_MSB(channel), PTA_MSB(dmabuf[5].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA6_LSB(channel), PTA_LSB(dmabuf[6].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA6_MSB(channel), PTA_MSB(dmabuf[6].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA7_LSB(channel), PTA_LSB(dmabuf[7].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA7_MSB(channel), PTA_MSB(dmabuf[7].mem_ptab_phys)); /* High */
+	}
+
+	return 0;
+}
+
+int saa716x_fgpi_setparams(struct saa716x_dmabuf *dmabuf,
+			   struct fgpi_stream_params *stream_params,
+			   int port)
+{
+	struct saa716x_dev *saa716x = dmabuf->saa716x;
+
+	u32 fgpi_port, buf_mode, val, mid;
+	u32 D1_XY_END, offst_1, offst_2;
+	int i = 0;
+	u8 dma_channel;
+
+	fgpi_port = fgpi_ch[port];
+	buf_mode = bamdma_bufmode[port];
+	dma_channel = saa716x->fgpi[port].dma_channel;
+
+	/* Reset FGPI block */
+	SAA716x_EPWR(fgpi_port, FGPI_SOFT_RESET, FGPI_SOFTWARE_RESET);
+
+	/* Reset DMA channel */
+	SAA716x_EPWR(BAM, buf_mode, 0x00000040);
+	saa716x_init_ptables(dmabuf, dma_channel, stream_params);
+
+
+	/* monitor BAM reset */
+	val = SAA716x_EPRD(BAM, buf_mode);
+	while (val && (i < 100)) {
+		msleep(30);
+		val = SAA716x_EPRD(BAM, buf_mode);
+		i++;
+	}
+
+	if (val) {
+		dprintk(SAA716x_ERROR, 1, "Error: BAM FGPI Reset failed!");
+		return -EIO;
+	}
+
+	/* set buffer count */
+	SAA716x_EPWR(BAM, buf_mode, FGPI_BUFFERS - 1);
+
+	/* initialize all available address offsets */
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_0(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_1(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_2(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_3(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_4(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_5(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_6(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_7(dma_channel), 0x0);
+
+	/* get module ID */
+	mid = SAA716x_EPRD(fgpi_port, FGPI_MODULE_ID);
+	if (mid != 0x14b0100)
+		dprintk(SAA716x_ERROR, 1, "FGPI Id<%04x> is not supported", mid);
+
+	/* Initialize FGPI block */
+	SAA716x_EPWR(fgpi_port, FGPI_REC_SIZE, stream_params->samples * (stream_params->bits / 8));
+	SAA716x_EPWR(fgpi_port, FGPI_STRIDE, stream_params->pitch);
+
+	offst_1 = 0;
+	offst_2 = 0;
+	switch (stream_params->stream_type) {
+	case FGPI_TRANSPORT_STREAM:
+		SAA716x_EPWR(fgpi_port, FGPI_CONTROL, 0x00000080);
+		SAA716x_EPWR(fgpi_port, FGPI_SIZE, stream_params->lines);
+		break;
+
+	case FGPI_RAW_STREAM:
+	case FGPI_PROGRAM_STREAM:
+		SAA716x_EPWR(fgpi_port, FGPI_CONTROL, 0x00000088);
+		SAA716x_EPWR(fgpi_port, FGPI_SIZE, stream_params->lines);
+		break;
+
+	case FGPI_VIDEO_STREAM:
+		SAA716x_EPWR(fgpi_port, FGPI_CONTROL, 0x00000040);
+		SAA716x_EPWR(fgpi_port, FGPI_D1_XY_START, stream_params->offset);
+
+		if ((stream_params->stream_flags & FGPI_INTERLACED) &&
+		    (stream_params->stream_flags & FGPI_ODD_FIELD) &&
+		    (stream_params->stream_flags & FGPI_EVEN_FIELD)) {
+
+			SAA716x_EPWR(fgpi_port, FGPI_SIZE, stream_params->lines / 2);
+			SAA716x_EPWR(fgpi_port, FGPI_STRIDE, stream_params->pitch * 2); /* interlaced stride of 2 lines */
+
+			D1_XY_END  = (stream_params->samples << 16);
+			D1_XY_END |= (stream_params->lines / 2) + stream_params->offset;
+
+			if (stream_params->stream_flags & FGPI_PAL)
+				offst_2 = stream_params->pitch;
+			else
+				offst_1 = stream_params->pitch;
+
+		} else {
+			SAA716x_EPWR(fgpi_port, FGPI_SIZE, stream_params->lines);
+			SAA716x_EPWR(fgpi_port, FGPI_STRIDE, stream_params->pitch); /* stride of 1 line */
+
+			D1_XY_END  = stream_params->samples << 16;
+			D1_XY_END |= stream_params->lines + stream_params->offset;
+		}
+
+		SAA716x_EPWR(fgpi_port, FGPI_D1_XY_END, D1_XY_END);
+		break;
+
+	default:
+		SAA716x_EPWR(fgpi_port, FGPI_CONTROL, 0x00000080);
+		break;
+	}
+
+	SAA716x_EPWR(fgpi_port, FGPI_BASE_1, (dma_channel << 21) + offst_1);
+	SAA716x_EPWR(fgpi_port, FGPI_BASE_2, (dma_channel << 21) + offst_2);
+
+	return 0;
+}
+
+int saa716x_fgpi_start(struct saa716x_dev *saa716x, int port,
+		       struct fgpi_stream_params *stream_params)
+{
+	u32 fgpi_port;
+	u32 config;
+	u32 val;
+	u32 i;
+
+	fgpi_port = fgpi_ch[port];
+
+	SAA716x_EPWR(fgpi_port, FGPI_INTERFACE, 0);
+	msleep(10);
+
+	if (saa716x_fgpi_setparams(saa716x->fgpi[port].dma_buf, stream_params, port) != 0) {
+		return -EIO;
+	}
+
+	saa716x->fgpi[port].read_index = 0;
+
+	config = MMU_DMA_CONFIG(saa716x->fgpi[port].dma_channel); /* DMACONFIGx */
+
+	val = SAA716x_EPRD(MMU, config);
+	SAA716x_EPWR(MMU, config, val & ~0x40);
+	SAA716x_EPWR(MMU, config, val | 0x40);
+
+	SAA716x_EPWR(fgpi_port, INT_ENABLE, 0x7F);
+
+	val = SAA716x_EPRD(MMU, config);
+	i = 0;
+	while (i < 500) {
+		if (val & 0x80)
+			break;
+		msleep(10);
+		val = SAA716x_EPRD(MMU, config);
+		i++;
+	}
+
+	if (!(val & 0x80)) {
+		dprintk(SAA716x_ERROR, 1, "Error: PTE pre-fetch failed!");
+		return -EIO;
+	}
+
+	val = SAA716x_EPRD(fgpi_port, FGPI_CONTROL);
+	val |= 0x3000;
+
+	saa716x_set_clk_external(saa716x, saa716x->fgpi[port].dma_channel);
+
+	SAA716x_EPWR(fgpi_port, FGPI_CONTROL, val);
+
+	SAA716x_EPWR(MSI, MSI_INT_ENA_SET_L, msi_int_tagack[port]);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_fgpi_start);
+
+int saa716x_fgpi_stop(struct saa716x_dev *saa716x, int port)
+{
+	u32 fgpi_port;
+	u32 val;
+
+	fgpi_port = fgpi_ch[port];
+
+	SAA716x_EPWR(MSI, MSI_INT_ENA_CLR_L, msi_int_tagack[port]);
+
+	val = SAA716x_EPRD(fgpi_port, FGPI_CONTROL);
+	val &= ~0x3000;
+	SAA716x_EPWR(fgpi_port, FGPI_CONTROL, val);
+
+	saa716x_set_clk_internal(saa716x, saa716x->fgpi[port].dma_channel);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_fgpi_stop);
+
+int saa716x_fgpi_init(struct saa716x_dev *saa716x, int port, int dma_buf_size,
+		      void (*worker)(unsigned long))
+{
+	int i;
+	int ret;
+
+	saa716x->fgpi[port].dma_channel = port + 6;
+	for (i = 0; i < FGPI_BUFFERS; i++)
+	{
+		ret = saa716x_dmabuf_alloc(saa716x,
+					   &saa716x->fgpi[port].dma_buf[i],
+					   dma_buf_size);
+		if (ret < 0) {
+			return ret;
+		}
+	}
+	saa716x->fgpi[port].saa716x = saa716x;
+	tasklet_init(&saa716x->fgpi[port].tasklet, worker,
+		     (unsigned long)&saa716x->fgpi[port]);
+	saa716x->fgpi[port].read_index = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_fgpi_init);
+
+int saa716x_fgpi_exit(struct saa716x_dev *saa716x, int port)
+{
+	int i;
+
+	tasklet_kill(&saa716x->fgpi[port].tasklet);
+	for (i = 0; i < FGPI_BUFFERS; i++)
+	{
+		saa716x_dmabuf_free(saa716x, &saa716x->fgpi[port].dma_buf[i]);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_fgpi_exit);
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_fgpi.h linux/drivers/media/pci/saa716x/saa716x_fgpi.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_fgpi.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_fgpi.h	2017-08-11 23:31:25.829389469 +0200
@@ -0,0 +1,92 @@
+#ifndef __SAA716x_FGPI_H
+#define __SAA716x_FGPI_H
+
+#include <linux/interrupt.h>
+
+#define FGPI_BUFFERS		8
+
+
+/*
+ * Port supported streams
+ *
+ * FGPI_AUDIO_STREAM
+ * FGPI_VIDEO_STREAM
+ * FGPI_VBI_STREAM
+ * FGPI_TRANSPORT_STREAM
+ * FGPI_PROGRAM_STREAM
+ */
+enum fgpi_stream_type {
+	FGPI_RAW_STREAM		= 0x00,
+	FGPI_AUDIO_STREAM	= 0x01,
+	FGPI_VIDEO_STREAM	= 0x02,
+	FGPI_VBI_STREAM		= 0x04,
+	FGPI_TRANSPORT_STREAM	= 0x08,
+	FGPI_PROGRAM_STREAM	= 0x10
+};
+
+/*
+ * Stream port flags
+ *
+ * FGPI_ODD_FIELD
+ * FGPI_EVEN_FIELD
+ * FGPI_HD_0
+ * FGPI_HD_1
+ * FGPI_PAL
+ * FGPI_NTSC
+ */
+enum fgpi_stream_flags {
+	FGPI_ODD_FIELD		= 0x0001,
+	FGPI_EVEN_FIELD		= 0x0002,
+	FGPI_INTERLACED		= 0x0004,
+	FGPI_HD0		= 0x0010,
+	FGPI_HD1		= 0x0020,
+	FGPI_PAL		= 0x0040,
+	FGPI_NTSC		= 0x0080,
+	FGPI_NO_SCALER		= 0x0100,
+};
+
+/*
+ * Stream port parameters
+ * bits: Bits per sample
+ * samples: samples perline
+ * lines: number of lines
+ * pitch: stream pitch in bytes
+ * offset: offset to first valid line
+ */
+struct fgpi_stream_params {
+	u32			bits;
+	u32			samples;
+	u32			lines;
+
+	s32			pitch;
+
+	u32			offset;
+	u32			page_tables;
+
+	enum fgpi_stream_flags	stream_flags;
+	enum fgpi_stream_type	stream_type;
+};
+
+struct saa716x_dmabuf;
+
+struct saa716x_fgpi_stream_port {
+	u8			dma_channel;
+	struct saa716x_dmabuf	dma_buf[FGPI_BUFFERS];
+	struct saa716x_dev	*saa716x;
+	struct tasklet_struct	tasklet;
+	u8			read_index;
+};
+
+extern void saa716x_fgpiint_disable(struct saa716x_dmabuf *dmabuf, int channel);
+extern int saa716x_fgpi_get_write_index(struct saa716x_dev *saa716x,
+					u32 fgpi_index);
+extern int saa716x_fgpi_start(struct saa716x_dev *saa716x, int port,
+			      struct fgpi_stream_params *stream_params);
+extern int saa716x_fgpi_stop(struct saa716x_dev *saa716x, int port);
+
+extern int saa716x_fgpi_init(struct saa716x_dev *saa716x, int port,
+			      int dma_buf_size,
+			      void (*worker)(unsigned long));
+extern int saa716x_fgpi_exit(struct saa716x_dev *saa716x, int port);
+
+#endif /* __SAA716x_FGPI_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_fgpi_reg.h linux/drivers/media/pci/saa716x/saa716x_fgpi_reg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_fgpi_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_fgpi_reg.h	2017-08-11 23:31:25.829389469 +0200
@@ -0,0 +1,74 @@
+#ifndef __SAA716x_FGPI_REG_H
+#define __SAA716x_FGPI_REG_H
+
+/* -------------- FGPI Registers -------------- */
+
+#define FGPI_CONTROL			0x000
+#define FGPI_CAPTURE_ENABLE_2		(0x00000001 << 13)
+#define FGPI_CAPTURE_ENABLE_1		(0x00000001 << 12)
+#define FGPI_MODE			(0x00000001 << 11)
+#define FGPI_SAMPLE_SIZE		(0x00000003 <<  8)
+#define FGPI_BUF_SYNC_MSG_STOP		(0x00000003 <<  5)
+#define FGPI_REC_START_MSG_START	(0x00000003 <<  2)
+#define FGPI_TSTAMP_SELECT		(0x00000001 <<  1)
+#define FGPI_VAR_LENGTH			(0x00000001 <<  0)
+
+#define FGPI_BASE_1			0x004
+#define FGPI_BASE_2			0x008
+#define FGPI_SIZE			0x00c
+#define FGPI_REC_SIZE			0x010
+#define FGPI_STRIDE			0x014
+#define FGPI_NUM_RECORD_1		0x018
+#define FGPI_NUM_RECORD_2		0x01c
+#define FGPI_THRESHOLD_1		0x020
+#define FGPI_THRESHOLD_2		0x024
+#define FGPI_D1_XY_START		0x028
+#define FGPI_D1_XY_END			0x02c
+
+#define INT_STATUS			0xfe0
+#define FGPI_BUF1_ACTIVE		(0x00000001 <<  7)
+#define FGPI_OVERFLOW			(0x00000001 <<  6)
+#define FGPI_MBE			(0x00000001 <<  5)
+#define FGPI_UNDERRUN			(0x00000001 <<  4)
+#define FGPI_THRESH2_REACHED		(0x00000001 <<  3)
+#define FGPI_THRESH1_REACHED		(0x00000001 <<  2)
+#define FGPI_BUF2_FULL			(0x00000001 <<  1)
+#define FGPI_BUF1_FULL			(0x00000001 <<  0)
+
+#define INT_ENABLE			0xfe4
+#define FGPI_OVERFLOW_ENA		(0x00000001 <<  6)
+#define FGPI_MBE_ENA			(0x00000001 <<  5)
+#define FGPI_UNDERRUN_ENA		(0x00000001 <<  4)
+#define FGPI_THRESH2_REACHED_ENA	(0x00000001 <<  3)
+#define FGPI_THRESH1_REACHED_ENA	(0x00000001 <<  2)
+#define FGPI_BUF2_FULL_ENA		(0x00000001 <<  1)
+#define FGPI_BUF1_FULL_ENA		(0x00000001 <<  0)
+
+#define INT_CLR_STATUS			0xfe8
+#define FGPI_OVERFLOW_ACK		(0x00000001 <<  6)
+#define FGPI_MBE_ACK			(0x00000001 <<  5)
+#define FGPI_UNDERRUN_ACK		(0x00000001 <<  4)
+#define FGPI_THRESH2_REACHED_ACK	(0x00000001 <<  3)
+#define FGPI_THRESH1_REACHED_ACK	(0x00000001 <<  2)
+#define FGPI_BUF2_DONE_ACK		(0x00000001 <<  1)
+#define FGPI_BUF1_DONE_ACK		(0x00000001 <<  0)
+
+#define INT_SET_STATUS			0xfec
+#define FGPI_OVERFLOW_SET		(0x00000001 <<  6)
+#define FGPI_MBE_SET			(0x00000001 <<  5)
+#define FGPI_UNDERRUN_SET		(0x00000001 <<  4)
+#define FGPI_THRESH2_REACHED_SET	(0x00000001 <<  3)
+#define FGPI_THRESH1_REACHED_SET	(0x00000001 <<  2)
+#define FGPI_BUF2_DONE_SET		(0x00000001 <<  1)
+#define FGPI_BUF1_DONE_SET		(0x00000001 <<  0)
+
+#define FGPI_SOFT_RESET			0xff0
+#define FGPI_SOFTWARE_RESET		(0x00000001 <<  0)
+
+#define FGPI_INTERFACE			0xff4
+#define FGPI_DISABLE_BUS_IF		(0x00000001 <<  0)
+
+#define FGPI_MOD_ID_EXT			0xff8
+#define FGPI_MODULE_ID			0xffc
+
+#endif /* __SAA716x_FGPI_REG_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_gpio.c linux/drivers/media/pci/saa716x/saa716x_gpio.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_gpio.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_gpio.c	2017-08-11 23:31:25.841389403 +0200
@@ -0,0 +1,140 @@
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_gpio_reg.h"
+
+#include "saa716x_gpio.h"
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+
+void saa716x_gpio_init(struct saa716x_dev *saa716x)
+{
+	spin_lock_init(&saa716x->gpio_lock);
+}
+EXPORT_SYMBOL_GPL(saa716x_gpio_init);
+
+int saa716x_get_gpio_mode(struct saa716x_dev *saa716x, u32 *config)
+{
+	*config = SAA716x_EPRD(GPIO, GPIO_WR_MODE);
+
+	return 0;
+}
+
+int saa716x_set_gpio_mode(struct saa716x_dev *saa716x, u32 mask, u32 config)
+{
+	unsigned long flags;
+	u32 reg;
+
+	spin_lock_irqsave(&saa716x->gpio_lock, flags);
+	reg = SAA716x_EPRD(GPIO, GPIO_WR_MODE);
+	reg &= ~mask;
+	reg |= (config & mask);
+	SAA716x_EPWR(GPIO, GPIO_WR_MODE, reg);
+	spin_unlock_irqrestore(&saa716x->gpio_lock, flags);
+
+	return 0;
+}
+
+u32 saa716x_gpio_rd(struct saa716x_dev *saa716x)
+{
+	return SAA716x_EPRD(GPIO, GPIO_RD);
+}
+
+void saa716x_gpio_wr(struct saa716x_dev *saa716x, u32 data)
+{
+	SAA716x_EPWR(GPIO, GPIO_WR, data);
+}
+
+void saa716x_gpio_ctl(struct saa716x_dev *saa716x, u32 mask, u32 bits)
+{
+	unsigned long flags;
+	u32 reg;
+
+	spin_lock_irqsave(&saa716x->gpio_lock, flags);
+
+	reg  = SAA716x_EPRD(GPIO, GPIO_OEN);
+	reg &= mask;
+	reg |= bits;
+	SAA716x_EPWR(GPIO, GPIO_OEN, reg);
+
+	spin_unlock_irqrestore(&saa716x->gpio_lock, flags);
+}
+
+void saa716x_gpio_bits(struct saa716x_dev *saa716x, u32 bits)
+{
+	unsigned long flags;
+	u32 reg;
+
+	spin_lock_irqsave(&saa716x->gpio_lock, flags);
+
+	reg  = SAA716x_EPRD(GPIO, GPIO_WR);
+	reg &= ~bits;
+	/* TODO ! add maskable config bits in here */
+	/* reg |= (config->mask & bits) */
+	reg |= bits;
+	SAA716x_EPWR(GPIO, GPIO_WR, reg);
+
+	spin_unlock_irqrestore(&saa716x->gpio_lock, flags);
+}
+
+void saa716x_gpio_set_output(struct saa716x_dev *saa716x, int gpio)
+{
+	uint32_t value;
+
+	value = SAA716x_EPRD(GPIO, GPIO_OEN);
+	value &= ~(1 << gpio);
+	SAA716x_EPWR(GPIO, GPIO_OEN, value);
+}
+EXPORT_SYMBOL_GPL(saa716x_gpio_set_output);
+
+void saa716x_gpio_set_input(struct saa716x_dev *saa716x, int gpio)
+{
+	uint32_t value;
+
+	value = SAA716x_EPRD(GPIO, GPIO_OEN);
+	value |= 1 << gpio;
+	SAA716x_EPWR(GPIO, GPIO_OEN, value);
+}
+EXPORT_SYMBOL_GPL(saa716x_gpio_set_input);
+
+void saa716x_gpio_set_mode(struct saa716x_dev *saa716x, int gpio, int mode)
+{
+	uint32_t value;
+
+	value = SAA716x_EPRD(GPIO, GPIO_WR_MODE);
+	if (mode)
+		value |= 1 << gpio;
+	else
+		value &= ~(1 << gpio);
+	SAA716x_EPWR(GPIO, GPIO_WR_MODE, value);
+}
+EXPORT_SYMBOL_GPL(saa716x_gpio_set_mode);
+
+void saa716x_gpio_write(struct saa716x_dev *saa716x, int gpio, int set)
+{
+	uint32_t value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&saa716x->gpio_lock, flags);
+	value = SAA716x_EPRD(GPIO, GPIO_WR);
+	if (set)
+		value |= 1 << gpio;
+	else
+		value &= ~(1 << gpio);
+	SAA716x_EPWR(GPIO, GPIO_WR, value);
+	spin_unlock_irqrestore(&saa716x->gpio_lock, flags);
+}
+EXPORT_SYMBOL_GPL(saa716x_gpio_write);
+
+int saa716x_gpio_read(struct saa716x_dev *saa716x, int gpio)
+{
+	uint32_t value;
+
+	value = SAA716x_EPRD(GPIO, GPIO_RD);
+	if (value & (1 << gpio))
+		return 1;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_gpio_read);
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_gpio.h linux/drivers/media/pci/saa716x/saa716x_gpio.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_gpio.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_gpio.h	2017-08-11 23:31:25.841389403 +0200
@@ -0,0 +1,26 @@
+#ifndef __SAA716x_GPIO_H
+#define __SAA716x_GPIO_H
+
+#define BOOT_MODE	GPIO_31 | GPIO_30
+#define AV_UNIT_B	GPIO_25
+#define AV_UNIT_A	GPIO_24
+#define AV_INTR_B	GPIO_01
+#define AV_INTR_A	GPIO_00
+
+struct saa716x_dev;
+
+extern void saa716x_gpio_init(struct saa716x_dev *saa716x);
+
+extern u32 saa716x_gpio_rd(struct saa716x_dev *saa716x);
+extern void saa716x_gpio_wr(struct saa716x_dev *saa716x, u32 data);
+extern void saa716x_gpio_ctl(struct saa716x_dev *saa716x, u32 mask, u32 bits);
+
+extern void saa716x_gpio_bits(struct saa716x_dev *saa716x, u32 bits);
+
+extern void saa716x_gpio_set_output(struct saa716x_dev *saa716x, int gpio);
+extern void saa716x_gpio_set_input(struct saa716x_dev *saa716x, int gpio);
+extern void saa716x_gpio_set_mode(struct saa716x_dev *saa716x, int gpio, int mode);
+extern void saa716x_gpio_write(struct saa716x_dev *saa716x, int gpio, int set);
+extern int saa716x_gpio_read(struct saa716x_dev *saa716x, int gpio);
+
+#endif /* __SAA716x_GPIO_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_gpio_reg.h linux/drivers/media/pci/saa716x/saa716x_gpio_reg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_gpio_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_gpio_reg.h	2017-08-11 23:31:25.841389403 +0200
@@ -0,0 +1,47 @@
+#ifndef __SAA716x_GPIO_REG_H
+#define __SAA716x_GPIO_REG_H
+
+/* -------------- GPIO Registers -------------- */
+
+#define GPIO_RD				0x000
+#define GPIO_WR				0x004
+#define GPIO_WR_MODE			0x008
+#define GPIO_OEN			0x00c
+
+#define GPIO_SW_RST			0xff0
+#define GPIO_SW_RESET			(0x00000001 <<  0)
+
+#define GPIO_31				(1 << 31)
+#define GPIO_30				(1 << 30)
+#define GPIO_29				(1 << 29)
+#define GPIO_28				(1 << 28)
+#define GPIO_27				(1 << 27)
+#define GPIO_26				(1 << 26)
+#define GPIO_25				(1 << 25)
+#define GPIO_24				(1 << 24)
+#define GPIO_23				(1 << 23)
+#define GPIO_22				(1 << 22)
+#define GPIO_21				(1 << 21)
+#define GPIO_20				(1 << 20)
+#define GPIO_19				(1 << 19)
+#define GPIO_18				(1 << 18)
+#define GPIO_17				(1 << 17)
+#define GPIO_16				(1 << 16)
+#define GPIO_15				(1 << 15)
+#define GPIO_14				(1 << 14)
+#define GPIO_13				(1 << 13)
+#define GPIO_12				(1 << 12)
+#define GPIO_11				(1 << 11)
+#define GPIO_10				(1 << 10)
+#define GPIO_09				(1 <<  9)
+#define GPIO_08				(1 <<  8)
+#define GPIO_07				(1 <<  7)
+#define GPIO_06				(1 <<  6)
+#define GPIO_05				(1 <<  5)
+#define GPIO_04				(1 <<  4)
+#define GPIO_03				(1 <<  3)
+#define GPIO_02				(1 <<  2)
+#define GPIO_01				(1 <<  1)
+#define GPIO_00				(1 <<  0)
+
+#endif /* __SAA716x_GPIO_REG_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_greg.c linux/drivers/media/pci/saa716x/saa716x_greg.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_greg.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_greg.c	2017-08-11 23:31:25.841389403 +0200
@@ -0,0 +1,42 @@
+#include <linux/kernel.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_greg_reg.h"
+#include "saa716x_greg.h"
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+
+static u32 g_save[12];
+
+void saa716x_greg_save(struct saa716x_dev *saa716x)
+{
+	g_save[0] = SAA716x_EPRD(GREG, GREG_SUBSYS_CONFIG);
+	g_save[1] = SAA716x_EPRD(GREG, GREG_MSI_BAR_PMCSR);
+	g_save[2] = SAA716x_EPRD(GREG, GREG_PMCSR_DATA_1);
+	g_save[3] = SAA716x_EPRD(GREG, GREG_PMCSR_DATA_2);
+	g_save[4] = SAA716x_EPRD(GREG, GREG_VI_CTRL);
+	g_save[5] = SAA716x_EPRD(GREG, GREG_FGPI_CTRL);
+	g_save[6] = SAA716x_EPRD(GREG, GREG_RSTU_CTRL);
+	g_save[7] = SAA716x_EPRD(GREG, GREG_I2C_CTRL);
+	g_save[8] = SAA716x_EPRD(GREG, GREG_OVFLW_CTRL);
+	g_save[9] = SAA716x_EPRD(GREG, GREG_TAG_ACK_FLEN);
+
+	g_save[10] = SAA716x_EPRD(GREG, GREG_VIDEO_IN_CTRL);
+}
+
+void saa716x_greg_restore(struct saa716x_dev *saa716x)
+{
+	SAA716x_EPWR(GREG, GREG_SUBSYS_CONFIG, g_save[0]);
+	SAA716x_EPWR(GREG, GREG_MSI_BAR_PMCSR, g_save[1]);
+	SAA716x_EPWR(GREG, GREG_PMCSR_DATA_1, g_save[2]);
+	SAA716x_EPWR(GREG, GREG_PMCSR_DATA_2, g_save[3]);
+	SAA716x_EPWR(GREG, GREG_VI_CTRL, g_save[4]);
+	SAA716x_EPWR(GREG, GREG_FGPI_CTRL, g_save[5]);
+	SAA716x_EPWR(GREG, GREG_RSTU_CTRL, g_save[6]);
+	SAA716x_EPWR(GREG, GREG_I2C_CTRL, g_save[7]);
+	SAA716x_EPWR(GREG, GREG_OVFLW_CTRL, g_save[8]);
+	SAA716x_EPWR(GREG, GREG_TAG_ACK_FLEN, g_save[9]);
+
+	SAA716x_EPWR(GREG, GREG_VIDEO_IN_CTRL, g_save[10]);
+}
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_greg.h linux/drivers/media/pci/saa716x/saa716x_greg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_greg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_greg.h	2017-08-11 23:31:25.841389403 +0200
@@ -0,0 +1,9 @@
+#ifndef __SAA716x_GREG_H
+#define __SAA716x_GREG_H
+
+struct saa716x_dev;
+
+extern void saa716x_greg_save(struct saa716x_dev *saa716x);
+extern void saa716x_greg_restore(struct saa716x_dev *saa716x);
+
+#endif /* __SAA716x_GREG_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_greg_reg.h linux/drivers/media/pci/saa716x/saa716x_greg_reg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_greg_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_greg_reg.h	2017-08-11 23:31:25.841389403 +0200
@@ -0,0 +1,91 @@
+#ifndef __SAA716x_GREG_REG_H
+#define __SAA716x_GREG_REG_H
+
+/* -------------- GREG Registers -------------- */
+
+#define GREG_SUBSYS_CONFIG		0x000
+#define GREG_SUBSYS_ID			(0x0000ffff << 16)
+#define GREG_SUBSYS_VID			(0x0000ffff <<  0)
+
+#define GREG_MSI_BAR_PMCSR		0x004
+#define GREG_PMCSR_SCALE_7		(0x00000003 << 30)
+#define GREG_PMCSR_SCALE_6		(0x00000003 << 28)
+#define GREG_PMCSR_SCALE_5		(0x00000003 << 26)
+#define GREG_PMCSR_SCALE_4		(0x00000003 << 24)
+#define GREG_PMCSR_SCALE_3		(0x00000003 << 22)
+#define GREG_PMCSR_SCALE_2		(0x00000003 << 20)
+#define GREG_PMCSR_SCALE_1		(0x00000003 << 18)
+#define GREG_PMCSR_SCALE_0		(0x00000003 << 16)
+
+#define GREG_BAR_WIDTH_17		(0x0000001e <<  8)
+#define GREG_BAR_WIDTH_18		(0x0000001c <<  8)
+#define GREG_BAR_WIDTH_19		(0x00000018 <<  8)
+#define GREG_BAR_WIDTH_20		(0x00000010 <<  8)
+
+#define GREG_BAR_PREFETCH		(0x00000001 <<  3)
+#define GREG_MSI_MM_CAP1		(0x00000000 <<  0) // FIXME !
+#define GREG_MSI_MM_CAP2		(0x00000001 <<  0)
+#define GREG_MSI_MM_CAP4		(0x00000002 <<  0)
+#define GREG_MSI_MM_CAP8		(0x00000003 <<  0)
+#define GREG_MSI_MM_CAP16		(0x00000004 <<  0)
+#define GREG_MSI_MM_CAP32		(0x00000005 <<  0)
+
+#define GREG_PMCSR_DATA_1		0x008
+#define GREG_PMCSR_DATA_2		0x00c
+#define GREG_VI_CTRL			0x010
+#define GREG_FGPI_CTRL			0x014
+
+#define GREG_RSTU_CTRL			0x018
+#define GREG_BOOT_READY			(0x00000001 << 13)
+#define GREG_RESET_REQ			(0x00000001 << 12)
+#define GREG_IP_RST_RELEASE		(0x00000001 << 11)
+#define GREG_ADAPTER_RST_RELEASE	(0x00000001 << 10)
+#define GREG_PCIE_CORE_RST_RELEASE	(0x00000001 <<  9)
+#define GREG_BOOT_IP_RST_RELEASE	(0x00000001 <<  8)
+#define GREG_BOOT_RST_RELEASE		(0x00000001 <<  7)
+#define GREG_CGU_RST_RELEASE		(0x00000001 <<  6)
+#define GREG_IP_RST_ASSERT		(0x00000001 <<  5)
+#define GREG_ADAPTER_RST_ASSERT		(0x00000001 <<  4)
+#define GREG_RST_ASSERT			(0x00000001 <<  3)
+#define GREG_BOOT_IP_RST_ASSERT		(0x00000001 <<  2)
+#define GREG_BOOT_RST_ASSERT		(0x00000001 <<  1)
+#define GREG_CGU_RST_ASSERT		(0x00000001 <<  0)
+
+#define GREG_I2C_CTRL			0x01c
+#define GREG_I2C_SLAVE_ADDR		(0x0000007f <<  0)
+
+#define GREG_OVFLW_CTRL			0x020
+#define GREG_OVERFLOW_ENABLE		(0x00001fff <<  0)
+
+#define GREG_TAG_ACK_FLEN		0x024
+#define GREG_TAG_ACK_FLEN_1B		(0x00000000 <<  0)
+#define GREG_TAG_ACK_FLEN_2B		(0x00000001 <<  0)
+#define GREG_TAG_ACK_FLEN_4B		(0x00000002 <<  0)
+#define GREG_TAG_ACK_FLEN_8B		(0x00000003 <<  0)
+
+#define GREG_VIDEO_IN_CTRL		0x028
+
+#define GREG_SPARE_1			0x02c
+#define GREG_SPARE_2			0x030
+#define GREG_SPARE_3			0x034
+#define GREG_SPARE_4			0x038
+#define GREG_SPARE_5			0x03c
+#define GREG_SPARE_6			0x040
+#define GREG_SPARE_7			0x044
+#define GREG_SPARE_8			0x048
+#define GREG_SPARE_9			0x04c
+#define GREG_SPARE_10			0x050
+#define GREG_SPARE_11			0x054
+#define GREG_SPARE_12			0x058
+#define GREG_SPARE_13			0x05c
+#define GREG_SPARE_14			0x060
+#define GREG_SPARE_15			0x064
+
+#define GREG_FAIL_DISABLE		0x068
+#define GREG_BOOT_FAIL_DISABLE		(0x00000001 <<  0)
+
+#define GREG_SW_RST			0xff0
+#define GREG_SW_RESET			(0x00000001 <<  0)
+
+
+#endif /* __SAA716x_GREG_REG_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_hybrid.c linux/drivers/media/pci/saa716x/saa716x_hybrid.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_hybrid.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_hybrid.c	2017-08-11 23:31:25.841389403 +0200
@@ -0,0 +1,726 @@
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include <linux/i2c.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_gpio_reg.h"
+#include "saa716x_greg_reg.h"
+#include "saa716x_msi_reg.h"
+
+#include "saa716x_adap.h"
+#include "saa716x_i2c.h"
+#include "saa716x_msi.h"
+#include "saa716x_hybrid.h"
+#include "saa716x_gpio.h"
+#include "saa716x_rom.h"
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+
+#include "zl10353.h"
+#include "mb86a16.h"
+#include "tda1004x.h"
+#include "tda827x.h"
+
+unsigned int verbose;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
+
+unsigned int int_type;
+module_param(int_type, int, 0644);
+MODULE_PARM_DESC(int_type, "force Interrupt Handler type: 0=INT-A, 1=MSI, 2=MSI-X. default INT-A mode");
+
+#define DRIVER_NAME	"SAA716x Hybrid"
+
+static int saa716x_hybrid_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+{
+	struct saa716x_dev *saa716x;
+	int err = 0;
+
+	saa716x = kzalloc(sizeof (struct saa716x_dev), GFP_KERNEL);
+	if (saa716x == NULL) {
+		printk(KERN_ERR "saa716x_hybrid_pci_probe ERROR: out of memory\n");
+		err = -ENOMEM;
+		goto fail0;
+	}
+
+	saa716x->verbose	= verbose;
+	saa716x->int_type	= int_type;
+	saa716x->pdev		= pdev;
+	saa716x->config		= (struct saa716x_config *) pci_id->driver_data;
+
+	err = saa716x_pci_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x PCI Initialization failed");
+		goto fail1;
+	}
+
+	err = saa716x_cgu_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x CGU Init failed");
+		goto fail1;
+	}
+
+	err = saa716x_core_boot(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x Core Boot failed");
+		goto fail2;
+	}
+	dprintk(SAA716x_DEBUG, 1, "SAA716x Core Boot Success");
+
+	err = saa716x_msi_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x MSI Init failed");
+		goto fail2;
+	}
+
+	err = saa716x_jetpack_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x Jetpack core Initialization failed");
+		goto fail1;
+	}
+
+	err = saa716x_i2c_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x I2C Initialization failed");
+		goto fail3;
+	}
+
+	saa716x_gpio_init(saa716x);
+
+	err = saa716x_dump_eeprom(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x EEPROM dump failed");
+	}
+
+	err = saa716x_eeprom_data(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x EEPROM dump failed");
+	}
+
+	/* enable decoders on 7162 */
+	if (pdev->device == SAA7162) {
+		saa716x_gpio_set_output(saa716x, 24);
+		saa716x_gpio_set_output(saa716x, 25);
+
+		saa716x_gpio_write(saa716x, 24, 0);
+		saa716x_gpio_write(saa716x, 25, 0);
+
+		msleep(10);
+
+		saa716x_gpio_write(saa716x, 24, 1);
+		saa716x_gpio_write(saa716x, 25, 1);
+	}
+
+	/* set default port mapping */
+	SAA716x_EPWR(GREG, GREG_VI_CTRL, 0x2C688F44);
+	/* enable FGPI3 and FGPI0 for TS input from Port 3 and 6 */
+	SAA716x_EPWR(GREG, GREG_FGPI_CTRL, 0x894);
+
+	err = saa716x_dvb_init(saa716x);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x DVB initialization failed");
+		goto fail4;
+	}
+
+	return 0;
+
+fail4:
+	saa716x_dvb_exit(saa716x);
+fail3:
+	saa716x_i2c_exit(saa716x);
+fail2:
+	saa716x_pci_exit(saa716x);
+fail1:
+	kfree(saa716x);
+fail0:
+	return err;
+}
+
+static void saa716x_hybrid_pci_remove(struct pci_dev *pdev)
+{
+	struct saa716x_dev *saa716x = pci_get_drvdata(pdev);
+
+	saa716x_dvb_exit(saa716x);
+	saa716x_i2c_exit(saa716x);
+	saa716x_pci_exit(saa716x);
+	kfree(saa716x);
+}
+
+static irqreturn_t saa716x_hybrid_pci_irq(int irq, void *dev_id)
+{
+	struct saa716x_dev *saa716x	= (struct saa716x_dev *) dev_id;
+
+	u32 stat_h, stat_l, mask_h, mask_l;
+
+	if (unlikely(saa716x == NULL)) {
+		printk("%s: saa716x=NULL", __func__);
+		return IRQ_NONE;
+	}
+
+	stat_l = SAA716x_EPRD(MSI, MSI_INT_STATUS_L);
+	stat_h = SAA716x_EPRD(MSI, MSI_INT_STATUS_H);
+	mask_l = SAA716x_EPRD(MSI, MSI_INT_ENA_L);
+	mask_h = SAA716x_EPRD(MSI, MSI_INT_ENA_H);
+
+	dprintk(SAA716x_DEBUG, 1, "MSI STAT L=<%02x> H=<%02x>, CTL L=<%02x> H=<%02x>",
+		stat_l, stat_h, mask_l, mask_h);
+
+	if (!((stat_l & mask_l) || (stat_h & mask_h)))
+		return IRQ_NONE;
+
+	if (stat_l)
+		SAA716x_EPWR(MSI, MSI_INT_STATUS_CLR_L, stat_l);
+
+	if (stat_h)
+		SAA716x_EPWR(MSI, MSI_INT_STATUS_CLR_H, stat_h);
+
+	saa716x_msi_event(saa716x, stat_l, stat_h);
+#if 0
+	dprintk(SAA716x_DEBUG, 1, "VI STAT 0=<%02x> 1=<%02x>, CTL 1=<%02x> 2=<%02x>",
+		SAA716x_EPRD(VI0, INT_STATUS),
+		SAA716x_EPRD(VI1, INT_STATUS),
+		SAA716x_EPRD(VI0, INT_ENABLE),
+		SAA716x_EPRD(VI1, INT_ENABLE));
+
+	dprintk(SAA716x_DEBUG, 1, "FGPI STAT 0=<%02x> 1=<%02x>, CTL 1=<%02x> 2=<%02x>",
+		SAA716x_EPRD(FGPI0, INT_STATUS),
+		SAA716x_EPRD(FGPI1, INT_STATUS),
+		SAA716x_EPRD(FGPI0, INT_ENABLE),
+		SAA716x_EPRD(FGPI0, INT_ENABLE));
+
+	dprintk(SAA716x_DEBUG, 1, "FGPI STAT 2=<%02x> 3=<%02x>, CTL 2=<%02x> 3=<%02x>",
+		SAA716x_EPRD(FGPI2, INT_STATUS),
+		SAA716x_EPRD(FGPI3, INT_STATUS),
+		SAA716x_EPRD(FGPI2, INT_ENABLE),
+		SAA716x_EPRD(FGPI3, INT_ENABLE));
+
+	dprintk(SAA716x_DEBUG, 1, "AI STAT 0=<%02x> 1=<%02x>, CTL 0=<%02x> 1=<%02x>",
+		SAA716x_EPRD(AI0, AI_STATUS),
+		SAA716x_EPRD(AI1, AI_STATUS),
+		SAA716x_EPRD(AI0, AI_CTL),
+		SAA716x_EPRD(AI1, AI_CTL));
+
+	dprintk(SAA716x_DEBUG, 1, "I2C STAT 0=<%02x> 1=<%02x>, CTL 0=<%02x> 1=<%02x>",
+		SAA716x_EPRD(I2C_A, INT_STATUS),
+		SAA716x_EPRD(I2C_B, INT_STATUS),
+		SAA716x_EPRD(I2C_A, INT_ENABLE),
+		SAA716x_EPRD(I2C_B, INT_ENABLE));
+
+	dprintk(SAA716x_DEBUG, 1, "DCS STAT=<%02x>, CTL=<%02x>",
+		SAA716x_EPRD(DCS, DCSC_INT_STATUS),
+		SAA716x_EPRD(DCS, DCSC_INT_ENABLE));
+#endif
+
+	if (stat_l) {
+		if (stat_l & MSI_INT_TAGACK_FGPI_0) {
+			tasklet_schedule(&saa716x->fgpi[0].tasklet);
+		}
+		if (stat_l & MSI_INT_TAGACK_FGPI_1) {
+			tasklet_schedule(&saa716x->fgpi[1].tasklet);
+		}
+		if (stat_l & MSI_INT_TAGACK_FGPI_2) {
+			tasklet_schedule(&saa716x->fgpi[2].tasklet);
+		}
+		if (stat_l & MSI_INT_TAGACK_FGPI_3) {
+			tasklet_schedule(&saa716x->fgpi[3].tasklet);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void demux_worker(unsigned long data)
+{
+	struct saa716x_fgpi_stream_port *fgpi_entry = (struct saa716x_fgpi_stream_port *)data;
+	struct saa716x_dev *saa716x = fgpi_entry->saa716x;
+	struct dvb_demux *demux;
+	u32 fgpi_index;
+	u32 i;
+	u32 write_index;
+
+	fgpi_index = fgpi_entry->dma_channel - 6;
+	demux = NULL;
+	for (i = 0; i < saa716x->config->adapters; i++) {
+		if (saa716x->config->adap_config[i].ts_port == fgpi_index) {
+			demux = &saa716x->saa716x_adap[i].demux;
+			break;
+		}
+	}
+	if (demux == NULL) {
+		printk(KERN_ERR "%s: unexpected channel %u\n",
+		       __func__, fgpi_entry->dma_channel);
+		return;
+	}
+
+	write_index = saa716x_fgpi_get_write_index(saa716x, fgpi_index);
+	if (write_index < 0)
+		return;
+
+	dprintk(SAA716x_DEBUG, 1, "dma buffer = %d", write_index);
+
+	if (write_index == fgpi_entry->read_index) {
+		printk(KERN_DEBUG "%s: called but nothing to do\n", __func__);
+		return;
+	}
+
+	do {
+		u8 *data = (u8 *)fgpi_entry->dma_buf[fgpi_entry->read_index].mem_virt;
+
+		pci_dma_sync_sg_for_cpu(saa716x->pdev,
+			fgpi_entry->dma_buf[fgpi_entry->read_index].sg_list,
+			fgpi_entry->dma_buf[fgpi_entry->read_index].list_len,
+			PCI_DMA_FROMDEVICE);
+
+		dvb_dmx_swfilter(demux, data, 348 * 188);
+
+		fgpi_entry->read_index = (fgpi_entry->read_index + 1) & 7;
+	} while (write_index != fgpi_entry->read_index);
+}
+
+/*
+ * Twinhan/Azurewave VP-6090
+ * DVB-S Frontend: 2x MB86A16
+ * DVB-T Frontend: 2x TDA10046 + TDA8275
+ */
+#define SAA716x_MODEL_TWINHAN_VP6090	"Twinhan/Azurewave VP-6090"
+#define SAA716x_DEV_TWINHAN_VP6090	"2xDVB-S + 2xDVB-T + 2xAnalog"
+
+static int tda1004x_vp6090_request_firmware(struct dvb_frontend *fe,
+					      const struct firmware **fw,
+					      char *name)
+{
+	struct saa716x_adapter *adapter = fe->dvb->priv;
+
+	return request_firmware(fw, name, &adapter->saa716x->pdev->dev);
+}
+
+static struct tda1004x_config tda1004x_vp6090_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	= tda1004x_vp6090_request_firmware,
+};
+
+static int vp6090_dvbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct saa716x_dev *saa716x = fe->dvb->priv;
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		dprintk(SAA716x_ERROR, 1, "Polarization=[13V]");
+		break;
+	case SEC_VOLTAGE_18:
+		dprintk(SAA716x_ERROR, 1, "Polarization=[18V]");
+		break;
+	case SEC_VOLTAGE_OFF:
+		dprintk(SAA716x_ERROR, 1, "Frontend (dummy) POWERDOWN");
+		break;
+	default:
+		dprintk(SAA716x_ERROR, 1, "Invalid = (%d)", (u32 ) voltage);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct mb86a16_config vp6090_mb86a16_config = {
+	.demod_address	= 0x08,
+	.set_voltage	= vp6090_dvbs_set_voltage,
+};
+
+static int saa716x_vp6090_frontend_attach(struct saa716x_adapter *adapter, int count)
+{
+	struct saa716x_dev *saa716x = adapter->saa716x;
+	struct saa716x_i2c *i2c = &saa716x->i2c[count];
+
+	dprintk(SAA716x_ERROR, 1, "Adapter (%d) SAA716x frontend Init", count);
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) Device ID=%02x", count, saa716x->pdev->subsystem_device);
+
+	dprintk(SAA716x_ERROR, 1, "Adapter (%d) Power ON", count);
+
+	saa716x_gpio_set_output(saa716x, 11);
+	saa716x_gpio_set_output(saa716x, 10);
+	saa716x_gpio_write(saa716x, 11, 1);
+	saa716x_gpio_write(saa716x, 10, 1);
+	msleep(100);
+#if 0
+	dprintk(SAA716x_ERROR, 1, "Probing for MB86A16 (DVB-S/DSS)");
+	adapter->fe = mb86a16_attach(&vp6090_mb86a16_config, &i2c->i2c_adapter);
+	if (adapter->fe) {
+		dprintk(SAA716x_ERROR, 1, "found MB86A16 DVB-S/DSS frontend @0x%02x",
+			vp6090_mb86a16_config.demod_address);
+
+	} else {
+		goto exit;
+	}
+#endif
+	adapter->fe = tda10046_attach(&tda1004x_vp6090_config, &i2c->i2c_adapter);
+	if (adapter->fe == NULL) {
+		dprintk(SAA716x_ERROR, 1, "Frontend attach failed");
+		return -ENODEV;
+	} else {
+		dprintk(SAA716x_ERROR, 1, "Done!");
+		return 0;
+	}
+
+	return 0;
+}
+
+static struct saa716x_config saa716x_vp6090_config = {
+	.model_name		= SAA716x_MODEL_TWINHAN_VP6090,
+	.dev_type		= SAA716x_DEV_TWINHAN_VP6090,
+	.boot_mode		= SAA716x_EXT_BOOT,
+	.adapters		= 1,
+	.frontend_attach	= saa716x_vp6090_frontend_attach,
+	.irq_handler		= saa716x_hybrid_pci_irq,
+	.i2c_rate		= SAA716x_I2C_RATE_100,
+};
+
+/*
+ * NXP Reference design (Atlantis)
+ * 2x DVB-T Frontend: 2x TDA10046
+ * Analog Decoder: 2x Internal
+ */
+#define SAA716x_MODEL_NXP_ATLANTIS	"Atlantis reference board"
+#define SAA716x_DEV_NXP_ATLANTIS	"2x DVB-T + 2x Analog"
+
+static int tda1004x_atlantis_request_firmware(struct dvb_frontend *fe,
+					      const struct firmware **fw,
+					      char *name)
+{
+	struct saa716x_adapter *adapter = fe->dvb->priv;
+
+	return request_firmware(fw, name, &adapter->saa716x->pdev->dev);
+}
+
+static struct tda1004x_config tda1004x_atlantis_config = {
+	.demod_address		= 0x8,
+	.invert			= 0,
+	.invert_oclk		= 0,
+	.xtal_freq		= TDA10046_XTAL_16M,
+	.agc_config		= TDA10046_AGC_TDA827X,
+	.if_freq		= TDA10046_FREQ_045,
+	.request_firmware	= tda1004x_atlantis_request_firmware,
+	.tuner_address          = 0x60,
+};
+
+static struct tda827x_config tda827x_atlantis_config = {
+	.init		= NULL,
+	.sleep		= NULL,
+	.config		= 0,
+	.switch_addr	= 0,
+	.agcf		= NULL,
+};
+
+static int saa716x_atlantis_frontend_attach(struct saa716x_adapter *adapter,
+					    int count)
+{
+	struct saa716x_dev *saa716x = adapter->saa716x;
+	struct saa716x_i2c *i2c;
+	u8 i2c_buf[3] = { 0x05, 0x23, 0x01 }; /* activate the silent I2C bus */
+	struct i2c_msg msg = {
+		.addr  = 0x42 >> 1,
+		.flags = 0,
+		.buf   = i2c_buf,
+		.len   = sizeof(i2c_buf)
+	};
+
+	if (count < saa716x->config->adapters) {
+		u32 reset_gpio;
+
+		dprintk(SAA716x_DEBUG, 1, "Adapter (%d) SAA716x frontend Init",
+			count);
+		dprintk(SAA716x_DEBUG, 1, "Adapter (%d) Device ID=%02x", count,
+			saa716x->pdev->subsystem_device);
+
+		if (count == 0) {
+			reset_gpio = 14;
+			i2c = &saa716x->i2c[SAA716x_I2C_BUS_A];
+		} else {
+			reset_gpio = 15;
+			i2c = &saa716x->i2c[SAA716x_I2C_BUS_B];
+		}
+
+		/* activate the silent I2C bus */
+		i2c_transfer(&i2c->i2c_adapter, &msg, 1);
+
+		saa716x_gpio_set_output(saa716x, reset_gpio);
+
+		/* Reset the demodulator */
+		saa716x_gpio_write(saa716x, reset_gpio, 1);
+		msleep(10);
+		saa716x_gpio_write(saa716x, reset_gpio, 0);
+		msleep(10);
+		saa716x_gpio_write(saa716x, reset_gpio, 1);
+		msleep(10);
+
+		adapter->fe = tda10046_attach(&tda1004x_atlantis_config,
+					      &i2c->i2c_adapter);
+		if (adapter->fe == NULL)
+			goto exit;
+
+		dprintk(SAA716x_ERROR, 1,
+			"found TDA10046 DVB-T frontend @0x%02x",
+			tda1004x_atlantis_config.demod_address);
+
+		if (dvb_attach(tda827x_attach, adapter->fe,
+			       tda1004x_atlantis_config.tuner_address,
+			       &i2c->i2c_adapter, &tda827x_atlantis_config)) {
+			dprintk(SAA716x_ERROR, 1, "found TDA8275 tuner @0x%02x",
+				tda1004x_atlantis_config.tuner_address);
+		} else {
+			goto exit;
+		}
+
+		dprintk(SAA716x_ERROR, 1, "Done!");
+		return 0;
+	}
+
+exit:
+	dprintk(SAA716x_ERROR, 1, "Frontend attach failed");
+	return -ENODEV;
+}
+
+static struct saa716x_config saa716x_atlantis_config = {
+	.model_name		= SAA716x_MODEL_NXP_ATLANTIS,
+	.dev_type		= SAA716x_DEV_NXP_ATLANTIS,
+	.boot_mode		= SAA716x_EXT_BOOT,
+	.adapters		= 2,
+	.frontend_attach	= saa716x_atlantis_frontend_attach,
+	.irq_handler		= saa716x_hybrid_pci_irq,
+	.i2c_rate		= SAA716x_I2C_RATE_100,
+	.adap_config		= {
+		{
+			/* Adapter 0 */
+			.ts_port = 3, /* using FGPI 3 */
+			.worker = demux_worker
+		},
+		{
+			/* Adapter 1 */
+			.ts_port = 0, /* using FGPI 0 */
+			.worker = demux_worker
+		}
+	}
+};
+
+/*
+ * NXP Reference design (NEMO)
+ * DVB-T Frontend: 1x TDA10046 + TDA8275
+ * Analog Decoder: External SAA7136
+ */
+#define SAA716x_MODEL_NXP_NEMO		"NEMO reference board"
+#define SAA716x_DEV_NXP_NEMO		"DVB-T + Analog"
+
+static int tda1004x_nemo_request_firmware(struct dvb_frontend *fe,
+					  const struct firmware **fw,
+					  char *name)
+{
+	struct saa716x_adapter *adapter = fe->dvb->priv;
+
+	return request_firmware(fw, name, &adapter->saa716x->pdev->dev);
+}
+
+static struct tda1004x_config tda1004x_nemo_config = {
+	.demod_address		= 0x8,
+	.invert			= 0,
+	.invert_oclk		= 0,
+	.xtal_freq		= TDA10046_XTAL_16M,
+	.agc_config		= TDA10046_AGC_TDA827X,
+	.if_freq		= TDA10046_FREQ_045,
+	.request_firmware	= tda1004x_nemo_request_firmware,
+	.tuner_address          = 0x60,
+};
+
+static struct tda827x_config tda827x_nemo_config = {
+	.init		= NULL,
+	.sleep		= NULL,
+	.config		= 0,
+	.switch_addr	= 0,
+	.agcf		= NULL,
+};
+
+static int saa716x_nemo_frontend_attach(struct saa716x_adapter *adapter, int count)
+{
+	struct saa716x_dev *saa716x = adapter->saa716x;
+	struct saa716x_i2c *demod_i2c = &saa716x->i2c[SAA716x_I2C_BUS_B];
+	struct saa716x_i2c *tuner_i2c = &saa716x->i2c[SAA716x_I2C_BUS_A];
+
+
+	if (count  == 0) {
+		dprintk(SAA716x_DEBUG, 1, "Adapter (%d) SAA716x frontend Init", count);
+		dprintk(SAA716x_DEBUG, 1, "Adapter (%d) Device ID=%02x", count, saa716x->pdev->subsystem_device);
+		dprintk(SAA716x_ERROR, 1, "Adapter (%d) Power ON", count);
+
+		/* GPIO 26 controls a +15dB gain */
+		saa716x_gpio_set_output(saa716x, 26);
+		saa716x_gpio_write(saa716x, 26, 0);
+
+		saa716x_gpio_set_output(saa716x, 14);
+
+		/* Reset the demodulator */
+		saa716x_gpio_write(saa716x, 14, 1);
+		msleep(10);
+		saa716x_gpio_write(saa716x, 14, 0);
+		msleep(10);
+		saa716x_gpio_write(saa716x, 14, 1);
+		msleep(10);
+
+		adapter->fe = tda10046_attach(&tda1004x_nemo_config,
+					      &demod_i2c->i2c_adapter);
+		if (adapter->fe) {
+			dprintk(SAA716x_ERROR, 1, "found TDA10046 DVB-T frontend @0x%02x",
+				tda1004x_nemo_config.demod_address);
+
+		} else {
+			goto exit;
+		}
+		if (dvb_attach(tda827x_attach, adapter->fe,
+			       tda1004x_nemo_config.tuner_address,
+			       &tuner_i2c->i2c_adapter, &tda827x_nemo_config)) {
+			dprintk(SAA716x_ERROR, 1, "found TDA8275 tuner @0x%02x",
+				tda1004x_nemo_config.tuner_address);
+		} else {
+			goto exit;
+		}
+		dprintk(SAA716x_ERROR, 1, "Done!");
+	}
+
+	return 0;
+exit:
+	dprintk(SAA716x_ERROR, 1, "Frontend attach failed");
+	return -ENODEV;
+}
+
+static struct saa716x_config saa716x_nemo_config = {
+	.model_name		= SAA716x_MODEL_NXP_NEMO,
+	.dev_type		= SAA716x_DEV_NXP_NEMO,
+	.boot_mode		= SAA716x_EXT_BOOT,
+	.adapters		= 1,
+	.frontend_attach	= saa716x_nemo_frontend_attach,
+	.irq_handler		= saa716x_hybrid_pci_irq,
+	.i2c_rate		= SAA716x_I2C_RATE_100,
+
+	.adap_config		= {
+		{
+			/* Adapter 0 */
+			.ts_port = 3, /* using FGPI 3 */
+			.worker = demux_worker
+		}
+	}
+};
+
+
+#define SAA716x_MODEL_AVERMEDIA_HC82	"Avermedia HC82 Express-54"
+#define SAA716x_DEV_AVERMEDIA_HC82	"DVB-T + Analog"
+
+#if 0
+static struct zl10353_config saa716x_averhc82_zl10353_config = {
+	.demod_address		= 0x1f,
+	.adc_clock		= 450560,
+	.if2			= 361667,
+	.no_tuner		= 1,
+	.parallel_ts		= 1,
+};
+#endif
+
+static int saa716x_averhc82_frontend_attach(struct saa716x_adapter *adapter, int count)
+{
+	struct saa716x_dev *saa716x = adapter->saa716x;
+
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) SAA716x frontend Init", count);
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) Device ID=%02x", count, saa716x->pdev->subsystem_device);
+
+//	adapter->fe = zl10353_attach(&saa716x_averhc82_zl10353_config, &i2c->i2c_adapter);
+
+
+	return 0;
+}
+
+static struct saa716x_config saa716x_averhc82_config = {
+	.model_name		= SAA716x_MODEL_AVERMEDIA_HC82,
+	.dev_type		= SAA716x_DEV_AVERMEDIA_HC82,
+	.boot_mode		= SAA716x_EXT_BOOT,
+	.adapters		= 1,
+	.frontend_attach	= saa716x_averhc82_frontend_attach,
+	.irq_handler		= saa716x_hybrid_pci_irq,
+	.i2c_rate		= SAA716x_I2C_RATE_100,
+};
+
+#define SAA716x_MODEL_AVERMEDIA_H788	"Avermedia H788"
+#define SAA716x_DEV_AVERMEDIA_H788	"DVB-T + Analaog"
+
+static int saa716x_averh88_frontend_attach(struct saa716x_adapter *adapter, int count)
+{
+	struct saa716x_dev *saa716x = adapter->saa716x;
+
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) SAA716x frontend Init", count);
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%d) Device ID=%02x", count, saa716x->pdev->subsystem_device);
+
+	return -ENODEV;
+}
+
+static struct saa716x_config saa716x_averh788_config = {
+	.model_name		= SAA716x_MODEL_AVERMEDIA_H788,
+	.dev_type		= SAA716x_DEV_AVERMEDIA_H788,
+	.boot_mode		= SAA716x_EXT_BOOT,
+	.adapters		= 1,
+	.frontend_attach	= saa716x_averh88_frontend_attach,
+	.irq_handler		= saa716x_hybrid_pci_irq,
+	.i2c_rate		= SAA716x_I2C_RATE_100,
+};
+
+static struct pci_device_id saa716x_hybrid_pci_table[] = {
+
+	MAKE_ENTRY(TWINHAN_TECHNOLOGIES, TWINHAN_VP_6090, SAA7162, &saa716x_vp6090_config),
+	MAKE_ENTRY(AVERMEDIA, AVERMEDIA_HC82, SAA7160, &saa716x_averhc82_config),
+	MAKE_ENTRY(AVERMEDIA, AVERMEDIA_H788, SAA7160, &saa716x_averh788_config),
+	MAKE_ENTRY(KWORLD, KWORLD_DVB_T_PE310, SAA7162, &saa716x_atlantis_config),
+	MAKE_ENTRY(NXP_REFERENCE_BOARD, PCI_ANY_ID, SAA7162, &saa716x_atlantis_config),
+	MAKE_ENTRY(NXP_REFERENCE_BOARD, PCI_ANY_ID, SAA7160, &saa716x_nemo_config),
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, saa716x_hybrid_pci_table);
+
+static struct pci_driver saa716x_hybrid_pci_driver = {
+	.name			= DRIVER_NAME,
+	.id_table		= saa716x_hybrid_pci_table,
+	.probe			= saa716x_hybrid_pci_probe,
+	.remove			= saa716x_hybrid_pci_remove,
+};
+
+static int __init saa716x_hybrid_init(void)
+{
+	return pci_register_driver(&saa716x_hybrid_pci_driver);
+}
+
+static void __exit saa716x_hybrid_exit(void)
+{
+	return pci_unregister_driver(&saa716x_hybrid_pci_driver);
+}
+
+module_init(saa716x_hybrid_init);
+module_exit(saa716x_hybrid_exit);
+
+MODULE_DESCRIPTION("SAA716x Hybrid driver");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_LICENSE("GPL");
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_hybrid.h linux/drivers/media/pci/saa716x/saa716x_hybrid.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_hybrid.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_hybrid.h	2017-08-11 23:31:25.849389358 +0200
@@ -0,0 +1,13 @@
+#ifndef __SAA716x_HYBRID_H
+#define __SAA716x_HYBRID_H
+
+#define TWINHAN_TECHNOLOGIES	0x1822
+#define AVERMEDIA		0x1461
+#define KWORLD			0x17DE
+
+#define TWINHAN_VP_6090		0x0027
+#define AVERMEDIA_HC82		0x2355
+#define AVERMEDIA_H788		0x1455
+#define KWORLD_DVB_T_PE310	0x7521
+
+#endif /* __SAA716x_HYBRID_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_i2c.c linux/drivers/media/pci/saa716x/saa716x_i2c.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_i2c.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_i2c.c	2017-08-11 23:31:25.849389358 +0200
@@ -0,0 +1,728 @@
+#include <linux/delay.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include <linux/i2c.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_i2c_reg.h"
+#include "saa716x_msi_reg.h"
+#include "saa716x_cgu_reg.h"
+
+#include "saa716x_i2c.h"
+#include "saa716x_msi.h"
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+
+#define SAA716x_I2C_TXFAIL	(I2C_ERROR_IBE		| \
+				 I2C_ACK_INTER_MTNA	| \
+				 I2C_FAILURE_INTER_MAF)
+
+#define SAA716x_I2C_TXBUSY	(I2C_TRANSMIT		| \
+				 I2C_TRANSMIT_PROG)
+
+#define SAA716x_I2C_RXBUSY	(I2C_RECEIVE		| \
+				 I2C_RECEIVE_CLEAR)
+
+static const char* state[] = {
+	"Idle",
+	"DoneStop",
+	"Busy",
+	"TOscl",
+	"TOarb",
+	"DoneWrite",
+	"DoneRead",
+	"DoneWriteTO",
+	"DoneReadTO",
+	"NoDevice",
+	"NoACK",
+	"BUSErr",
+	"ArbLost",
+	"SEQErr",
+	"STErr"
+};
+
+int saa716x_i2c_irqevent(struct saa716x_dev *saa716x, u8 bus)
+{
+	u32 stat, mask;
+	u32 *I2C_DEV;
+
+	BUG_ON(saa716x == NULL);
+	I2C_DEV = saa716x->I2C_DEV;
+
+	stat = SAA716x_EPRD(I2C_DEV[bus], INT_STATUS);
+	mask = SAA716x_EPRD(I2C_DEV[bus], INT_ENABLE);
+	saa716x->i2c[bus].i2c_stat = stat;
+	dprintk(SAA716x_DEBUG, 0, "Bus(%d) I2C event: Status=<%s> --> Stat=<%02x> Mask=<%02x>",
+		bus, state[stat], stat, mask);
+
+	if (!(stat & mask))
+		return -1;
+
+	SAA716x_EPWR(I2C_DEV[bus], INT_CLR_STATUS, stat);
+
+	if (stat & I2C_INTERRUPT_STFNF)
+		dprintk(SAA716x_DEBUG, 0, "<STFNF> ");
+
+	if (stat & I2C_INTERRUPT_MTFNF) {
+		dprintk(SAA716x_DEBUG, 0, "<MTFNF> ");
+	}
+
+	if (stat & I2C_INTERRUPT_RFDA)
+		dprintk(SAA716x_DEBUG, 0, "<RFDA> ");
+
+	if (stat & I2C_INTERRUPTE_RFF)
+		dprintk(SAA716x_DEBUG, 0, "<RFF> ");
+
+	if (stat & I2C_SLAVE_INTERRUPT_STDR)
+		dprintk(SAA716x_DEBUG, 0, "<STDR> ");
+
+	if (stat & I2C_MASTER_INTERRUPT_MTDR) {
+		dprintk(SAA716x_DEBUG, 0, "<MTDR> ");
+	}
+
+	if (stat & I2C_ERROR_IBE)
+		dprintk(SAA716x_DEBUG, 0, "<IBE> ");
+
+	if (stat & I2C_MODE_CHANGE_INTER_MSMC)
+		dprintk(SAA716x_DEBUG, 0, "<MSMC> ");
+
+	if (stat & I2C_SLAVE_RECEIVE_INTER_SRSD)
+		dprintk(SAA716x_DEBUG, 0, "<SRSD> ");
+
+	if (stat & I2C_SLAVE_TRANSMIT_INTER_STSD)
+		dprintk(SAA716x_DEBUG, 0, "<STSD> ");
+
+	if (stat & I2C_ACK_INTER_MTNA)
+		dprintk(SAA716x_DEBUG, 0, "<MTNA> ");
+
+	if (stat & I2C_FAILURE_INTER_MAF)
+		dprintk(SAA716x_DEBUG, 0, "<MAF> ");
+
+	if (stat & I2C_INTERRUPT_MTD)
+		dprintk(SAA716x_DEBUG, 0, "<MTD> ");
+
+	return 0;
+}
+
+static irqreturn_t saa716x_i2c_irq(int irq, void *dev_id)
+{
+	struct saa716x_dev *saa716x	= (struct saa716x_dev *) dev_id;
+
+	if (unlikely(saa716x == NULL)) {
+		printk("%s: saa716x=NULL", __func__);
+		return IRQ_NONE;
+	}
+	dprintk(SAA716x_DEBUG, 1, "MSI STAT L=<%02x> H=<%02x>, CTL L=<%02x> H=<%02x>",
+		SAA716x_EPRD(MSI, MSI_INT_STATUS_L),
+		SAA716x_EPRD(MSI, MSI_INT_STATUS_H),
+		SAA716x_EPRD(MSI, MSI_INT_ENA_L),
+		SAA716x_EPRD(MSI, MSI_INT_ENA_H));
+
+	dprintk(SAA716x_DEBUG, 1, "I2C STAT 0=<%02x> 1=<%02x>, CTL 0=<%02x> 1=<%02x>",
+		SAA716x_EPRD(I2C_A, INT_STATUS),
+		SAA716x_EPRD(I2C_B, INT_STATUS),
+		SAA716x_EPRD(I2C_A, INT_CLR_STATUS),
+		SAA716x_EPRD(I2C_B, INT_CLR_STATUS));
+
+	return IRQ_HANDLED;
+}
+
+static void saa716x_term_xfer(struct saa716x_i2c *i2c, u32 I2C_DEV)
+{
+	struct saa716x_dev *saa716x = i2c->saa716x;
+
+	SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0xc0); /* Start: SCL/SDA High */
+	msleep(10);
+	SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0x80);
+	msleep(10);
+	SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0x00);
+	msleep(10);
+	SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0x80);
+	msleep(10);
+	SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0xc0);
+
+	return;
+}
+
+static void saa716x_i2c_hwdeinit(struct saa716x_i2c *i2c, u32 I2C_DEV)
+{
+	struct saa716x_dev *saa716x = i2c->saa716x;
+
+	/* Disable all interrupts and clear status */
+	SAA716x_EPWR(I2C_DEV, INT_CLR_ENABLE, 0x1fff);
+	SAA716x_EPWR(I2C_DEV, INT_CLR_STATUS, 0x1fff);
+}
+
+static int saa716x_i2c_hwinit(struct saa716x_i2c *i2c, u32 I2C_DEV)
+{
+	struct saa716x_dev *saa716x = i2c->saa716x;
+	struct i2c_adapter *adapter = &i2c->i2c_adapter;
+
+	int i, err = 0;
+	u32 reg;
+
+	reg = SAA716x_EPRD(I2C_DEV, I2C_STATUS);
+	if (!(reg & 0xd)) {
+		dprintk(SAA716x_ERROR, 1, "Adapter (%02x) %s RESET failed, Exiting !",
+			I2C_DEV, adapter->name);
+		err = -EIO;
+		goto exit;
+	}
+
+	/* Flush queue */
+	SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0xcc);
+
+	/* Disable all interrupts and clear status */
+	SAA716x_EPWR(I2C_DEV, INT_CLR_ENABLE, 0x1fff);
+	SAA716x_EPWR(I2C_DEV, INT_CLR_STATUS, 0x1fff);
+
+	/* Reset I2C Core and generate a delay */
+	SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0xc1);
+
+	for (i = 0; i < 100; i++) {
+		reg = SAA716x_EPRD(I2C_DEV, I2C_CONTROL);
+		if (reg == 0xc0) {
+			dprintk(SAA716x_ERROR, 1, "Adapter (%02x) %s RESET",
+				I2C_DEV, adapter->name);
+			break;
+		}
+		msleep(1);
+
+		if (i == 99)
+			err = -EIO;
+	}
+
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "Adapter (%02x) %s RESET failed",
+			I2C_DEV, adapter->name);
+
+		saa716x_term_xfer(i2c, I2C_DEV);
+		err = -EIO;
+		goto exit;
+	}
+
+	/* I2C Rate Setup */
+	switch (i2c->i2c_rate) {
+	case SAA716x_I2C_RATE_400:
+
+		dprintk(SAA716x_DEBUG, 1, "Initializing Adapter %s @ 400k", adapter->name);
+		SAA716x_EPWR(I2C_DEV, I2C_CLOCK_DIVISOR_HIGH, 0x1a); /* 0.5 * 27MHz/400kHz */
+		SAA716x_EPWR(I2C_DEV, I2C_CLOCK_DIVISOR_LOW,  0x21); /* 0.5 * 27MHz/400kHz */
+		SAA716x_EPWR(I2C_DEV, I2C_SDA_HOLD, 0x19);
+		break;
+
+	case SAA716x_I2C_RATE_100:
+
+		dprintk(SAA716x_DEBUG, 1, "Initializing Adapter %s @ 100k", adapter->name);
+		SAA716x_EPWR(I2C_DEV, I2C_CLOCK_DIVISOR_HIGH, 0x68); /* 0.5 * 27MHz/100kHz */
+		SAA716x_EPWR(I2C_DEV, I2C_CLOCK_DIVISOR_LOW,  0x87); /* 0.5 * 27MHz/100kHz */
+		SAA716x_EPWR(I2C_DEV, I2C_SDA_HOLD, 0x60);
+		break;
+
+	default:
+
+		dprintk(SAA716x_ERROR, 1, "Adapter %s Unknown Rate (Rate=0x%02x)",
+			adapter->name,
+			i2c->i2c_rate);
+
+		break;
+	}
+
+	/* Disable all interrupts and clear status */
+	SAA716x_EPWR(I2C_DEV, INT_CLR_ENABLE, 0x1fff);
+	SAA716x_EPWR(I2C_DEV, INT_CLR_STATUS, 0x1fff);
+
+	if (i2c->i2c_mode >= SAA716x_I2C_MODE_IRQ) {
+		/* Enabled interrupts:
+		* Master Transaction Done,
+		* Master Transaction Data Request
+		* (0x81)
+		*/
+		msleep(5);
+
+		SAA716x_EPWR(I2C_DEV, INT_SET_ENABLE,
+			I2C_SET_ENABLE_MTDR | I2C_SET_ENABLE_MTD);
+
+		/* Check interrupt enable status */
+		reg = SAA716x_EPRD(I2C_DEV, INT_ENABLE);
+		if (reg != 0x81) {
+
+			dprintk(SAA716x_ERROR, 1,
+				"Adapter (%d) %s Interrupt enable failed, Exiting !",
+				i,
+				adapter->name);
+
+			err = -EIO;
+			goto exit;
+		}
+	}
+
+	/* Check status */
+	reg = SAA716x_EPRD(I2C_DEV, I2C_STATUS);
+	if (!(reg & 0xd)) {
+
+		dprintk(SAA716x_ERROR, 1,
+			"Adapter (%02x) %s has bad state, Exiting !",
+			I2C_DEV,
+			adapter->name);
+
+		err = -EIO;
+		goto exit;
+	}
+#if 0
+	saa716x_add_irqvector(saa716x,
+				i2c_vec[i].vector,
+				i2c_vec[i].edge,
+				i2c_vec[i].handler,
+				SAA716x_I2C_ADAPTER(i));
+#endif
+	reg = SAA716x_EPRD(CGU, CGU_SCR_3);
+	dprintk(SAA716x_DEBUG, 1, "Adapter (%02x) Autowake <%d> Active <%d>",
+		I2C_DEV,
+		(reg >> 1) & 0x01,
+		reg & 0x01);
+
+	return 0;
+exit:
+	return err;
+}
+
+static int saa716x_i2c_send(struct saa716x_i2c *i2c, u32 I2C_DEV, u32 data)
+{
+	struct saa716x_dev *saa716x = i2c->saa716x;
+	int i, err = 0;
+	u32 reg;
+
+	if (i2c->i2c_mode >= SAA716x_I2C_MODE_IRQ) {
+		/* Write to FIFO */
+		SAA716x_EPWR(I2C_DEV, TX_FIFO, data);
+		return 0;
+	}
+
+	/* Check FIFO status before TX */
+	reg = SAA716x_EPRD(I2C_DEV, I2C_STATUS);
+	i2c->stat_tx_prior = reg;
+	if (reg & SAA716x_I2C_TXBUSY) {
+		for (i = 0; i < 100; i++) {
+			/* TODO! check for hotplug devices */
+			msleep(10);
+			reg = SAA716x_EPRD(I2C_DEV, I2C_STATUS);
+
+			if (reg & SAA716x_I2C_TXBUSY) {
+				dprintk(SAA716x_ERROR, 1, "FIFO full or Blocked");
+
+				err = saa716x_i2c_hwinit(i2c, I2C_DEV);
+				if (err < 0) {
+					dprintk(SAA716x_ERROR, 1, "Error Reinit");
+					err = -EIO;
+					goto exit;
+				}
+			} else {
+				break;
+			}
+		}
+	}
+
+	/* Write to FIFO */
+	SAA716x_EPWR(I2C_DEV, TX_FIFO, data);
+
+	/* Check for data write */
+	for (i = 0; i < 1000; i++) {
+		/* TODO! check for hotplug devices */
+		reg = SAA716x_EPRD(I2C_DEV, I2C_STATUS);
+		if (reg & I2C_TRANSMIT_CLEAR) {
+			break;
+		}
+	}
+	i2c->stat_tx_done = reg;
+
+	if (!(reg & I2C_TRANSMIT_CLEAR)) {
+		dprintk(SAA716x_ERROR, 1, "TXFIFO not empty after Timeout, tried %d loops!", i);
+		err = -EIO;
+		goto exit;
+	}
+
+	return err;
+
+exit:
+	dprintk(SAA716x_ERROR, 1, "I2C Send failed (Err=%d)", err);
+	return err;
+}
+
+static int saa716x_i2c_recv(struct saa716x_i2c *i2c, u32 I2C_DEV, u32 *data)
+{
+	struct saa716x_dev *saa716x = i2c->saa716x;
+	int i, err = 0;
+	u32 reg;
+
+	/* Check FIFO status before RX */
+	for (i = 0; i < 1000; i++) {
+		reg = SAA716x_EPRD(I2C_DEV, I2C_STATUS);
+		if (!(reg & SAA716x_I2C_RXBUSY)) {
+			break;
+		}
+	}
+	if (reg & SAA716x_I2C_RXBUSY) {
+		dprintk(SAA716x_INFO, 1, "FIFO empty");
+		err = -EIO;
+		goto exit;
+	}
+
+	/* Read from FIFO */
+	*data = SAA716x_EPRD(I2C_DEV, RX_FIFO);
+
+	return 0;
+exit:
+	dprintk(SAA716x_ERROR, 1, "Error Reading data, err=%d", err);
+	return err;
+}
+
+static void saa716x_i2c_irq_start(struct saa716x_i2c *i2c, u32 I2C_DEV)
+{
+	struct saa716x_dev *saa716x = i2c->saa716x;
+
+	if (i2c->i2c_mode == SAA716x_I2C_MODE_POLLING)
+		return;
+
+	i2c->i2c_op = 1;
+	SAA716x_EPWR(I2C_DEV, INT_CLR_STATUS, 0x1fff);
+}
+
+static int saa716x_i2c_irq_wait(struct saa716x_i2c *i2c, u32 I2C_DEV)
+{
+	struct saa716x_dev *saa716x = i2c->saa716x;
+	unsigned long timeout;
+	int err = 0;
+
+	if (i2c->i2c_mode == SAA716x_I2C_MODE_POLLING)
+		return 0;
+
+	timeout = HZ/100 + 1; /* 10ms */
+	timeout = wait_event_interruptible_timeout(i2c->i2c_wq, i2c->i2c_op == 0, timeout);
+	if (timeout == -ERESTARTSYS || i2c->i2c_op) {
+		SAA716x_EPWR(I2C_DEV, INT_CLR_STATUS, 0x1fff);
+		if (timeout == -ERESTARTSYS) {
+			/* a signal arrived */
+			err = -ERESTARTSYS;
+		} else {
+			dprintk(SAA716x_ERROR, 1, "timed out waiting for end of xfer!");
+			err = -EIO;
+		}
+	}
+	return err;
+}
+
+static int saa716x_i2c_write_msg(struct saa716x_i2c *i2c, u32 I2C_DEV,
+				 u16 addr, u8 *buf, u16 len, u8 add_stop)
+{
+	struct saa716x_dev *saa716x = i2c->saa716x;
+	u32 data;
+	int err;
+	int i;
+	int bytes;
+
+	saa716x_i2c_irq_start(i2c, I2C_DEV);
+
+	/* first write START with I2C address */
+	data = I2C_START_BIT | (addr << 1);
+	dprintk(SAA716x_DEBUG, 1, "length=%d Addr:0x%02x", len, data);
+	err = saa716x_i2c_send(i2c, I2C_DEV, data);
+	if (err < 0) {
+		dprintk(SAA716x_ERROR, 1, "Address write failed");
+		goto exit;
+	}
+
+	bytes = i2c->block_size - 1;
+
+	/* now write the data */
+	while (len > 0) {
+		if (bytes == i2c->block_size) {
+			/* this is not the first round, so restart irq */
+			saa716x_i2c_irq_start(i2c, I2C_DEV);
+		}
+
+		if (bytes > len)
+			bytes = len;
+
+		for (i = 0; i < bytes; i++) {
+			data = buf[i];
+			dprintk(SAA716x_DEBUG, 0, "    <W %04x> 0x%02x\n", i, data);
+			if (add_stop && i == (len - 1))
+				data |= I2C_STOP_BIT;
+			err = saa716x_i2c_send(i2c, I2C_DEV, data);
+			if (err < 0) {
+				dprintk(SAA716x_ERROR, 1, "Data send failed");
+				goto exit;
+			}
+		}
+
+		err = saa716x_i2c_irq_wait(i2c, I2C_DEV);
+		if (err < 0) {
+			goto exit;
+		}
+
+		len -= bytes;
+		buf += bytes;
+		bytes = i2c->block_size;
+	}
+
+	return 0;
+
+exit:
+	dprintk(SAA716x_ERROR, 1, "Error writing data, err=%d", err);
+	return err;
+}
+
+static int saa716x_i2c_read_msg(struct saa716x_i2c *i2c, u32 I2C_DEV,
+				u16 addr, u8 *buf, u16 len, u8 add_stop)
+{
+	struct saa716x_dev *saa716x = i2c->saa716x;
+	u32 data;
+	int err;
+	int i;
+	int bytes;
+
+	saa716x_i2c_irq_start(i2c, I2C_DEV);
+
+	/* first write START with I2C address */
+	data = I2C_START_BIT | (addr << 1) | 1;
+	dprintk(SAA716x_DEBUG, 1, "length=%d Addr:0x%02x", len, data);
+	err = saa716x_i2c_send(i2c, I2C_DEV, data);
+	if (err < 0) {
+		dprintk(SAA716x_ERROR, 1, "Address write failed");
+		goto exit;
+	}
+
+	bytes = i2c->block_size - 1;
+
+	/* now read the data */
+	while (len > 0) {
+		if (bytes == i2c->block_size) {
+			/* this is not the first round, so restart irq */
+			saa716x_i2c_irq_start(i2c, I2C_DEV);
+		}
+
+		if (bytes > len)
+			bytes = len;
+
+		for (i = 0; i < bytes; i++) {
+			data = 0x00; /* dummy write for reading */
+			if (add_stop && i == (len - 1))
+				data |= I2C_STOP_BIT;
+			err = saa716x_i2c_send(i2c, I2C_DEV, data);
+			if (err < 0) {
+				dprintk(SAA716x_ERROR, 1, "Data send failed");
+				goto exit;
+			}
+		}
+
+		err = saa716x_i2c_irq_wait(i2c, I2C_DEV);
+		if (err < 0) {
+			goto exit;
+		}
+
+		for (i = 0; i < bytes; i++) {
+			err = saa716x_i2c_recv(i2c, I2C_DEV, &data);
+			if (err < 0) {
+				dprintk(SAA716x_ERROR, 1, "Data receive failed");
+				goto exit;
+			}
+			dprintk(SAA716x_DEBUG, 0, "    <R %04x> 0x%02x\n\n", i, data);
+			buf[i] = data;
+		}
+
+		len -= bytes;
+		buf += bytes;
+		bytes = i2c->block_size;
+	}
+
+	return 0;
+
+exit:
+	dprintk(SAA716x_ERROR, 1, "Error reading data, err=%d", err);
+	return err;
+}
+
+static int saa716x_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
+{
+	struct saa716x_i2c *i2c		= i2c_get_adapdata(adapter);
+	struct saa716x_dev *saa716x	= i2c->saa716x;
+
+	u32 DEV = SAA716x_I2C_BUS(i2c->i2c_dev);
+	int i, j, err = 0;
+	int t;
+
+	dprintk(SAA716x_DEBUG, 0, "\n");
+	dprintk(SAA716x_DEBUG, 1, "Bus(%02x) I2C transfer", DEV);
+	mutex_lock(&i2c->i2c_lock);
+
+	for (t = 0; t < 3; t++) {
+		for (i = 0; i < num; i++) {
+			if (msgs[i].flags & I2C_M_RD)
+				err = saa716x_i2c_read_msg(i2c, DEV,
+					msgs[i].addr, msgs[i].buf, msgs[i].len,
+					i == (num - 1));
+			else
+				err = saa716x_i2c_write_msg(i2c, DEV,
+					msgs[i].addr, msgs[i].buf, msgs[i].len,
+					i == (num - 1));
+			if (err < 0) {
+				err = -EIO;
+				goto retry;
+			}
+		}
+		break;
+retry:
+		dprintk(SAA716x_INFO, 1, "Error in Transfer, try %d", t);
+		for (i = 0; i < num; i++) {
+			dprintk(SAA716x_INFO, 1, "msg %d, addr = 0x%02x, len=%d, flags=0x%x",
+				i, msgs[i].addr, msgs[i].len, msgs[i].flags);
+			if (!(msgs[i].flags & I2C_M_RD)) {
+				for (j = 0; j < msgs[i].len; j++) {
+					dprintk(SAA716x_INFO, 1, "    <W %04x> 0x%02x",
+						j, msgs[i].buf[j]);
+				}
+			}
+		}
+		err = saa716x_i2c_hwinit(i2c, DEV);
+		if (err < 0) {
+			dprintk(SAA716x_ERROR, 1, "Error Reinit");
+			err = -EIO;
+			goto bail_out;
+		}
+	}
+
+	mutex_unlock(&i2c->i2c_lock);
+	return num;
+
+bail_out:
+	dprintk(SAA716x_ERROR, 1, "ERROR: Bailing out <%d>", err);
+	mutex_unlock(&i2c->i2c_lock);
+	return err;
+}
+
+static u32 saa716x_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm saa716x_algo = {
+	.master_xfer	= saa716x_i2c_xfer,
+	.functionality	= saa716x_i2c_func,
+};
+
+struct saa716x_i2cvec {
+	u32			vector;
+	enum saa716x_edge	edge;
+	irqreturn_t (*handler)(int irq, void *dev_id);
+};
+
+static const struct saa716x_i2cvec i2c_vec[] = {
+	{
+		.vector		= I2CINT_0,
+		.edge		= SAA716x_EDGE_RISING,
+		.handler	= saa716x_i2c_irq
+	}, {
+		.vector 	= I2CINT_1,
+		.edge		= SAA716x_EDGE_RISING,
+		.handler	= saa716x_i2c_irq
+	}
+};
+
+int saa716x_i2c_init(struct saa716x_dev *saa716x)
+{
+	struct pci_dev *pdev		= saa716x->pdev;
+	struct saa716x_i2c *i2c		= saa716x->i2c;
+	struct i2c_adapter *adapter	= NULL;
+
+	int i, err = 0;
+
+	dprintk(SAA716x_DEBUG, 1, "Initializing SAA%02x I2C Core",
+		saa716x->pdev->device);
+
+	for (i = 0; i < SAA716x_I2C_ADAPTERS; i++) {
+
+		mutex_init(&i2c->i2c_lock);
+
+		init_waitqueue_head(&i2c->i2c_wq);
+		i2c->i2c_op = 0;
+
+		i2c->i2c_dev	= i;
+		i2c->i2c_rate	= saa716x->config->i2c_rate;
+		i2c->i2c_mode	= saa716x->config->i2c_mode;
+		adapter		= &i2c->i2c_adapter;
+
+		if (i2c->i2c_mode == SAA716x_I2C_MODE_IRQ_BUFFERED)
+			i2c->block_size = 8;
+		else
+			i2c->block_size = 1;
+
+		if (adapter != NULL) {
+
+			i2c_set_adapdata(adapter, i2c);
+
+			strcpy(adapter->name, SAA716x_I2C_ADAPTER(i));
+
+			adapter->owner		= THIS_MODULE;
+			adapter->algo		= &saa716x_algo;
+			adapter->algo_data 	= NULL;
+			adapter->timeout	= 500; /* FIXME ! */
+			adapter->retries	= 3; /* FIXME ! */
+			adapter->dev.parent	= &pdev->dev;
+
+			dprintk(SAA716x_DEBUG, 1, "Initializing adapter (%d) %s",
+				i,
+				adapter->name);
+
+			err = i2c_add_adapter(adapter);
+			if (err < 0) {
+				dprintk(SAA716x_ERROR, 1, "Adapter (%d) %s init failed", i, adapter->name);
+				goto exit;
+			}
+
+			i2c->saa716x = saa716x;
+			saa716x_i2c_hwinit(i2c, SAA716x_I2C_BUS(i));
+		}
+		i2c++;
+	}
+
+	if (saa716x->config->i2c_mode >= SAA716x_I2C_MODE_IRQ) {
+		SAA716x_EPWR(MSI, MSI_INT_ENA_SET_H, MSI_INT_I2CINT_0);
+		SAA716x_EPWR(MSI, MSI_INT_ENA_SET_H, MSI_INT_I2CINT_1);
+	}
+
+	dprintk(SAA716x_DEBUG, 1, "SAA%02x I2C Core succesfully initialized",
+		saa716x->pdev->device);
+
+	return 0;
+exit:
+	return err;
+}
+EXPORT_SYMBOL_GPL(saa716x_i2c_init);
+
+void saa716x_i2c_exit(struct saa716x_dev *saa716x)
+{
+	struct saa716x_i2c *i2c		= saa716x->i2c;
+	struct i2c_adapter *adapter	= NULL;
+	int i;
+
+	dprintk(SAA716x_DEBUG, 1, "Removing SAA%02x I2C Core", saa716x->pdev->device);
+
+	for (i = 0; i < SAA716x_I2C_ADAPTERS; i++) {
+
+		adapter = &i2c->i2c_adapter;
+#if 0
+		saa716x_remove_irqvector(saa716x, i2c_vec[i].vector);
+#endif
+		saa716x_i2c_hwdeinit(i2c, SAA716x_I2C_BUS(i));
+		dprintk(SAA716x_DEBUG, 1, "Removing adapter (%d) %s", i, adapter->name);
+
+		i2c_del_adapter(adapter);
+		i2c++;
+	}
+}
+EXPORT_SYMBOL_GPL(saa716x_i2c_exit);
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_i2c.h linux/drivers/media/pci/saa716x/saa716x_i2c.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_i2c.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_i2c.h	2017-08-11 23:31:25.849389358 +0200
@@ -0,0 +1,52 @@
+#ifndef __SAA716x_I2C_H
+#define __SAA716x_I2C_H
+
+#define SAA716x_I2C_ADAPTERS	2
+
+#define SAA716x_I2C_ADAPTER(__dev) ((	\
+	(__dev == 1) ?			\
+		"SAA716x I2C Core 1" :	\
+		"SAA716x I2C Core 0"))
+
+#define SAA716x_I2C_BUS(__x) ((__x == 1) ? 0x0000c000 : 0x0000b000)
+
+#define SAA716x_I2C_BUS_A		0x01
+#define SAA716x_I2C_BUS_B		0x00
+
+struct saa716x_dev;
+
+enum saa716x_i2c_rate {
+	SAA716x_I2C_RATE_400 = 1,
+	SAA716x_I2C_RATE_100,
+};
+
+enum saa716x_i2c_mode {
+	SAA716x_I2C_MODE_POLLING = 0,
+	SAA716x_I2C_MODE_IRQ,
+	SAA716x_I2C_MODE_IRQ_BUFFERED
+};
+
+struct saa716x_i2c {
+	struct i2c_adapter		i2c_adapter;
+	struct mutex			i2c_lock;
+	struct saa716x_dev		*saa716x;
+	u8				i2c_dev;
+
+	enum saa716x_i2c_rate		i2c_rate; /* run time */
+	enum saa716x_i2c_mode		i2c_mode;
+	u32				block_size; /* block size for buffered
+						       mode, 1 otherwise */
+	u32				i2c_stat;
+
+	u32				stat_tx_prior;
+	u32				stat_tx_done;
+	wait_queue_head_t		i2c_wq;
+	int				i2c_op;
+};
+
+extern int saa716x_i2c_init(struct saa716x_dev *saa716x);
+extern void saa716x_i2c_exit(struct saa716x_dev *saa716x);
+extern void saa716x_i2cint_disable(struct saa716x_dev *saa716x);
+extern int saa716x_i2c_irqevent(struct saa716x_dev *saa716x, u8 bus);
+
+#endif /* __SAA716x_I2C_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_i2c_reg.h linux/drivers/media/pci/saa716x/saa716x_i2c_reg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_i2c_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_i2c_reg.h	2017-08-11 23:31:25.849389358 +0200
@@ -0,0 +1,145 @@
+#ifndef __SAA716x_I2C_REG_H
+#define __SAA716x_I2C_REG_H
+
+/* -------------- I2C Registers -------------- */
+
+#define RX_FIFO				0x000
+#define I2C_RX_BYTE			(0x000000ff <<  0)
+
+#define TX_FIFO				0x000
+#define I2C_STOP_BIT			(0x00000001 <<  9)
+#define I2C_START_BIT			(0x00000001 <<  8)
+#define I2C_TX_BYTE			(0x000000ff <<  0)
+
+#define I2C_STATUS			0x008
+#define I2C_TRANSMIT			(0x00000001 << 11)
+#define I2C_RECEIVE			(0x00000001 << 10)
+#define I2C_TRANSMIT_S_PROG		(0x00000001 <<  9)
+#define I2C_TRANSMIT_S_CLEAR		(0x00000001 <<  8)
+#define I2C_TRANSMIT_PROG		(0x00000001 <<  7)
+#define I2C_TRANSMIT_CLEAR		(0x00000001 <<  6)
+#define I2C_RECEIVE_PROG		(0x00000001 <<  5)
+#define I2C_RECEIVE_CLEAR		(0x00000001 <<  4)
+#define I2C_SDA_LINE			(0x00000001 <<  3)
+#define I2C_SCL_LINE			(0x00000001 <<  2)
+#define I2C_START_STOP_FLAG		(0x00000001 <<  1)
+#define I2C_MODE_STATUS			(0x00000001 <<  0)
+
+#define I2C_CONTROL			0x00c
+#define I2C_SCL_CONTROL			(0x00000001 <<  7)
+#define I2C_SDA_CONTROL			(0x00000001 <<  6)
+#define I2C_RECEIVE_PROTECT		(0x00000001 <<  5)
+#define I2C_RECEIVE_PRO_READ		(0x00000001 <<  4)
+#define I2C_TRANS_SELF_CLEAR		(0x00000001 <<  3)
+#define I2C_TRANS_S_SELF_CLEAR		(0x00000001 <<  2)
+#define I2C_SLAVE_ADDR_10BIT		(0x00000001 <<  1)
+#define I2C_RESET			(0x00000001 <<  0)
+
+#define I2C_CLOCK_DIVISOR_HIGH		0x010
+#define I2C_CLOCK_HIGH			(0x0000ffff <<  0)
+
+#define I2C_CLOCK_DIVISOR_LOW		0x014
+#define I2C_CLOCK_LOW			(0x0000ffff <<  0)
+
+#define I2C_RX_LEVEL			0x01c
+#define I2C_RECEIVE_RANGE		(0x0000007f <<  0)
+
+#define I2C_TX_LEVEL			0x020
+#define I2C_TRANSMIT_RANGE		(0x0000007f <<  0)
+
+#define I2C_SDA_HOLD			0x028
+#define I2C_HOLD_TIME			(0x0000007f <<  0)
+
+#define MODULE_CONF			0xfd4
+#define INT_CLR_ENABLE			0xfd8
+#define I2C_CLR_ENABLE_STFNF		(0x00000001 << 12)
+#define I2C_CLR_ENABLE_MTFNF		(0x00000001 << 11)
+#define I2C_CLR_ENABLE_RFDA		(0x00000001 << 10)
+#define I2C_CLR_ENABLE_RFF		(0x00000001 <<  9)
+#define I2C_CLR_ENABLE_STDR		(0x00000001 <<  8)
+#define I2C_CLR_ENABLE_MTDR		(0x00000001 <<  7)
+#define I2C_CLR_ENABLE_IBE		(0x00000001 <<  6)
+#define I2C_CLR_ENABLE_MSMC		(0x00000001 <<  5)
+#define I2C_CLR_ENABLE_SRSD		(0x00000001 <<  4)
+#define I2C_CLR_ENABLE_STSD		(0x00000001 <<  3)
+#define I2C_CLR_ENABLE_MTNA		(0x00000001 <<  2)
+#define I2C_CLR_ENABLE_MAF		(0x00000001 <<  1)
+#define I2C_CLR_ENABLE_MTD		(0x00000001 <<  0)
+
+#define INT_SET_ENABLE			0xfdc
+#define I2C_SET_ENABLE_STFNF		(0x00000001 << 12)
+#define I2C_SET_ENABLE_MTFNF		(0x00000001 << 11)
+#define I2C_SET_ENABLE_RFDA		(0x00000001 << 10)
+#define I2C_SET_ENABLE_RFF		(0x00000001 <<  9)
+#define I2C_SET_ENABLE_STDR		(0x00000001 <<  8)
+#define I2C_SET_ENABLE_MTDR		(0x00000001 <<  7)
+#define I2C_SET_ENABLE_IBE		(0x00000001 <<  6)
+#define I2C_SET_ENABLE_MSMC		(0x00000001 <<  5)
+#define I2C_SET_ENABLE_SRSD		(0x00000001 <<  4)
+#define I2C_SET_ENABLE_STSD		(0x00000001 <<  3)
+#define I2C_SET_ENABLE_MTNA		(0x00000001 <<  2)
+#define I2C_SET_ENABLE_MAF		(0x00000001 <<  1)
+#define I2C_SET_ENABLE_MTD		(0x00000001 <<  0)
+
+#define INT_STATUS			0xfe0
+#define I2C_INTERRUPT_STFNF		(0x00000001 << 12)
+#define I2C_INTERRUPT_MTFNF		(0x00000001 << 11)
+#define I2C_INTERRUPT_RFDA		(0x00000001 << 10)
+#define I2C_INTERRUPTE_RFF		(0x00000001 <<  9)
+#define I2C_SLAVE_INTERRUPT_STDR	(0x00000001 <<  8)
+#define I2C_MASTER_INTERRUPT_MTDR	(0x00000001 <<  7)
+#define I2C_ERROR_IBE			(0x00000001 <<  6)
+#define I2C_MODE_CHANGE_INTER_MSMC	(0x00000001 <<  5)
+#define I2C_SLAVE_RECEIVE_INTER_SRSD	(0x00000001 <<  4)
+#define I2C_SLAVE_TRANSMIT_INTER_STSD	(0x00000001 <<  3)
+#define I2C_ACK_INTER_MTNA		(0x00000001 <<  2)
+#define I2C_FAILURE_INTER_MAF		(0x00000001 <<  1)
+#define I2C_INTERRUPT_MTD		(0x00000001 <<  0)
+
+#define INT_ENABLE			0xfe4
+#define I2C_ENABLE_STFNF		(0x00000001 << 12)
+#define I2C_ENABLE_MTFNF		(0x00000001 << 11)
+#define I2C_ENABLE_RFDA			(0x00000001 << 10)
+#define I2C_ENABLE_RFF			(0x00000001 <<  9)
+#define I2C_ENABLE_STDR			(0x00000001 <<  8)
+#define I2C_ENABLE_MTDR			(0x00000001 <<  7)
+#define I2C_ENABLE_IBE			(0x00000001 <<  6)
+#define I2C_ENABLE_MSMC			(0x00000001 <<  5)
+#define I2C_ENABLE_SRSD			(0x00000001 <<  4)
+#define I2C_ENABLE_STSD			(0x00000001 <<  3)
+#define I2C_ENABLE_MTNA			(0x00000001 <<  2)
+#define I2C_ENABLE_MAF			(0x00000001 <<  1)
+#define I2C_ENABLE_MTD			(0x00000001 <<  0)
+
+#define INT_CLR_STATUS			0xfe8
+#define I2C_CLR_STATUS_STFNF		(0x00000001 << 12)
+#define I2C_CLR_STATUS_MTFNF		(0x00000001 << 11)
+#define I2C_CLR_STATUS_RFDA		(0x00000001 << 10)
+#define I2C_CLR_STATUS_RFF		(0x00000001 <<  9)
+#define I2C_CLR_STATUS_STDR		(0x00000001 <<  8)
+#define I2C_CLR_STATUS_MTDR		(0x00000001 <<  7)
+#define I2C_CLR_STATUS_IBE		(0x00000001 <<  6)
+#define I2C_CLR_STATUS_MSMC		(0x00000001 <<  5)
+#define I2C_CLR_STATUS_SRSD		(0x00000001 <<  4)
+#define I2C_CLR_STATUS_STSD		(0x00000001 <<  3)
+#define I2C_CLR_STATUS_MTNA		(0x00000001 <<  2)
+#define I2C_CLR_STATUS_MAF		(0x00000001 <<  1)
+#define I2C_CLR_STATIS_MTD		(0x00000001 <<  0)
+
+#define INT_SET_STATUS			0xfec
+#define I2C_SET_STATUS_STFNF		(0x00000001 << 12)
+#define I2C_SET_STATUS_MTFNF		(0x00000001 << 11)
+#define I2C_SET_STATUS_RFDA		(0x00000001 << 10)
+#define I2C_SET_STATUS_RFF		(0x00000001 <<  9)
+#define I2C_SET_STATUS_STDR		(0x00000001 <<  8)
+#define I2C_SET_STATUS_MTDR		(0x00000001 <<  7)
+#define I2C_SET_STATUS_IBE		(0x00000001 <<  6)
+#define I2C_SET_STATUS_MSMC		(0x00000001 <<  5)
+#define I2C_SET_STATUS_SRSD		(0x00000001 <<  4)
+#define I2C_SET_STATUS_STSD		(0x00000001 <<  3)
+#define I2C_SET_STATUS_MTNA		(0x00000001 <<  2)
+#define I2C_SET_STATUS_MAF		(0x00000001 <<  1)
+#define I2C_SET_STATIS_MTD		(0x00000001 <<  0)
+
+
+#endif /* __SAA716x_I2C_REG_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_mod.h linux/drivers/media/pci/saa716x/saa716x_mod.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_mod.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_mod.h	2017-08-11 23:31:25.849389358 +0200
@@ -0,0 +1,50 @@
+#ifndef __SAA716x_MOD_H
+#define __SAA716x_MOD_H
+
+/* BAR = 17 bits */
+/*
+	VI0	0x00000000
+	VI1	0x00001000
+	FGPI0	0x00002000
+	FGPI1	0x00003000
+	FGPI2	0x00004000
+	FGPI3	0x00005000
+	AI0	0x00006000
+	AI1	0x00007000
+	BAM	0x00008000
+	MMU	0x00009000
+	MSI	0x0000a000
+	I2C_B	0x0000b000
+	I2C_A	0x0000c000
+	SPI	0x0000d000
+	GPIO	0x0000e000
+	PHI_0	0x0000f000
+	CGU	0x00013000
+	DCS	0x00014000
+	GREG	0x00012000
+
+	PHI_1	0x00020000
+*/
+
+#define VI0				0x00000000
+#define VI1				0x00001000
+#define FGPI0				0x00002000
+#define FGPI1				0x00003000
+#define FGPI2				0x00004000
+#define FGPI3				0x00005000
+#define AI0				0x00006000
+#define AI1				0x00007000
+#define BAM				0x00008000
+#define MMU				0x00009000
+#define MSI				0x0000a000
+#define I2C_B				0x0000b000
+#define I2C_A				0x0000c000
+#define SPI				0x0000d000
+#define GPIO				0x0000e000
+#define PHI_0				0x0000f000
+#define GREG				0x00012000
+#define CGU				0x00013000
+#define DCS				0x00014000
+#define PHI_1				0x00020000
+
+#endif /* __SAA716x_MOD_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_msi.c linux/drivers/media/pci/saa716x/saa716x_msi.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_msi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_msi.c	2017-08-11 23:31:25.861389291 +0200
@@ -0,0 +1,479 @@
+#include <linux/delay.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_msi_reg.h"
+#include "saa716x_msi.h"
+#include "saa716x_spi.h"
+
+#include "saa716x_priv.h"
+
+#define SAA716x_MSI_VECTORS		50
+
+static const char *vector_name[] = {
+	"TAGACK_VI0_0",
+	"TAGACK_VI0_1",
+	"TAGACK_VI0_2",
+	"TAGACK_VI1_0",
+	"TAGACK_VI1_1",
+	"TAGACK_VI1_2",
+	"TAGACK_FGPI_0",
+	"TAGACK_FGPI_1",
+	"TAGACK_FGPI_2",
+	"TAGACK_FGPI_3",
+	"TAGACK_AI_0",
+	"TAGACK_AI_1",
+	"OVRFLW_VI0_0",
+	"OVRFLW_VI0_1",
+	"OVRFLW_VI0_2",
+	"OVRFLW_VI1_0",
+	"OVRFLW_VI1_1",
+	"OVRFLW_VI1_2",
+	"OVRFLW_FGPI_O",
+	"OVRFLW_FGPI_1",
+	"OVRFLW_FGPI_2",
+	"OVRFLW_FGPI_3",
+	"OVRFLW_AI_0",
+	"OVRFLW_AI_1",
+	"AVINT_VI0",
+	"AVINT_VI1",
+	"AVINT_FGPI_0",
+	"AVINT_FGPI_1",
+	"AVINT_FGPI_2",
+	"AVINT_FGPI_3",
+	"AVINT_AI_0",
+	"AVINT_AI_1",
+	"UNMAPD_TC_INT",
+	"EXTINT_0",
+	"EXTINT_1",
+	"EXTINT_2",
+	"EXTINT_3",
+	"EXTINT_4",
+	"EXTINT_5",
+	"EXTINT_6",
+	"EXTINT_7",
+	"EXTINT_8",
+	"EXTINT_9",
+	"EXTINT_10",
+	"EXTINT_11",
+	"EXTINT_12",
+	"EXTINT_13",
+	"EXTINT_14",
+	"EXTINT_15",
+	"I2CINT_0",
+	"I2CINT_1"
+};
+
+static u32 MSI_CONFIG_REG[51] = {
+	MSI_CONFIG0,
+	MSI_CONFIG1,
+	MSI_CONFIG2,
+	MSI_CONFIG3,
+	MSI_CONFIG4,
+	MSI_CONFIG5,
+	MSI_CONFIG6,
+	MSI_CONFIG7,
+	MSI_CONFIG8,
+	MSI_CONFIG9,
+	MSI_CONFIG10,
+	MSI_CONFIG11,
+	MSI_CONFIG12,
+	MSI_CONFIG13,
+	MSI_CONFIG14,
+	MSI_CONFIG15,
+	MSI_CONFIG16,
+	MSI_CONFIG17,
+	MSI_CONFIG18,
+	MSI_CONFIG19,
+	MSI_CONFIG20,
+	MSI_CONFIG21,
+	MSI_CONFIG22,
+	MSI_CONFIG23,
+	MSI_CONFIG24,
+	MSI_CONFIG25,
+	MSI_CONFIG26,
+	MSI_CONFIG27,
+	MSI_CONFIG28,
+	MSI_CONFIG29,
+	MSI_CONFIG30,
+	MSI_CONFIG31,
+	MSI_CONFIG32,
+	MSI_CONFIG33,
+	MSI_CONFIG34,
+	MSI_CONFIG35,
+	MSI_CONFIG36,
+	MSI_CONFIG37,
+	MSI_CONFIG38,
+	MSI_CONFIG39,
+	MSI_CONFIG40,
+	MSI_CONFIG41,
+	MSI_CONFIG42,
+	MSI_CONFIG43,
+	MSI_CONFIG44,
+	MSI_CONFIG45,
+	MSI_CONFIG46,
+	MSI_CONFIG47,
+	MSI_CONFIG48,
+	MSI_CONFIG49,
+	MSI_CONFIG50
+};
+
+int saa716x_msi_event(struct saa716x_dev *saa716x, u32 stat_l, u32 stat_h)
+{
+	dprintk(SAA716x_DEBUG, 0, "%s: MSI event ", __func__);
+
+	if (stat_l & MSI_INT_TAGACK_VI0_0)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[0]);
+
+	if (stat_l & MSI_INT_TAGACK_VI0_1)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[1]);
+
+	if (stat_l & MSI_INT_TAGACK_VI0_2)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[2]);
+
+	if (stat_l & MSI_INT_TAGACK_VI1_0)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[3]);
+
+	if (stat_l & MSI_INT_TAGACK_VI1_1)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[4]);
+
+	if (stat_l & MSI_INT_TAGACK_VI1_2)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[5]);
+
+	if (stat_l & MSI_INT_TAGACK_FGPI_0)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[6]);
+
+	if (stat_l & MSI_INT_TAGACK_FGPI_1)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[7]);
+
+	if (stat_l & MSI_INT_TAGACK_FGPI_2)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[8]);
+
+	if (stat_l & MSI_INT_TAGACK_FGPI_3)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[9]);
+
+	if (stat_l & MSI_INT_TAGACK_AI_0)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[10]);
+
+	if (stat_l & MSI_INT_TAGACK_AI_1)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[11]);
+
+	if (stat_l & MSI_INT_OVRFLW_VI0_0)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[12]);
+
+	if (stat_l & MSI_INT_OVRFLW_VI0_1)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[13]);
+
+	if (stat_l & MSI_INT_OVRFLW_VI0_2)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[14]);
+
+	if (stat_l & MSI_INT_OVRFLW_VI1_0)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[15]);
+
+	if (stat_l & MSI_INT_OVRFLW_VI1_1)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[16]);
+
+	if (stat_l & MSI_INT_OVRFLW_VI1_2)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[17]);
+
+	if (stat_l & MSI_INT_OVRFLW_FGPI_0)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[18]);
+
+	if (stat_l & MSI_INT_OVRFLW_FGPI_1)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[19]);
+
+	if (stat_l & MSI_INT_OVRFLW_FGPI_2)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[20]);
+
+	if (stat_l & MSI_INT_OVRFLW_FGPI_3)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[21]);
+
+	if (stat_l & MSI_INT_OVRFLW_AI_0)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[22]);
+
+	if (stat_l & MSI_INT_OVRFLW_AI_1)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[23]);
+
+	if (stat_l & MSI_INT_AVINT_VI0)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[24]);
+
+	if (stat_l & MSI_INT_AVINT_VI1)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[25]);
+
+	if (stat_l & MSI_INT_AVINT_FGPI_0)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[26]);
+
+	if (stat_l & MSI_INT_AVINT_FGPI_1)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[27]);
+
+	if (stat_l & MSI_INT_AVINT_FGPI_2)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[28]);
+
+	if (stat_l & MSI_INT_AVINT_FGPI_3)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[29]);
+
+	if (stat_l & MSI_INT_AVINT_AI_0)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[30]);
+
+	if (stat_l & MSI_INT_AVINT_AI_1)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[31]);
+
+	if (stat_h & MSI_INT_UNMAPD_TC_INT)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[32]);
+
+	if (stat_h & MSI_INT_EXTINT_0)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[33]);
+
+	if (stat_h & MSI_INT_EXTINT_1)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[34]);
+
+	if (stat_h & MSI_INT_EXTINT_2)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[35]);
+
+	if (stat_h & MSI_INT_EXTINT_3)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[36]);
+
+	if (stat_h & MSI_INT_EXTINT_4)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[37]);
+
+	if (stat_h & MSI_INT_EXTINT_5)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[38]);
+
+	if (stat_h & MSI_INT_EXTINT_6)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[39]);
+
+	if (stat_h & MSI_INT_EXTINT_7)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[40]);
+
+	if (stat_h & MSI_INT_EXTINT_8)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[41]);
+
+	if (stat_h & MSI_INT_EXTINT_9)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[42]);
+
+	if (stat_h & MSI_INT_EXTINT_10)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[43]);
+
+	if (stat_h & MSI_INT_EXTINT_11)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[44]);
+
+	if (stat_h & MSI_INT_EXTINT_12)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[45]);
+
+	if (stat_h & MSI_INT_EXTINT_13)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[46]);
+
+	if (stat_h & MSI_INT_EXTINT_14)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[47]);
+
+	if (stat_h & MSI_INT_EXTINT_15)
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[48]);
+
+	if (stat_h & MSI_INT_I2CINT_0) {
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[49]);
+		saa716x_i2c_irqevent(saa716x, 0);
+	}
+
+	if (stat_h & MSI_INT_I2CINT_1) {
+		dprintk(SAA716x_DEBUG, 0, "<%s> ", vector_name[50]);
+		saa716x_i2c_irqevent(saa716x, 1);
+	}
+
+	dprintk(SAA716x_DEBUG, 0, "\n");
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_msi_event);
+
+int saa716x_msi_init(struct saa716x_dev *saa716x)
+{
+	u32 ena_l, ena_h, sta_l, sta_h, mid;
+	int i;
+
+	dprintk(SAA716x_DEBUG, 1, "Initializing MSI ..");
+	saa716x->handlers = 0;
+
+	/* get module id & version */
+	mid = SAA716x_EPRD(MSI, MSI_MODULE_ID);
+	if (mid != 0x30100)
+		dprintk(SAA716x_ERROR, 1, "MSI Id<%04x> is not supported", mid);
+
+	/* let HW take care of MSI race */
+	SAA716x_EPWR(MSI, MSI_DELAY_TIMER, 0x0);
+
+	/* INTA Polarity: Active High */
+	SAA716x_EPWR(MSI, MSI_INTA_POLARITY, MSI_INTA_POLARITY_HIGH);
+
+	/*
+	 * IRQ Edge Rising: 25:24 = 0x01
+	 * Traffic Class: 18:16 = 0x00
+	 * MSI ID: 4:0 = 0x00
+	 */
+	for (i = 0; i < SAA716x_MSI_VECTORS; i++)
+		SAA716x_EPWR(MSI, MSI_CONFIG_REG[i], MSI_INT_POL_EDGE_RISE);
+
+	/* get Status */
+	ena_l = SAA716x_EPRD(MSI, MSI_INT_ENA_L);
+	ena_h = SAA716x_EPRD(MSI, MSI_INT_ENA_H);
+	sta_l = SAA716x_EPRD(MSI, MSI_INT_STATUS_L);
+	sta_h = SAA716x_EPRD(MSI, MSI_INT_STATUS_H);
+
+	/* disable and clear enabled and asserted IRQ's */
+	if (sta_l)
+		SAA716x_EPWR(MSI, MSI_INT_STATUS_CLR_L, sta_l);
+
+	if (sta_h)
+		SAA716x_EPWR(MSI, MSI_INT_STATUS_CLR_H, sta_h);
+
+	if (ena_l)
+		SAA716x_EPWR(MSI, MSI_INT_ENA_CLR_L, ena_l);
+
+	if (ena_h)
+		SAA716x_EPWR(MSI, MSI_INT_ENA_CLR_H, ena_h);
+
+	msleep(5);
+
+	/* Check IRQ's really disabled */
+	ena_l = SAA716x_EPRD(MSI, MSI_INT_ENA_L);
+	ena_h = SAA716x_EPRD(MSI, MSI_INT_ENA_H);
+	sta_l = SAA716x_EPRD(MSI, MSI_INT_STATUS_L);
+	sta_h = SAA716x_EPRD(MSI, MSI_INT_STATUS_H);
+
+	if ((ena_l == 0) && (ena_h == 0) && (sta_l == 0) && (sta_h == 0)) {
+		dprintk(SAA716x_DEBUG, 1, "Interrupts ena_l <%02x> ena_h <%02x> sta_l <%02x> sta_h <%02x>",
+			ena_l, ena_h, sta_l, sta_h);
+
+		return 0;
+	} else {
+		dprintk(SAA716x_DEBUG, 1, "I/O error");
+		return -EIO;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_msi_init);
+
+void saa716x_msiint_disable(struct saa716x_dev *saa716x)
+{
+	dprintk(SAA716x_DEBUG, 1, "Disabling Interrupts ...");
+
+	SAA716x_EPWR(MSI, MSI_INT_ENA_L, 0x0);
+	SAA716x_EPWR(MSI, MSI_INT_ENA_H, 0x0);
+	SAA716x_EPWR(MSI, MSI_INT_STATUS_CLR_L, 0xffffffff);
+	SAA716x_EPWR(MSI, MSI_INT_STATUS_CLR_L, 0x0000ffff);
+}
+EXPORT_SYMBOL_GPL(saa716x_msiint_disable);
+
+
+/* Map the given vector Id to the hardware bitmask. */
+static void saa716x_map_vector(struct saa716x_dev *saa716x, int vector, u32 *mask_l, u32 *mask_h)
+{
+	u32 tmp = 1;
+
+	if (vector < 32) {
+		/* Bits 0 - 31 */
+		tmp   <<= vector;
+		*mask_l = tmp;
+		*mask_h	= 0;
+	} else {
+		/* Bits 32 - 48 */
+		tmp <<= vector - 32;
+		*mask_l = 0;
+		*mask_h = tmp;
+	}
+}
+
+int saa716x_add_irqvector(struct saa716x_dev *saa716x,
+			  int vector,
+			  enum saa716x_edge edge,
+			  irqreturn_t (*handler)(int irq, void *dev_id),
+			  char *desc)
+{
+	struct saa716x_msix_entry *msix_handler = NULL;
+
+	u32 config, mask_l, mask_h, ena_l, ena_h;
+
+	BUG_ON(saa716x == NULL);
+	BUG_ON(vector > SAA716x_MSI_VECTORS);
+	dprintk(SAA716x_DEBUG, 1, "Adding Vector %d <%s>", vector, vector_name[vector]);
+
+	if ((vector > 32) && (vector < 49)) {
+		config = SAA716x_EPRD(MSI, MSI_CONFIG_REG[vector]);
+		config &= 0xfcffffff; /* clear polarity */
+
+		switch (edge) {
+		default:
+		case SAA716x_EDGE_RISING:
+			SAA716x_EPWR(MSI, MSI_CONFIG_REG[vector], config | 0x01000000);
+			break;
+
+		case SAA716x_EDGE_FALLING:
+			SAA716x_EPWR(MSI, MSI_CONFIG_REG[vector], config | 0x02000000);
+			break;
+
+		case SAA716x_EDGE_ANY:
+			SAA716x_EPWR(MSI, MSI_CONFIG_REG[vector], config | 0x03000000);
+			break;
+		}
+	}
+
+	saa716x_map_vector(saa716x, vector, &mask_l, &mask_h);
+
+	/* add callback */
+	msix_handler = &saa716x->saa716x_msix_handler[saa716x->handlers];
+	strcpy(msix_handler->desc, desc);
+	msix_handler->vector = vector;
+	msix_handler->handler = handler;
+	saa716x->handlers++;
+
+	SAA716x_EPWR(MSI, MSI_INT_ENA_SET_L, mask_l);
+	SAA716x_EPWR(MSI, MSI_INT_ENA_SET_H, mask_h);
+
+	ena_l = SAA716x_EPRD(MSI, MSI_INT_ENA_L);
+	ena_h = SAA716x_EPRD(MSI, MSI_INT_ENA_H);
+	dprintk(SAA716x_DEBUG, 1, "Interrupts ena_l <%02x> ena_h <%02x>", ena_l, ena_h);
+
+	return 0;
+}
+
+int saa716x_remove_irqvector(struct saa716x_dev *saa716x, int vector)
+{
+	struct saa716x_msix_entry *msix_handler;
+	int i;
+	u32 mask_l, mask_h;
+
+	msix_handler = &saa716x->saa716x_msix_handler[saa716x->handlers];
+	BUG_ON(msix_handler == NULL);
+	dprintk(SAA716x_DEBUG, 1, "Removing Vector %d <%s>", vector, vector_name[vector]);
+
+	/* loop through the registered handlers */
+	for (i = 0; i < saa716x->handlers; i++) {
+
+		/* we found our vector */
+		if (msix_handler->vector == vector) {
+			BUG_ON(msix_handler->handler == NULL); /* no handler yet */
+			dprintk(SAA716x_DEBUG, 1, "Vector %d <%s> removed",
+				msix_handler->vector,
+				msix_handler->desc);
+
+			/* check whether it is already released */
+			if (msix_handler->handler) {
+				msix_handler->vector = 0;
+				msix_handler->handler = NULL;
+				saa716x->handlers--;
+			}
+		}
+	}
+
+	saa716x_map_vector(saa716x, vector, &mask_l, &mask_h);
+
+	/* disable vector */
+	SAA716x_EPWR(MSI, MSI_INT_ENA_CLR_L, mask_l);
+	SAA716x_EPWR(MSI, MSI_INT_ENA_CLR_H, mask_h);
+
+	return 0;
+}
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_msi.h linux/drivers/media/pci/saa716x/saa716x_msi.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_msi.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_msi.h	2017-08-11 23:31:25.861389291 +0200
@@ -0,0 +1,87 @@
+#ifndef __SAA716x_MSI_H
+#define __SAA716x_MSI_H
+
+#define	TAGACK_VI0_0		0x000
+#define	TAGACK_VI0_1		0x001
+#define	TAGACK_VI0_2		0x002
+#define	TAGACK_VI1_0		0x003
+#define	TAGACK_VI1_1		0x004
+#define	TAGACK_VI1_2		0x005
+#define	TAGACK_FGPI_0		0x006
+#define	TAGACK_FGPI_1		0x007
+#define	TAGACK_FGPI_2		0x008
+#define	TAGACK_FGPI_3		0x009
+#define	TAGACK_AI_0		0x00a
+#define	TAGACK_AI_1		0x00b
+#define	OVRFLW_VI0_0		0x00c
+#define	OVRFLW_VI0_1		0x00d
+#define	OVRFLW_VI0_2		0x00e
+#define	OVRFLW_VI1_0		0x00f
+#define	OVRFLW_VI1_1		0x010
+#define	OVRFLW_VI1_2		0x011
+#define	OVRFLW_FGPI_O		0x012
+#define	OVRFLW_FGPI_1		0x013
+#define	OVRFLW_FGPI_2		0x014
+#define	OVRFLW_FGPI_3		0x015
+#define	OVRFLW_AI_0		0x016
+#define	OVRFLW_AI_1		0x017
+#define	AVINT_VI0		0x018
+#define	AVINT_VI1		0x019
+#define	AVINT_FGPI_0		0x01a
+#define	AVINT_FGPI_1		0x01b
+#define	AVINT_FGPI_2		0x01c
+#define	AVINT_FGPI_3		0x01d
+#define	AVINT_AI_0		0x01e
+#define	AVINT_AI_1		0x01f
+#define	UNMAPD_TC_INT		0x020
+#define	EXTINT_0		0x021
+#define	EXTINT_1		0x022
+#define	EXTINT_2		0x023
+#define	EXTINT_3		0x024
+#define	EXTINT_4		0x025
+#define	EXTINT_5		0x026
+#define EXTINT_6		0x027
+#define	EXTINT_7		0x028
+#define	EXTINT_8		0x029
+#define	EXTINT_9		0x02a
+#define	EXTINT_10		0x02b
+#define	EXTINT_11		0x02c
+#define	EXTINT_12		0x02d
+#define	EXTINT_13		0x02e
+#define	EXTINT_14		0x02f
+#define	EXTINT_15		0x030
+#define	I2CINT_0		0x031
+#define	I2CINT_1		0x032
+
+#define SAA716x_TC0		0x000
+#define SAA716x_TC1		0x001
+#define SAA716x_TC2		0x002
+#define SAA716x_TC3		0x003
+#define SAA716x_TC4		0x004
+#define SAA716x_TC5		0x005
+#define SAA716x_TC6		0x006
+#define SAA716x_TC7		0x007
+
+
+enum saa716x_edge {
+	SAA716x_EDGE_RISING	= 1,
+	SAA716x_EDGE_FALLING	= 2,
+	SAA716x_EDGE_ANY	= 3
+};
+
+struct saa716x_dev;
+
+extern int saa716x_msi_event(struct saa716x_dev *saa716x, u32 stat_l, u32 stat_h);
+
+extern int saa716x_msi_init(struct saa716x_dev *saa716x);
+extern void saa716x_msiint_disable(struct saa716x_dev *saa716x);
+
+extern int saa716x_add_irqvector(struct saa716x_dev *saa716x,
+				 int vector,
+				 enum saa716x_edge edge,
+				 irqreturn_t (*handler)(int irq, void *dev_id),
+				 char *desc);
+
+extern int saa716x_remove_irqvector(struct saa716x_dev *saa716x, int vector);
+
+#endif /* __SAA716x_MSI_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_msi_reg.h linux/drivers/media/pci/saa716x/saa716x_msi_reg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_msi_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_msi_reg.h	2017-08-11 23:31:25.861389291 +0200
@@ -0,0 +1,143 @@
+#ifndef __SAA716x_MSI_REG_H
+#define __SAA716x_MSI_REG_H
+
+/* -------------- MSI Registers -------------- */
+
+#define MSI_DELAY_TIMER			0x000
+#define MSI_DELAY_1CLK			(0x00000001 <<  0)
+#define MSI_DELAY_2CLK			(0x00000002 <<  0)
+
+#define MSI_INTA_POLARITY		0x004
+#define MSI_INTA_POLARITY_HIGH		(0x00000001 <<  0)
+
+#define MSI_CONFIG0			0x008
+#define MSI_CONFIG1			0x00c
+#define MSI_CONFIG2			0x010
+#define MSI_CONFIG3			0x014
+#define MSI_CONFIG4			0x018
+#define MSI_CONFIG5			0x01c
+#define MSI_CONFIG6			0x020
+#define MSI_CONFIG7			0x024
+#define MSI_CONFIG8			0x028
+#define MSI_CONFIG9			0x02c
+#define MSI_CONFIG10			0x030
+#define MSI_CONFIG11			0x034
+#define MSI_CONFIG12			0x038
+#define MSI_CONFIG13			0x03c
+#define MSI_CONFIG14			0x040
+#define MSI_CONFIG15			0x044
+#define MSI_CONFIG16			0x048
+#define MSI_CONFIG17			0x04c
+#define MSI_CONFIG18			0x050
+#define MSI_CONFIG19			0x054
+#define MSI_CONFIG20			0x058
+#define MSI_CONFIG21			0x05c
+#define MSI_CONFIG22			0x060
+#define MSI_CONFIG23			0x064
+#define MSI_CONFIG24			0x068
+#define MSI_CONFIG25			0x06c
+#define MSI_CONFIG26			0x070
+#define MSI_CONFIG27			0x074
+#define MSI_CONFIG28			0x078
+#define MSI_CONFIG29			0x07c
+#define MSI_CONFIG30			0x080
+#define MSI_CONFIG31			0x084
+#define MSI_CONFIG32			0x088
+#define MSI_CONFIG33			0x08c
+#define MSI_CONFIG34			0x090
+#define MSI_CONFIG35			0x094
+#define MSI_CONFIG36			0x098
+#define MSI_CONFIG37			0x09c
+#define MSI_CONFIG38			0x0a0
+#define MSI_CONFIG39			0x0a4
+#define MSI_CONFIG40			0x0a8
+#define MSI_CONFIG41			0x0ac
+#define MSI_CONFIG42			0x0b0
+#define MSI_CONFIG43			0x0b4
+#define MSI_CONFIG44			0x0b8
+#define MSI_CONFIG45			0x0bc
+#define MSI_CONFIG46			0x0c0
+#define MSI_CONFIG47			0x0c4
+#define MSI_CONFIG48			0x0c8
+#define MSI_CONFIG49			0x0cc
+#define MSI_CONFIG50			0x0d0
+
+#define MSI_INT_POL_EDGE_RISE		(0x00000001 << 24)
+#define MSI_INT_POL_EDGE_FALL		(0x00000002 << 24)
+#define MSI_INT_POL_EDGE_ANY		(0x00000003 << 24)
+#define MSI_TC				(0x00000007 << 16)
+#define MSI_ID				(0x0000000f <<  0)
+
+#define MSI_INT_STATUS_L		0xfc0
+#define MSI_INT_TAGACK_VI0_0		(0x00000001 <<  0)
+#define MSI_INT_TAGACK_VI0_1		(0x00000001 <<  1)
+#define MSI_INT_TAGACK_VI0_2		(0x00000001 <<  2)
+#define MSI_INT_TAGACK_VI1_0		(0x00000001 <<  3)
+#define MSI_INT_TAGACK_VI1_1		(0x00000001 <<  4)
+#define MSI_INT_TAGACK_VI1_2		(0x00000001 <<  5)
+#define MSI_INT_TAGACK_FGPI_0		(0x00000001 <<  6)
+#define MSI_INT_TAGACK_FGPI_1		(0x00000001 <<  7)
+#define MSI_INT_TAGACK_FGPI_2		(0x00000001 <<  8)
+#define MSI_INT_TAGACK_FGPI_3		(0x00000001 <<  9)
+#define MSI_INT_TAGACK_AI_0		(0x00000001 << 10)
+#define MSI_INT_TAGACK_AI_1		(0x00000001 << 11)
+#define MSI_INT_OVRFLW_VI0_0		(0x00000001 << 12)
+#define MSI_INT_OVRFLW_VI0_1		(0x00000001 << 13)
+#define MSI_INT_OVRFLW_VI0_2		(0x00000001 << 14)
+#define MSI_INT_OVRFLW_VI1_0		(0x00000001 << 15)
+#define MSI_INT_OVRFLW_VI1_1		(0x00000001 << 16)
+#define MSI_INT_OVRFLW_VI1_2		(0x00000001 << 17)
+#define MSI_INT_OVRFLW_FGPI_0		(0x00000001 << 18)
+#define MSI_INT_OVRFLW_FGPI_1		(0x00000001 << 19)
+#define MSI_INT_OVRFLW_FGPI_2		(0x00000001 << 20)
+#define MSI_INT_OVRFLW_FGPI_3		(0x00000001 << 21)
+#define MSI_INT_OVRFLW_AI_0		(0x00000001 << 22)
+#define MSI_INT_OVRFLW_AI_1		(0x00000001 << 23)
+#define MSI_INT_AVINT_VI0		(0x00000001 << 24)
+#define MSI_INT_AVINT_VI1		(0x00000001 << 25)
+#define MSI_INT_AVINT_FGPI_0		(0x00000001 << 26)
+#define MSI_INT_AVINT_FGPI_1		(0x00000001 << 27)
+#define MSI_INT_AVINT_FGPI_2		(0x00000001 << 28)
+#define MSI_INT_AVINT_FGPI_3		(0x00000001 << 29)
+#define MSI_INT_AVINT_AI_0		(0x00000001 << 30)
+#define MSI_INT_AVINT_AI_1		(0x00000001 << 31)
+
+#define MSI_INT_STATUS_H		0xfc4
+#define MSI_INT_UNMAPD_TC_INT		(0x00000001 <<  0)
+#define MSI_INT_EXTINT_0		(0x00000001 <<  1)
+#define MSI_INT_EXTINT_1		(0x00000001 <<  2)
+#define MSI_INT_EXTINT_2		(0x00000001 <<  3)
+#define MSI_INT_EXTINT_3		(0x00000001 <<  4)
+#define MSI_INT_EXTINT_4		(0x00000001 <<  5)
+#define MSI_INT_EXTINT_5		(0x00000001 <<  6)
+#define MSI_INT_EXTINT_6		(0x00000001 <<  7)
+#define MSI_INT_EXTINT_7		(0x00000001 <<  8)
+#define MSI_INT_EXTINT_8		(0x00000001 <<  9)
+#define MSI_INT_EXTINT_9		(0x00000001 << 10)
+#define MSI_INT_EXTINT_10		(0x00000001 << 11)
+#define MSI_INT_EXTINT_11		(0x00000001 << 12)
+#define MSI_INT_EXTINT_12		(0x00000001 << 13)
+#define MSI_INT_EXTINT_13		(0x00000001 << 14)
+#define MSI_INT_EXTINT_14		(0x00000001 << 15)
+#define MSI_INT_EXTINT_15		(0x00000001 << 16)
+#define MSI_INT_I2CINT_0		(0x00000001 << 17)
+#define MSI_INT_I2CINT_1		(0x00000001 << 18)
+
+#define MSI_INT_STATUS_CLR_L		0xfc8
+#define MSI_INT_STATUS_CLR_H		0xfcc
+#define MSI_INT_STATUS_SET_L		0xfd0
+#define MSI_INT_STATUS_SET_H		0xfd4
+#define MSI_INT_ENA_L			0xfd8
+#define MSI_INT_ENA_H			0xfdc
+#define MSI_INT_ENA_CLR_L		0xfe0
+#define MSI_INT_ENA_CLR_H		0xfe4
+#define MSI_INT_ENA_SET_L		0xfe8
+#define MSI_INT_ENA_SET_H		0xfec
+
+#define MSI_SW_RST			0xff0
+#define MSI_SW_RESET			(0x0001 <<  0)
+
+#define MSI_MODULE_ID			0xffc
+
+
+#endif /* __SAA716x_MSI_REG_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_pci.c linux/drivers/media/pci/saa716x/saa716x_pci.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_pci.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_pci.c	2017-08-11 23:31:25.861389291 +0200
@@ -0,0 +1,280 @@
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include "saa716x_spi.h"
+#include "saa716x_msi.h"
+#include "saa716x_priv.h"
+
+#define DRIVER_NAME				"SAA716x Core"
+
+static irqreturn_t saa716x_msi_handler(int irq, void *dev_id)
+{
+	return IRQ_HANDLED;
+}
+
+static int saa716x_enable_msi(struct saa716x_dev *saa716x)
+{
+	struct pci_dev *pdev = saa716x->pdev;
+	int err;
+
+	err = pci_enable_msi(pdev);
+	if (err) {
+		dprintk(SAA716x_ERROR, 1, "MSI enable failed <%d>", err);
+		return err;
+	}
+
+	return err;
+}
+
+static int saa716x_enable_msix(struct saa716x_dev *saa716x)
+{
+	struct pci_dev *pdev = saa716x->pdev;
+	int i, ret = 0;
+
+	for (i = 0; i < SAA716x_MSI_MAX_VECTORS; i++)
+		saa716x->msix_entries[i].entry = i;
+
+	ret = pci_enable_msix(pdev, saa716x->msix_entries, SAA716x_MSI_MAX_VECTORS);
+	if (ret < 0)
+		dprintk(SAA716x_ERROR, 1, "MSI-X request failed <%d>", ret);
+	if (ret > 0)
+		dprintk(SAA716x_ERROR, 1, "Request exceeds available IRQ's <%d>", ret);
+
+	return ret;
+}
+
+static int saa716x_request_irq(struct saa716x_dev *saa716x)
+{
+	struct pci_dev *pdev = saa716x->pdev;
+	struct saa716x_config *config = saa716x->config;
+	int i, ret = 0;
+
+	if (saa716x->int_type == MODE_MSI) {
+		dprintk(SAA716x_DEBUG, 1, "Using MSI mode");
+		ret = saa716x_enable_msi(saa716x);
+	} else if (saa716x->int_type == MODE_MSI_X) {
+		dprintk(SAA716x_DEBUG, 1, "Using MSI-X mode");
+		ret = saa716x_enable_msix(saa716x);
+	}
+
+	if (ret) {
+		dprintk(SAA716x_ERROR, 1, "INT-A Mode");
+		saa716x->int_type = MODE_INTA;
+	}
+
+	if (saa716x->int_type == MODE_MSI) {
+		ret = request_irq(pdev->irq,
+				  config->irq_handler,
+				  0,
+				  DRIVER_NAME,
+				  saa716x);
+
+		if (ret) {
+			pci_disable_msi(pdev);
+			dprintk(SAA716x_ERROR, 1, "MSI registration failed");
+			ret = -EIO;
+		}
+	}
+
+	if (saa716x->int_type == MODE_MSI_X) {
+		for (i = 0; SAA716x_MSI_MAX_VECTORS; i++) {
+			ret = request_irq(saa716x->msix_entries[i].vector,
+					  saa716x->saa716x_msix_handler[i].handler,
+					  IRQF_SHARED,
+					  saa716x->saa716x_msix_handler[i].desc,
+					  saa716x);
+
+			dprintk(SAA716x_ERROR, 1, "%s @ 0x%p", saa716x->saa716x_msix_handler[i].desc, saa716x->saa716x_msix_handler[i].handler);
+			if (ret) {
+				dprintk(SAA716x_ERROR, 1, "%s MSI-X-%d registration failed <%d>", saa716x->saa716x_msix_handler[i].desc, i, ret);
+				return -1;
+			}
+		}
+	}
+
+	if (saa716x->int_type == MODE_INTA) {
+		ret = request_irq(pdev->irq,
+				  config->irq_handler,
+				  IRQF_SHARED,
+				  DRIVER_NAME,
+				  saa716x);
+		if (ret < 0) {
+			dprintk(SAA716x_ERROR, 1, "SAA716x IRQ registration failed <%d>", ret);
+			ret = -ENODEV;
+		}
+	}
+
+	return ret;
+}
+
+static void saa716x_free_irq(struct saa716x_dev *saa716x)
+{
+	struct pci_dev *pdev = saa716x->pdev;
+	int i, vector;
+
+	if (saa716x->int_type == MODE_MSI_X) {
+
+		for (i = 0; i < SAA716x_MSI_MAX_VECTORS; i++) {
+			vector = saa716x->msix_entries[i].vector;
+			free_irq(vector, saa716x);
+		}
+
+		pci_disable_msix(pdev);
+
+	} else {
+		free_irq(pdev->irq, saa716x);
+		if (saa716x->int_type == MODE_MSI)
+			pci_disable_msi(pdev);
+	}
+}
+
+int saa716x_pci_init(struct saa716x_dev *saa716x)
+{
+	struct pci_dev *pdev = saa716x->pdev;
+	int err = 0, ret = -ENODEV, i, use_dac, pm_cap;
+	u32 msi_cap;
+	u8 revision;
+
+	dprintk(SAA716x_ERROR, 1, "found a %s PCIe card", saa716x->config->model_name);
+
+	err = pci_enable_device(pdev);
+	if (err != 0) {
+		ret = -ENODEV;
+		dprintk(SAA716x_ERROR, 1, "ERROR: PCI enable failed (%i)", err);
+		goto fail0;
+	}
+
+	if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+		use_dac = 1;
+		err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+		if (err) {
+			dprintk(SAA716x_ERROR, 1, "Unable to obtain 64bit DMA");
+			goto fail1;
+		}
+	} else if ((err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) != 0) {
+		dprintk(SAA716x_ERROR, 1, "Unable to obtain 32bit DMA");
+		goto fail1;
+	}
+
+	pci_set_master(pdev);
+
+	pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+	if (pm_cap == 0) {
+		dprintk(SAA716x_ERROR, 1, "Cannot find Power Management Capability");
+		err = -EIO;
+		goto fail1;
+	}
+
+	if (!request_mem_region(pci_resource_start(pdev, 0),
+				pci_resource_len(pdev, 0),
+				DRIVER_NAME)) {
+
+		dprintk(SAA716x_ERROR, 1, "BAR0 Request failed");
+		ret = -ENODEV;
+		goto fail1;
+	}
+
+	if (pci_resource_len(pdev, 0) < 0x30000) {
+		dprintk(SAA716x_ERROR, 1, "wrong BAR0 length");
+		ret = -ENODEV;
+		goto fail2;
+	}
+
+	saa716x->mmio = ioremap_nocache(pci_resource_start(pdev, 0), 0x30000);
+	if (!saa716x->mmio) {
+		dprintk(SAA716x_ERROR, 1, "Mem 0 remap failed");
+		ret = -ENODEV;
+		goto fail2;
+	}
+
+	for (i = 0; i < SAA716x_MSI_MAX_VECTORS; i++)
+		saa716x->msix_entries[i].entry = i;
+
+	err = saa716x_request_irq(saa716x);
+	if (err < 0) {
+		dprintk(SAA716x_ERROR, 1, "SAA716x IRQ registration failed, err=%d", err);
+		ret = -ENODEV;
+		goto fail3;
+	}
+
+	pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
+	pci_read_config_dword(pdev, 0x40, &msi_cap);
+
+	saa716x->revision	= revision;
+
+	dprintk(SAA716x_ERROR, 0, "    SAA%02x Rev %d [%04x:%04x], ",
+		saa716x->pdev->device,
+		revision,
+		saa716x->pdev->subsystem_vendor,
+		saa716x->pdev->subsystem_device);
+
+	dprintk(SAA716x_ERROR, 0,
+		"irq: %d,\n    mmio: 0x%p\n",
+		saa716x->pdev->irq,
+		saa716x->mmio);
+
+	dprintk(SAA716x_ERROR, 0, "    SAA%02x %sBit, MSI %s, MSI-X=%d msgs",
+		saa716x->pdev->device,
+		(((msi_cap >> 23) & 0x01) == 1 ? "64":"32"),
+		(((msi_cap >> 16) & 0x01) == 1 ? "Enabled" : "Disabled"),
+		(1 << ((msi_cap >> 17) & 0x07)));
+
+	dprintk(SAA716x_ERROR, 0, "\n");
+
+	pci_set_drvdata(pdev, saa716x);
+
+	return 0;
+
+fail3:
+	dprintk(SAA716x_ERROR, 1, "Err: IO Unmap");
+	if (saa716x->mmio)
+		iounmap(saa716x->mmio);
+fail2:
+	dprintk(SAA716x_ERROR, 1, "Err: Release regions");
+	release_mem_region(pci_resource_start(pdev, 0),
+			   pci_resource_len(pdev, 0));
+
+fail1:
+	dprintk(SAA716x_ERROR, 1, "Err: Disabling device");
+	pci_disable_device(pdev);
+
+fail0:
+	pci_set_drvdata(pdev, NULL);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(saa716x_pci_init);
+
+void saa716x_pci_exit(struct saa716x_dev *saa716x)
+{
+	struct pci_dev *pdev = saa716x->pdev;
+
+	saa716x_free_irq(saa716x);
+
+	dprintk(SAA716x_NOTICE, 1, "SAA%02x mem0: 0x%p",
+		saa716x->pdev->device,
+		saa716x->mmio);
+
+	if (saa716x->mmio) {
+		iounmap(saa716x->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(saa716x_pci_exit);
+
+MODULE_DESCRIPTION("SAA716x bridge driver");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_LICENSE("GPL");
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_phi.c linux/drivers/media/pci/saa716x/saa716x_phi.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_phi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_phi.c	2017-08-11 23:31:25.861389291 +0200
@@ -0,0 +1,23 @@
+#include <linux/kernel.h>
+
+#include "saa716x_mod.h"
+#include "saa716x_phi_reg.h"
+#include "saa716x_phi.h"
+#include "saa716x_priv.h"
+
+
+int saa716x_phi_init(struct saa716x_dev *saa716x)
+{
+	int i;
+
+	/* Reset PHI */
+	SAA716x_EPWR(PHI_0, PHI_SW_RST, 0x1);
+	for (i = 0; i < 20; i++) {
+		msleep(1);
+		if (!(SAA716x_EPRD(PHI_0, PHI_SW_RST)))
+			break;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_phi_init);
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_phi.h linux/drivers/media/pci/saa716x/saa716x_phi.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_phi.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_phi.h	2017-08-11 23:31:25.861389291 +0200
@@ -0,0 +1,8 @@
+#ifndef __SAA716x_PHI_H
+#define __SAA716x_PHI_H
+
+struct saa716x_dev;
+
+extern int saa716x_phi_init(struct saa716x_dev *saa716x);
+
+#endif /* __SAA716x_PHI_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_phi_reg.h linux/drivers/media/pci/saa716x/saa716x_phi_reg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_phi_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_phi_reg.h	2017-08-11 23:31:25.861389291 +0200
@@ -0,0 +1,94 @@
+#ifndef __SAA716x_PHI_REG_H
+#define __SAA716x_PHI_REG_H
+
+/* -------------- PHI_0 Registers -------------- */
+
+#define PHI_0_MODE			0x0000
+#define PHI_0_0_CONFIG			0x0008
+#define PHI_0_1_CONFIG			0x000c
+#define PHI_0_2_CONFIG			0x0010
+#define PHI_0_3_CONFIG			0x0014
+
+#define PHI_POLARITY			0x0038
+#define PHI_TIMEOUT			0x003c
+#define PHI_SW_RST			0x0ff0
+
+#define PHI_0_0_RW_0			0x1000
+#define PHI_0_0_RW_511			0x17fc
+
+#define PHI_0_1_RW_0			0x1800
+#define PHI_0_1_RW_511			0x1ffc
+
+#define PHI_0_2_RW_0			0x2000
+#define PHI_0_2_RW_511			0x27fc
+
+#define PHI_0_3_RW_0			0x2800
+#define PHI_0_3_RW_511			0x2ffc
+
+#define PHI_CSN_DEASSERT		(0x00000001 <<  2)
+#define PHI_AUTO_INCREMENT		(0x00000001 <<  1)
+#define PHI_FIFO_MODE			(0x00000001 <<  0)
+
+#define PHI_DELAY_RD_WR			(0x0000001f << 27)
+#define PHI_EXTEND_RDY3			(0x00000003 << 25)
+#define PHI_EXTEND_RDY2			(0x00000003 << 23)
+#define PHI_EXTEND_RDY1			(0x00000003 << 21)
+#define PHI_EXTEND_RDY0			(0x00000003 << 19)
+#define PHI_RDY3_OD			(0x00000001 << 18)
+#define PHI_RDY2_OD			(0x00000001 << 17)
+#define PHI_RDY1_OD			(0x00000001 << 16)
+#define PHI_RDY0_OD			(0x00000001 << 15)
+#define PHI_ALE_POL			(0x00000001 << 14)
+#define PHI_WRN_POL			(0x00000001 << 13)
+#define PHI_RDN_POL			(0x00000001 << 12)
+#define PHI_RDY3_POL			(0x00000001 << 11)
+#define PHI_RDY2_POL			(0x00000001 << 10)
+#define PHI_RDY1_POL			(0x00000001 <<  9)
+#define PHI_RDY0_POL			(0x00000001 <<  8)
+#define PHI_CSN7_POL			(0x00000001 <<  7)
+#define PHI_CSN6_POL			(0x00000001 <<  6)
+#define PHI_CSN5_POL			(0x00000001 <<  5)
+#define PHI_CSN4_POL			(0x00000001 <<  4)
+#define PHI_CSN3_POL			(0x00000001 <<  3)
+#define PHI_CSN2_POL			(0x00000001 <<  2)
+#define PHI_CSN1_POL			(0x00000001 <<  1)
+#define PHI_CSN0_POL			(0x00000001 <<  0)
+
+/* -------------- PHI_1 Registers -------------- */
+
+#define PHI_1_MODE			0x00004
+#define PHI_1_0_CONFIG			0x00018
+#define PHI_1_1_CONFIG			0x0001c
+#define PHI_1_2_CONFIG			0x00020
+#define PHI_1_3_CONFIG			0x00024
+#define PHI_1_4_CONFIG			0x00028
+#define PHI_1_5_CONFIG			0x0002c
+#define PHI_1_6_CONFIG			0x00030
+#define PHI_1_7_CONFIG			0x00034
+
+#define PHI_1_0_RW_0			0x00000
+#define PHI_1_0_RW_16383		0x0fffc
+
+#define PHI_1_1_RW_0			0x10000
+#define PHI_1_1_RW_16383		0x1fffc
+
+#define PHI_1_2_RW_0			0x20000
+#define PHI_1_2_RW_16383		0x2fffc
+
+#define PHI_1_3_RW_0			0x30000
+#define PHI_1_3_RW_16383		0x3fffc
+
+#define PHI_1_4_RW_0			0x40000
+#define PHI_1_4_RW_16383		0x4fffc
+
+#define PHI_1_5_RW_0			0x50000
+#define PHI_1_5_RW_16383		0x5fffc
+
+#define PHI_1_6_RW_0			0x60000
+#define PHI_1_6_RW_16383		0x6fffc
+
+#define PHI_1_7_RW_0			0x70000
+#define PHI_1_7_RW_16383		0x7fffc
+
+
+#endif /* __SAA716x_PHI_REG_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_priv.h linux/drivers/media/pci/saa716x/saa716x_priv.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_priv.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_priv.h	2017-08-11 23:31:25.873389225 +0200
@@ -0,0 +1,193 @@
+#ifndef __SAA716x_PRIV_H
+#define __SAA716x_PRIV_H
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include "saa716x_i2c.h"
+#include "saa716x_boot.h"
+#include "saa716x_cgu.h"
+#include "saa716x_dma.h"
+#include "saa716x_fgpi.h"
+#include "saa716x_spi.h"
+#include "saa716x_vip.h"
+
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dmxdev.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#define SAA716x_ERROR		0
+#define SAA716x_NOTICE		1
+#define SAA716x_INFO		2
+#define SAA716x_DEBUG		3
+
+#define SAA716x_DEV		(saa716x)->num
+#define SAA716x_VERBOSE		(saa716x)->verbose
+#define SAA716x_MAX_ADAPTERS	4
+
+#define dprintk(__x, __y, __fmt, __arg...) do {								\
+	if (__y) {											\
+		if	((SAA716x_VERBOSE > SAA716x_ERROR) && (SAA716x_VERBOSE > __x))			\
+			printk(KERN_ERR "%s (%d): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\
+		else if	((SAA716x_VERBOSE > SAA716x_NOTICE) && (SAA716x_VERBOSE > __x))			\
+			printk(KERN_NOTICE "%s (%d): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\
+		else if ((SAA716x_VERBOSE > SAA716x_INFO) && (SAA716x_VERBOSE > __x))			\
+			printk(KERN_INFO "%s (%d): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\
+		else if ((SAA716x_VERBOSE > SAA716x_DEBUG) && (SAA716x_VERBOSE > __x))			\
+			printk(KERN_DEBUG "%s (%d): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\
+	} else {											\
+		if (SAA716x_VERBOSE > __x)								\
+			printk(__fmt , ##__arg);							\
+	}												\
+} while(0)
+
+
+#define NXP_SEMICONDUCTOR	0x1131
+#define SAA7160			0x7160
+#define SAA7161			0x7161
+#define SAA7162			0x7162
+
+#define NXP_REFERENCE_BOARD	0x1131
+
+#define MAKE_ENTRY(__subven, __subdev, __chip, __configptr) {		\
+		.vendor		= NXP_SEMICONDUCTOR,			\
+		.device		= (__chip),				\
+		.subvendor	= (__subven),				\
+		.subdevice	= (__subdev),				\
+		.driver_data	= (unsigned long) (__configptr)		\
+}
+
+#define SAA716x_EPWR(__offst, __addr, __data)	writel((__data), (saa716x->mmio + (__offst + __addr)))
+#define SAA716x_EPRD(__offst, __addr)		readl((saa716x->mmio + (__offst + __addr)))
+
+#define SAA716x_MSI_MAX_VECTORS			16
+
+struct saa716x_msix_entry {
+	int vector;
+	u8 desc[32];
+	irqreturn_t (*handler)(int irq, void *dev_id);
+};
+
+struct saa716x_dev;
+struct saa716x_adapter;
+struct saa716x_spi_config;
+
+struct saa716x_adap_config {
+	u32				ts_port;
+	void				(*worker)(unsigned long);
+};
+
+struct saa716x_config {
+	char				*model_name;
+	char				*dev_type;
+
+	enum saa716x_boot_mode		boot_mode;
+
+	int				adapters;
+	int				frontends;
+
+	int (*frontend_attach)(struct saa716x_adapter *adapter, int count);
+	irqreturn_t (*irq_handler)(int irq, void *dev_id);
+
+	struct saa716x_adap_config	adap_config[SAA716x_MAX_ADAPTERS];
+	enum saa716x_i2c_rate		i2c_rate;
+	enum saa716x_i2c_mode		i2c_mode;
+};
+
+struct saa716x_adapter {
+	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			dvb_net;
+
+	struct saa716x_dev		*saa716x;
+
+	u8				feeds;
+	u8				count;
+};
+
+struct saa716x_dev {
+	struct saa716x_config		*config;
+	struct pci_dev			*pdev;
+
+	int				num; /* device count */
+	int				verbose;
+
+	u8 				revision;
+
+	/* PCI */
+	void __iomem			*mmio;
+
+#define MODE_INTA	0
+#define MODE_MSI	1
+#define MODE_MSI_X	2
+	u8				int_type;
+
+	struct msix_entry		msix_entries[SAA716x_MSI_MAX_VECTORS];
+	struct saa716x_msix_entry	saa716x_msix_handler[56];
+	u8				handlers; /* no. of active handlers */
+
+	/* I2C */
+	struct saa716x_i2c		i2c[2];
+	u32				i2c_rate; /* init time */
+	u32				I2C_DEV[2];
+
+	struct saa716x_spi_state	*saa716x_spi;
+	struct saa716x_spi_config	spi_config;
+
+	struct saa716x_adapter		saa716x_adap[SAA716x_MAX_ADAPTERS];
+	struct mutex			adap_lock;
+	struct saa716x_cgu		cgu;
+
+	spinlock_t			gpio_lock;
+	/* DMA */
+
+	struct saa716x_fgpi_stream_port	fgpi[4];
+	struct saa716x_vip_stream_port	vip[2];
+
+	u32				id_offst;
+	u32				id_len;
+	void				*priv;
+
+	/* remote control */
+	void				*ir_priv;
+};
+
+/* PCI */
+extern int saa716x_pci_init(struct saa716x_dev *saa716x);
+extern void saa716x_pci_exit(struct saa716x_dev *saa716x);
+
+/* MSI */
+extern int saa716x_msi_init(struct saa716x_dev *saa716x);
+extern void saa716x_msi_exit(struct saa716x_dev *saa716x);
+extern void saa716x_msiint_disable(struct saa716x_dev *saa716x);
+
+/* DMA */
+extern int saa716x_dma_init(struct saa716x_dev *saa716x);
+extern void saa716x_dma_exit(struct saa716x_dev *saa716x);
+
+/* AUDIO */
+extern int saa716x_audio_init(struct saa716x_dev *saa716x);
+extern void saa716x_audio_exit(struct saa716x_dev *saa716x);
+
+/* Boot */
+extern int saa716x_core_boot(struct saa716x_dev *saa716x);
+extern int saa716x_jetpack_init(struct saa716x_dev *saa716x);
+
+/* Remote control */
+extern int saa716x_ir_init(struct saa716x_dev *saa716x);
+extern void saa716x_ir_exit(struct saa716x_dev *saa716x);
+extern void saa716x_ir_handler(struct saa716x_dev *saa716x, u32 ir_cmd);
+
+#endif /* __SAA716x_PRIV_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_rom.c linux/drivers/media/pci/saa716x/saa716x_rom.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_rom.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_rom.c	2017-08-11 23:31:25.873389225 +0200
@@ -0,0 +1,1071 @@
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "saa716x_rom.h"
+#include "saa716x_adap.h"
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+
+int i;
+
+static int eeprom_read_bytes(struct saa716x_dev *saa716x, u16 reg, u16 len, u8 *val)
+{
+	struct saa716x_i2c *i2c		= saa716x->i2c;
+	struct i2c_adapter *adapter	= &i2c[SAA716x_I2C_BUS_B].i2c_adapter;
+
+	u8 b0[] = { MSB(reg), LSB(reg) };
+	int ret;
+
+	struct i2c_msg msg[] = {
+		{ .addr = 0x50, .flags = 0,	   .buf = b0,  .len = sizeof (b0) },
+		{ .addr = 0x50,	.flags = I2C_M_RD, .buf = val, .len = len }
+	};
+
+	ret = i2c_transfer(adapter, msg, 2);
+	if (ret != 2) {
+		dprintk(SAA716x_ERROR, 1, "read error <reg=0x%02x, ret=%i>", reg, ret);
+		return -EREMOTEIO;
+	}
+
+	return ret;
+}
+
+static int saa716x_read_rombytes(struct saa716x_dev *saa716x, u16 reg, u16 len, u8 *val)
+{
+	struct saa716x_i2c *i2c		= saa716x->i2c;
+	struct i2c_adapter *adapter	= &i2c[SAA716x_I2C_BUS_B].i2c_adapter;
+	struct i2c_msg msg[2];
+
+	u8 b0[2];
+	int ret, count;
+
+	count = len / DUMP_BYTES;
+	if (len % DUMP_BYTES)
+		count++;
+
+	count *= 2;
+
+	for (i = 0; i < count; i += 2) {
+		dprintk(SAA716x_DEBUG, 1, "Length=%d, Count=%d, Reg=0x%02x",
+			len,
+			count,
+			reg);
+
+		b0[0] = MSB(reg);
+		b0[1] = LSB(reg);
+
+		/* Write */
+		msg[0].addr  = 0x50;
+		msg[0].flags = 0;
+		msg[0].buf   = b0;
+		msg[0].len   = 2;
+
+		/* Read */
+		msg[1].addr  = 0x50;
+		msg[1].flags = I2C_M_RD;
+		msg[1].buf   = val;
+
+		if (i == (count - 2)) {
+			/* last message */
+			if (len % DUMP_BYTES) {
+				msg[1].len = len % DUMP_BYTES;
+				dprintk(SAA716x_DEBUG, 1, "Last Message length=%d", len % DUMP_BYTES);
+			} else {
+				msg[1].len = DUMP_BYTES;
+			}
+		} else {
+			msg[1].len = DUMP_BYTES;
+		}
+
+		ret = i2c_transfer(adapter, msg, 2);
+		if (ret != 2) {
+			dprintk(SAA716x_ERROR, 1, "read error <reg=0x%02x, ret=%i>", reg, ret);
+			return -EREMOTEIO;
+		}
+
+		reg += DUMP_BYTES;
+		val += DUMP_BYTES;
+	}
+
+	return 0;
+}
+
+static int saa716x_get_offset(struct saa716x_dev *saa716x, u8 *buf, u32 *offset)
+{
+	int i;
+
+	*offset = 0;
+	for (i = 0; i < 256; i++) {
+		if (!(strncmp("START", buf + i, 5)))
+			break;
+	}
+	dprintk(SAA716x_DEBUG, 1, "Offset @ %d", i);
+	*offset = i;
+
+	return 0;
+}
+
+static int saa716x_eeprom_header(struct saa716x_dev *saa716x,
+				 struct saa716x_romhdr *rom_header,
+				 u8 *buf,
+				 u32 *offset)
+{
+	memcpy(rom_header, &buf[*offset], sizeof (struct saa716x_romhdr));
+	if (rom_header->header_size != sizeof (struct saa716x_romhdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			sizeof (struct saa716x_romhdr),
+			rom_header->header_size);
+
+		return -1;
+	}
+	*offset += sizeof (struct saa716x_romhdr);
+
+	dprintk(SAA716x_DEBUG, 0, "SAA%02x ROM: Data=%d bytes\n",
+		saa716x->pdev->device,
+		rom_header->data_size);
+
+	dprintk(SAA716x_DEBUG, 0, "SAA%02x ROM: Version=%d\n",
+		saa716x->pdev->device,
+		rom_header->version);
+
+	dprintk(SAA716x_DEBUG, 0, "SAA%02x ROM: Devices=%d\n",
+		saa716x->pdev->device,
+		rom_header->devices);
+
+	dprintk(SAA716x_DEBUG, 0, "SAA%02x ROM: Compressed=%d\n\n",
+		saa716x->pdev->device,
+		rom_header->compression);
+
+	return 0;
+}
+
+int saa716x_dump_eeprom(struct saa716x_dev *saa716x)
+{
+	struct saa716x_romhdr rom_header;
+	u8 buf[DUMP_BYTES];
+	int i, err = 0;
+	u32 offset = 0;
+
+	err = eeprom_read_bytes(saa716x, DUMP_OFFST, DUMP_BYTES, buf);
+	if (err < 0) {
+		dprintk(SAA716x_ERROR, 1, "EEPROM Read error");
+		return err;
+	}
+
+	dprintk(SAA716x_DEBUG, 0, "    Card: %s\n",
+		saa716x->config->model_name);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"    ---------------- SAA%02x ROM @ Offset 0x%02x ----------------",
+		saa716x->pdev->device,
+		DUMP_OFFST);
+
+	for (i = 0; i < DUMP_BYTES; i++) {
+		if ((i % 16) == 0) {
+			dprintk(SAA716x_DEBUG, 0, "\n    ");
+			dprintk(SAA716x_DEBUG, 0, "%04x: ", i);
+		}
+
+		if ((i %  8) == 0)
+			dprintk(SAA716x_DEBUG, 0, " ");
+		if ((i %  4) == 0)
+			dprintk(SAA716x_DEBUG, 0, " ");
+		dprintk(SAA716x_DEBUG, 0, "%02x ", buf[i]);
+	}
+	dprintk(SAA716x_DEBUG, 0, "\n");
+	dprintk(SAA716x_DEBUG, 0,
+		"    ---------------- SAA%02x ROM Dump end ---------------------\n\n",
+		saa716x->pdev->device);
+
+	err = saa716x_get_offset(saa716x, buf, &offset);
+	if (err != 0) {
+		dprintk(SAA716x_ERROR, 1, "ERROR: Descriptor not found <%d>", err);
+		return err;
+	}
+	offset += 5;
+	saa716x->id_offst = offset;
+	/* Get header */
+	err = saa716x_eeprom_header(saa716x, &rom_header, buf, &offset);
+	if (err != 0) {
+		dprintk(SAA716x_ERROR, 1, "ERROR: Header Read failed <%d>", err);
+		return -1;
+	}
+	saa716x->id_len = rom_header.data_size;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_dump_eeprom);
+
+static void saa716x_descriptor_dbg(struct saa716x_dev *saa716x,
+				   u8 *buf,
+				   u32 *offset,
+				   u8 size,
+				   u8 ext_size)
+{
+	int i;
+
+	dprintk(SAA716x_DEBUG, 0, "       ");
+	for (i = 0; i < 49; i++)
+		dprintk(SAA716x_DEBUG, 0, "-");
+
+	for (i = 0; i < size + ext_size; i++) {
+		if ((i % 16) == 0)
+			dprintk(SAA716x_DEBUG, 0, "\n      ");
+		if ((i %  8) == 0)
+			dprintk(SAA716x_DEBUG, 0, " ");
+		if ((i %  4) == 0)
+			dprintk(SAA716x_DEBUG, 0, " ");
+
+		dprintk(SAA716x_DEBUG, 0, "%02x ", buf[*offset + i]);
+	}
+
+	dprintk(SAA716x_DEBUG, 0, "\n       ");
+	for (i = 0; i < 49; i++)
+		dprintk(SAA716x_DEBUG, 0, "-");
+	dprintk(SAA716x_DEBUG, 0, "\n");
+
+}
+
+static int saa716x_decoder_info(struct saa716x_dev *saa716x,
+				u8 *buf,
+				u32 *offset)
+{
+	struct saa716x_decoder_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_decoder_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_decoder_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_decoder_hdr));
+
+		return -1;
+	}
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext Data=%d bytes\n\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+	return 0;
+}
+
+static int saa716x_gpio_info(struct saa716x_dev *saa716x,
+			     u8 *buf,
+			     u32 *offset)
+{
+	struct saa716x_gpio_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_gpio_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_gpio_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_gpio_hdr));
+
+		return -1;
+	}
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Pins=%d\n",
+		saa716x->pdev->device,
+		header.pins);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d\n\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+
+	return 0;
+}
+
+static int saa716x_video_decoder_info(struct saa716x_dev *saa716x,
+				      u8 *buf,
+				      u32 *offset)
+{
+	struct saa716x_video_decoder_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_video_decoder_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_video_decoder_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_video_decoder_hdr));
+
+		return -1;
+	}
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: PORT 0=0x%02x\n",
+		saa716x->pdev->device,
+		header.video_port0);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: PORT 1=0x%02x\n",
+		saa716x->pdev->device,
+		header.video_port1);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: PORT 2=0x%02x\n",
+		saa716x->pdev->device,
+		header.video_port2);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: VBI PORT ID=0x%02x\n",
+		saa716x->pdev->device,
+		header.vbi_port_id);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Video PORT Type=0x%02x\n",
+		saa716x->pdev->device,
+		header.video_port_type);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: VBI PORT Type=0x%02x\n",
+		saa716x->pdev->device,
+		header.vbi_port_type);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Encoder PORT Type=0x%02x\n",
+		saa716x->pdev->device,
+		header.encoder_port_type);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Video Output=0x%02x\n",
+		saa716x->pdev->device,
+		header.video_output);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: VBI Output=0x%02x\n",
+		saa716x->pdev->device,
+		header.vbi_output);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Encoder Output=0x%02x\n",
+		saa716x->pdev->device,
+		header.encoder_output);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+	return 0;
+}
+
+static int saa716x_audio_decoder_info(struct saa716x_dev *saa716x,
+				      u8 *buf,
+				      u32 *offset)
+{
+	struct saa716x_audio_decoder_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_audio_decoder_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_audio_decoder_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_audio_decoder_hdr));
+
+		return -1;
+	}
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+	return 0;
+}
+
+static int saa716x_event_source_info(struct saa716x_dev *saa716x,
+				     u8 *buf,
+				     u32 *offset)
+{
+	struct saa716x_evsrc_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_evsrc_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_evsrc_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_evsrc_hdr));
+
+		return -1;
+	}
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+	return 0;
+}
+
+static int saa716x_crossbar_info(struct saa716x_dev *saa716x,
+				 u8 *buf,
+				 u32 *offset)
+{
+	struct saa716x_xbar_hdr header;
+	struct saa716x_xbar_pair_info pair_info;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_xbar_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_xbar_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_xbar_hdr));
+
+		return -1;
+	}
+
+	memcpy(&pair_info, &buf[*offset], sizeof (struct saa716x_xbar_pair_info));
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Pairs=%d\n",
+		saa716x->pdev->device,
+		header.pair_inputs);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data + (sizeof (struct saa716x_xbar_pair_info) * header.pair_inputs);
+	return 0;
+}
+
+static int saa716x_tuner_info(struct saa716x_dev *saa716x,
+			      u8 *buf,
+			      u32 *offset)
+{
+	struct saa716x_tuner_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_tuner_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_tuner_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_tuner_hdr));
+
+		return -1;
+	}
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+	return 0;
+}
+
+static int saa716x_pll_info(struct saa716x_dev *saa716x,
+			    u8 *buf,
+			    u32 *offset)
+{
+	struct saa716x_pll_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_pll_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_pll_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_pll_hdr));
+
+		return -1;
+	}
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+	return 0;
+}
+
+static int saa716x_channel_decoder_info(struct saa716x_dev *saa716x,
+					u8 *buf,
+					u32 *offset)
+{
+	struct saa716x_channel_decoder_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_channel_decoder_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_channel_decoder_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_channel_decoder_hdr));
+
+		return -1;
+	}
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+	return 0;
+}
+
+static int saa716x_encoder_info(struct saa716x_dev *saa716x,
+				u8 *buf,
+				u32 *offset)
+{
+	struct saa716x_encoder_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_encoder_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_encoder_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_encoder_hdr));
+
+		return -1;
+	}
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+	return 0;
+}
+
+static int saa716x_ir_info(struct saa716x_dev *saa716x,
+			   u8 *buf,
+			   u32 *offset)
+{
+	struct saa716x_ir_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_ir_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_ir_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_ir_hdr));
+
+		return -1;
+	}
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+	return 0;
+}
+
+static int saa716x_eeprom_info(struct saa716x_dev *saa716x,
+			       u8 *buf,
+			       u32 *offset)
+{
+	struct saa716x_eeprom_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_eeprom_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_eeprom_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_eeprom_hdr));
+
+		return -1;
+	}
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+	return 0;
+}
+
+static int saa716x_filter_info(struct saa716x_dev *saa716x,
+			       u8 *buf,
+			       u32 *offset)
+{
+	struct saa716x_filter_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_filter_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_filter_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_filter_hdr));
+
+		return -1;
+	}
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+	return 0;
+}
+
+static int saa716x_streamdev_info(struct saa716x_dev *saa716x,
+				  u8 *buf,
+				  u32 *offset)
+{
+	struct saa716x_streamdev_hdr header;
+
+	memcpy(&header, &buf[*offset], sizeof (struct saa716x_streamdev_hdr));
+	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
+	if (header.size != sizeof (struct saa716x_streamdev_hdr)) {
+		dprintk(SAA716x_ERROR, 1,
+			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			header.size,
+			sizeof (struct saa716x_streamdev_hdr));
+
+		return -1;
+	}
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		header.size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n",
+		saa716x->pdev->device,
+		header.ext_data);
+
+	*offset += header.size + header.ext_data;
+	return 0;
+}
+
+static int saa716x_unknown_device_info(struct saa716x_dev *saa716x,
+				       u8 *buf,
+				       u32 *offset)
+{
+	u8 size;
+	u8 ext_size = 0;
+
+	size = buf[*offset];
+	if (size > 1)
+		ext_size = buf[*offset + size -1];
+
+	saa716x_descriptor_dbg(saa716x, buf, offset, size, ext_size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		size);
+
+	dprintk(SAA716x_DEBUG, 0,
+		"        SAA%02x ROM: Ext data=%d bytes\n\n",
+		saa716x->pdev->device,
+		ext_size);
+
+	*offset += size + ext_size;
+	return 0;
+}
+
+
+static void saa716x_device_dbg(struct saa716x_dev *saa716x,
+			       u8 *buf,
+			       u32 *offset,
+			       u8 size,
+			       u8 ext_size,
+			       u8 addr_size)
+{
+	int i;
+
+	dprintk(SAA716x_DEBUG, 0, "   ");
+	for (i = 0; i < 53; i++)
+		dprintk(SAA716x_DEBUG, 0, "-");
+
+	for (i = 0; i < size + ext_size + addr_size; i++) {
+		if ((i % 16) == 0)
+			dprintk(SAA716x_DEBUG, 0, "\n  ");
+		if ((i %  8) == 0)
+			dprintk(SAA716x_DEBUG, 0, " ");
+		if ((i %  4) == 0)
+			dprintk(SAA716x_DEBUG, 0, " ");
+
+		dprintk(SAA716x_DEBUG, 0, "%02x ", buf[*offset + i]);
+	}
+
+	dprintk(SAA716x_DEBUG, 0, "\n   ");
+	for (i = 0; i < 53; i++)
+		dprintk(SAA716x_DEBUG, 0, "-");
+	dprintk(SAA716x_DEBUG, 0, "\n");
+
+}
+
+
+static int saa716x_device_info(struct saa716x_dev *saa716x,
+			       struct saa716x_devinfo *device,
+			       u8 *buf,
+			       u32 *offset)
+{
+	u8 address = 0;
+
+	memcpy(device, &buf[*offset], sizeof (struct saa716x_devinfo));
+	if (device->struct_size != sizeof (struct saa716x_devinfo)) {
+		dprintk(SAA716x_ERROR, 1, "ERROR: Device size mismatch! Read=%d bytes, expected=%d bytes",
+		device->struct_size,
+		sizeof (struct saa716x_devinfo));
+
+		return -1;
+	}
+
+	saa716x_device_dbg(saa716x,
+			   buf,
+			   offset,
+			   device->struct_size,
+			   device->extd_data_size,
+			   device->addr_size);
+
+	*offset += device->struct_size;
+
+	if (device->addr_size) {
+		address = buf[*offset];
+		address >>= 1;
+		*offset += device->addr_size;
+	}
+
+	dprintk(SAA716x_DEBUG, 0, "    SAA%02x ROM: Device @ 0x%02x\n",
+		saa716x->pdev->device,
+		address);
+
+	dprintk(SAA716x_DEBUG, 0, "    SAA%02x ROM: Size=%d bytes\n",
+		saa716x->pdev->device,
+		device->struct_size);
+
+	dprintk(SAA716x_DEBUG, 0, "    SAA%02x ROM: Device ID=0x%02x\n",
+		saa716x->pdev->device,
+		device->device_id);
+
+	dprintk(SAA716x_DEBUG, 0, "    SAA%02x ROM: Master ID=0x%02x\n",
+		saa716x->pdev->device,
+		device->master_devid);
+
+	dprintk(SAA716x_DEBUG, 0, "    SAA%02x ROM: Bus ID=0x%02x\n",
+		saa716x->pdev->device,
+		device->master_busid);
+
+	dprintk(SAA716x_DEBUG, 0, "    SAA%02x ROM: Device type=0x%02x\n",
+		saa716x->pdev->device,
+		device->device_type);
+
+	dprintk(SAA716x_DEBUG, 0, "    SAA%02x ROM: Implementation ID=0x%02x\n",
+		saa716x->pdev->device,
+		device->implem_id);
+
+	dprintk(SAA716x_DEBUG, 0, "    SAA%02x ROM: Path ID=0x%02x\n",
+		saa716x->pdev->device,
+		device->path_id);
+
+	dprintk(SAA716x_DEBUG, 0, "    SAA%02x ROM: GPIO ID=0x%02x\n",
+		saa716x->pdev->device,
+		device->gpio_id);
+
+	dprintk(SAA716x_DEBUG, 0, "    SAA%02x ROM: Address=%d bytes\n",
+		saa716x->pdev->device,
+		device->addr_size);
+
+	dprintk(SAA716x_DEBUG, 0, "    SAA%02x ROM: Extended data=%d bytes\n\n",
+		saa716x->pdev->device,
+		device->extd_data_size);
+
+	if (device->extd_data_size) {
+		u32 mask;
+
+		mask = 0x00000001;
+		while (mask) {
+			if (device->device_type & mask) {
+				switch (mask) {
+				case DECODER_DEVICE:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found decoder device\n",
+						saa716x->pdev->device);
+
+					saa716x_decoder_info(saa716x, buf, offset);
+					break;
+
+				case GPIO_SOURCE:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found GPIO device\n",
+						saa716x->pdev->device);
+
+					saa716x_gpio_info(saa716x, buf, offset);
+					break;
+
+				case VIDEO_DECODER:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found Video Decoder device\n",
+						saa716x->pdev->device);
+
+					saa716x_video_decoder_info(saa716x, buf, offset);
+					break;
+
+				case AUDIO_DECODER:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found Audio Decoder device\n",
+						saa716x->pdev->device);
+
+					saa716x_audio_decoder_info(saa716x, buf, offset);
+					break;
+
+				case EVENT_SOURCE:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found Event source\n",
+						saa716x->pdev->device);
+
+					saa716x_event_source_info(saa716x, buf, offset);
+					break;
+
+				case CROSSBAR:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found Crossbar device\n",
+						saa716x->pdev->device);
+
+					saa716x_crossbar_info(saa716x, buf, offset);
+					break;
+
+				case TUNER_DEVICE:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found Tuner device\n",
+						saa716x->pdev->device);
+
+					saa716x_tuner_info(saa716x, buf, offset);
+					break;
+
+				case PLL_DEVICE:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found PLL device\n",
+						saa716x->pdev->device);
+
+					saa716x_pll_info(saa716x, buf, offset);
+					break;
+
+				case CHANNEL_DECODER:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found Channel Demodulator device\n",
+						saa716x->pdev->device);
+
+					saa716x_channel_decoder_info(saa716x, buf, offset);
+					break;
+
+				case RDS_DECODER:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found RDS Decoder device\n",
+						saa716x->pdev->device);
+
+					saa716x_unknown_device_info(saa716x, buf, offset);
+					break;
+
+				case ENCODER_DEVICE:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found Encoder device\n",
+						saa716x->pdev->device);
+
+					saa716x_encoder_info(saa716x, buf, offset);
+					break;
+
+				case IR_DEVICE:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found IR device\n",
+						saa716x->pdev->device);
+
+					saa716x_ir_info(saa716x, buf, offset);
+					break;
+
+				case EEPROM_DEVICE:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found EEPROM device\n",
+						saa716x->pdev->device);
+
+					saa716x_eeprom_info(saa716x, buf, offset);
+					break;
+
+				case NOISE_FILTER:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found Noise filter device\n",
+						saa716x->pdev->device);
+
+					saa716x_filter_info(saa716x, buf, offset);
+					break;
+
+				case LNx_DEVICE:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found LNx device\n",
+						saa716x->pdev->device);
+
+					saa716x_unknown_device_info(saa716x, buf, offset);
+					break;
+
+				case STREAM_DEVICE:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found streaming device\n",
+						saa716x->pdev->device);
+
+					saa716x_streamdev_info(saa716x, buf, offset);
+					break;
+
+				case CONFIGSPACE_DEVICE:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found Configspace device\n",
+						saa716x->pdev->device);
+
+					saa716x_unknown_device_info(saa716x, buf, offset);
+					break;
+
+				default:
+					dprintk(SAA716x_DEBUG, 0,
+						"        SAA%02x ROM: Found unknown device\n",
+						saa716x->pdev->device);
+
+					saa716x_unknown_device_info(saa716x, buf, offset);
+					break;
+				}
+			}
+			mask <<= 1;
+		}
+	}
+
+	dprintk(SAA716x_DEBUG, 0, "\n");
+
+	return 0;
+}
+
+int saa716x_eeprom_data(struct saa716x_dev *saa716x)
+{
+	struct saa716x_romhdr rom_header;
+	struct saa716x_devinfo *device;
+
+	u8 buf[1024];
+	int i, ret = 0;
+	u32 offset = 0;
+
+	/* dump */
+	ret = saa716x_read_rombytes(saa716x, saa716x->id_offst, saa716x->id_len + 8, buf);
+	if (ret < 0) {
+		dprintk(SAA716x_ERROR, 1, "EEPROM Read error <%d>", ret);
+		goto err0;
+	}
+
+	/* Get header */
+	ret = saa716x_eeprom_header(saa716x, &rom_header, buf, &offset);
+	if (ret != 0) {
+		dprintk(SAA716x_ERROR, 1, "ERROR: Header Read failed <%d>", ret);
+		goto err0;
+	}
+
+	/* allocate for device info */
+	device = kzalloc(sizeof (struct saa716x_devinfo) * rom_header.devices, GFP_KERNEL);
+	if (device == NULL) {
+		dprintk(SAA716x_ERROR, 1, "ERROR: out of memory");
+		goto err0;
+	}
+
+	for (i = 0; i < rom_header.devices; i++) {
+		dprintk(SAA716x_DEBUG, 0, "    SAA%02x ROM: ===== Device %d =====\n",
+			saa716x->pdev->device,
+			i);
+
+		ret = saa716x_device_info(saa716x, &device[i], buf, &offset);
+		if (ret != 0) {
+			dprintk(SAA716x_ERROR, 1, "ERROR: Device info read failed <%d>", ret);
+			goto err1;
+		}
+	}
+
+	kfree(device);
+
+	return 0;
+
+err1:
+	kfree(device);
+
+err0:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(saa716x_eeprom_data);
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_rom.h linux/drivers/media/pci/saa716x/saa716x_rom.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_rom.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_rom.h	2017-08-11 23:31:25.873389225 +0200
@@ -0,0 +1,253 @@
+#ifndef __SAA716x_ROM_H
+#define __SAA716x_ROM_H
+
+
+#define MSB(__x)	((__x >> 8) & 0xff)
+#define LSB(__x)	(__x & 0xff)
+
+#define DUMP_BYTES	0xf0
+#define DUMP_OFFST	0x000
+
+struct saa716x_dev;
+
+struct saa716x_romhdr {
+	u16	header_size;
+	u8	compression;
+	u8	version;
+	u16	data_size;
+	u8	devices;
+	u8	checksum;
+} __attribute__((packed));
+
+struct saa716x_devinfo {
+	u8	struct_size;
+	u8	device_id;
+	u8	master_devid;
+	u8	master_busid;
+	u32	device_type;
+	u16	implem_id;
+	u8	path_id;
+	u8	gpio_id;
+	u16	addr_size;
+	u16	extd_data_size;
+} __attribute__((packed));
+
+enum saa716x_device_types {
+	DECODER_DEVICE		= 0x00000001,
+	GPIO_SOURCE		= 0x00000002,
+	VIDEO_DECODER		= 0x00000004,
+	AUDIO_DECODER		= 0x00000008,
+	EVENT_SOURCE		= 0x00000010,
+	CROSSBAR		= 0x00000020,
+	TUNER_DEVICE		= 0x00000040,
+	PLL_DEVICE		= 0x00000080,
+	CHANNEL_DECODER		= 0x00000100,
+	RDS_DECODER		= 0x00000200,
+	ENCODER_DEVICE		= 0x00000400,
+	IR_DEVICE		= 0x00000800,
+	EEPROM_DEVICE		= 0x00001000,
+	NOISE_FILTER		= 0x00002000,
+	LNx_DEVICE		= 0x00004000,
+	STREAM_DEVICE		= 0x00010000,
+	CONFIGSPACE_DEVICE	= 0x80000000
+};
+
+struct saa716x_decoder_hdr {
+	u8 size;
+	u8 ext_data;
+};
+
+struct saa716x_decoder_info {
+	struct saa716x_decoder_hdr decoder_hdr;
+	u8 *ext_data;
+};
+
+struct saa716x_gpio_hdr {
+	u8 size;
+	u8 pins;
+	u8 rsvd;
+	u8 ext_data;
+};
+
+struct saa716x_gpio_info {
+	struct saa716x_gpio_hdr gpio_hdr;
+	u8 *ext_data;
+};
+
+struct saa716x_video_decoder_hdr {
+	u8 size;
+	u8 video_port0;
+	u8 video_port1;
+	u8 video_port2;
+	u8 vbi_port_id;
+	u8 video_port_type;
+	u8 vbi_port_type;
+	u8 encoder_port_type;
+	u8 video_output;
+	u8 vbi_output;
+	u8 encoder_output;
+	u8 ext_data;
+};
+
+struct saa716x_video_decoder_info {
+	struct saa716x_video_decoder_hdr decoder_hdr;
+	u8 *ext_data;
+};
+
+struct saa716x_audio_decoder_hdr {
+	u8 size;
+	u8 port;
+	u8 output;
+	u8 ext_data;
+};
+
+struct saa716x_audio_decoder_info {
+	struct saa716x_audio_decoder_hdr decoder_hdr;
+	u8 *ext_data;
+};
+
+struct saa716x_evsrc_hdr {
+	u8 size;
+	u8 master_devid;
+	u16 condition_id;
+	u8 rsvd;
+	u8 ext_data;
+};
+
+struct saa716x_evsrc_info {
+	struct saa716x_evsrc_hdr evsrc_hdr;
+	u8 *ext_data;
+};
+
+enum saa716x_input_pair_type {
+	TUNER_SIF	= 0x00,
+	TUNER_LINE	= 0x01,
+	TUNER_SPDIF	= 0x02,
+	TUNER_NONE	= 0x03,
+	CVBS_LINE	= 0x04,
+	CVBS_SPDIF	= 0x05,
+	CVBS_NONE	= 0x06,
+	YC_LINE		= 0x07,
+	YC_SPDIF	= 0x08,
+	YC_NONE		= 0x09,
+	YPbPr_LINE	= 0x0a,
+	YPbPr_SPDIF	= 0x0b,
+	YPbPr_NONE	= 0x0c,
+	NO_LINE		= 0x0d,
+	NO_SPDIF	= 0x0e,
+	RGB_LINE	= 0x0f,
+	RGB_SPDIF	= 0x10,
+	RGB_NONE	= 0x11
+};
+
+struct saa716x_xbar_pair_info {
+	u8 pair_input_type;
+	u8 video_input_id;
+	u8 audio_input_id;
+};
+
+struct saa716x_xbar_hdr {
+	u8 size;
+	u8 pair_inputs;
+	u8 pair_route_default;
+	u8 ext_data;
+};
+
+struct saa716x_xbar_info {
+	struct saa716x_xbar_hdr xbar_hdr;
+	struct saa716x_xbar_pair_info *pair_info;
+	u8 *ext_data;
+};
+
+struct saa716x_tuner_hdr {
+	u8 size;
+	u8 ext_data;
+};
+
+struct saa716x_tuner_info {
+	struct saa716x_tuner_hdr tuner_hdr;
+	u8 *ext_data;
+};
+
+struct saa716x_pll_hdr {
+	u8 size;
+	u8 ext_data;
+};
+
+struct saa716x_pll_info {
+	struct saa716x_pll_hdr pll_hdr;
+	u8 *ext_data;
+};
+
+struct saa716x_channel_decoder_hdr {
+	u8 size;
+	u8 port;
+	u8 ext_data;
+};
+
+struct saa716x_channel_decoder_info {
+	struct saa716x_channel_decoder_hdr channel_dec_hdr;
+	u8 *ext_data;
+};
+
+struct saa716x_encoder_hdr {
+	u8 size;
+	u8 stream_port0;
+	u8 stream_port1;
+	u8 ext_data;
+};
+
+struct saa716x_encoder_info {
+	struct saa716x_encoder_hdr encoder_hdr;
+	u8 *ext_data;
+};
+
+struct saa716x_ir_hdr {
+	u8 size;
+	u8 ir_caps;
+	u8 ext_data;
+};
+
+struct saa716x_ir_info {
+	struct saa716x_ir_hdr ir_hdr;
+	u8 *ext_data;
+};
+
+struct saa716x_eeprom_hdr {
+	u8 size;
+	u8 rel_device;
+	u8 ext_data;
+};
+
+struct saa716x_eeprom_info {
+	struct saa716x_eeprom_hdr eeprom_hdr;
+	u8 *ext_data;
+};
+
+struct saa716x_filter_hdr {
+	u8 size;
+	u8 video_decoder;
+	u8 audio_decoder;
+	u8 event_source;
+	u8 ext_data;
+};
+
+struct saa716x_filter_info {
+	struct saa716x_filter_hdr filter_hdr;
+	u8 *ext_data;
+};
+
+struct saa716x_streamdev_hdr {
+	u8 size;
+	u8 ext_data;
+};
+
+struct saa716x_streamdev_info {
+	struct saa716x_streamdev_hdr streamdev_hdr;
+	u8 *ext_data;
+};
+
+extern int saa716x_dump_eeprom(struct saa716x_dev *saa716x);
+extern int saa716x_eeprom_data(struct saa716x_dev *saa716x);
+
+#endif /* __SAA716x_ROM_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_spi.c linux/drivers/media/pci/saa716x/saa716x_spi.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_spi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_spi.c	2017-08-11 23:31:25.873389225 +0200
@@ -0,0 +1,313 @@
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+
+#include <linux/spi/spi.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_spi_reg.h"
+#include "saa716x_spi.h"
+#include "saa716x_priv.h"
+
+#if 0 // not needed atm
+int saa716x_spi_irqevent(struct saa716x_dev *saa716x)
+{
+	u32 stat, mask;
+
+	BUG_ON(saa716x == NULL);
+
+	stat = SAA716x_EPRD(SPI, SPI_STATUS);
+	mask = SAA716x_EPRD(SPI, SPI_CONTROL_REG) & SPI_SERIAL_INTER_ENABLE;
+	if ((!stat && !mask))
+		return -1;
+
+	dprintk(SAA716x_DEBUG, 0, "SPI event: Stat=<%02x>", stat);
+
+	if (stat & SPI_TRANSFER_FLAG)
+		dprintk(SAA716x_DEBUG, 0, "<TXFER> ");
+	if (stat & SPI_WRITE_COLLISSION)
+		dprintk(SAA716x_DEBUG, 0, "<WCOLL> ");
+	if (stat & SPI_READ_OVERRUN)
+		dprintk(SAA716x_DEBUG, 0, "<ROFLW> ");
+	if (stat & SPI_MODE_FAULT)
+		dprintk(SAA716x_DEBUG, 0, "<FAULT> ");
+	if (stat & SPI_SLAVE_ABORT)
+		dprintk(SAA716x_DEBUG, 0, "<ABORT> ");
+
+	return 0;
+}
+#endif
+
+void saa716x_spi_write(struct saa716x_dev *saa716x, const u8 *data, int length)
+{
+	int i;
+	u32 value;
+	int rounds;
+
+	for (i = 0; i < length; i++) {
+		SAA716x_EPWR(SPI, SPI_DATA, data[i]);
+		rounds = 0;
+		value = SAA716x_EPRD(SPI, SPI_STATUS);
+
+		while ((value & SPI_TRANSFER_FLAG) == 0 && rounds < 5000) {
+			value = SAA716x_EPRD(SPI, SPI_STATUS);
+			rounds++;
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(saa716x_spi_write);
+
+#if 0 // not needed atm
+static int saa716x_spi_status(struct saa716x_dev *saa716x, u32 *status)
+{
+	u32 stat;
+
+	stat = SAA716x_EPRD(SPI, SPI_STATUS);
+
+	if (stat & SPI_TRANSFER_FLAG)
+		dprintk(SAA716x_DEBUG, 1, "Transfer complete <%02x>", stat);
+
+	if (stat & SPI_WRITE_COLLISSION)
+		dprintk(SAA716x_DEBUG, 1, "Write collission <%02x>", stat);
+
+	if (stat & SPI_READ_OVERRUN)
+		dprintk(SAA716x_DEBUG, 1, "Read Overrun <%02x>", stat);
+
+	if (stat & SPI_MODE_FAULT)
+		dprintk(SAA716x_DEBUG, 1, "MODE fault <%02x>", stat);
+
+	if (stat & SPI_SLAVE_ABORT)
+		dprintk(SAA716x_DEBUG, 1, "SLAVE abort <%02x>", stat);
+
+	*status = stat;
+
+	return 0;
+}
+
+#define SPI_CYCLE_TIMEOUT	100
+
+static int saa716x_spi_xfer(struct saa716x_dev *saa716x, u32 *data)
+{
+	u32 i, status = 0;
+
+	/* write data and wait for completion */
+	SAA716x_EPWR(SPI, SPI_DATA, data[i]);
+	for (i = 0; i < SPI_CYCLE_TIMEOUT; i++) {
+		msleep(10);
+		saa716x_spi_status(saa716x, &status);
+#if 0
+		if (status & SPI_TRANSFER_FLAG) {
+			data = SAA716x_EPRD(SPI, SPI_DATA);
+			return 0;
+		}
+#endif
+		if (status & (SPI_WRITE_COLLISSION	|
+			      SPI_READ_OVERRUN		|
+			      SPI_MODE_FAULT		|
+			      SPI_SLAVE_ABORT))
+
+			return -EIO;
+	}
+
+	return -EIO;
+}
+
+#if 0
+static int saa716x_spi_wr(struct saa716x_dev *saa716x, const u8 *data, int length)
+{
+	struct saa716x_spi_config *config = saa716x->spi_config;
+	u32 gpio_mask;
+	int ret = 0;
+
+	// protect against multiple access
+	spin_lock(&saa716x->gpio_lock);
+
+	// configure the module
+	saa716x_spi_config(saa716x);
+
+    // check input
+
+	// change polarity of GPIO if active high
+	if (config->active_hi) {
+		select  = 1;
+		release = 0;
+	}
+
+	// configure GPIO, first set output register to low selected level
+	saa716x_gpio_write(saa716x, gpio, select);
+
+	// set mode register to register controlled (0)
+	gpio_mask = (1 << gpio);
+	saa716x_set_gpio_mode(saa716x, gpio_mask, 0);
+
+	// configure bit as output (0)
+	saa716x_gpio_ctl(saa716x, gpio_mask, 0);
+
+	// wait at least 500ns before sending a byte
+	msleep(1);
+
+	// send command
+	for (i = 0; i < dwCommandSize; i++) {
+		ucData   = 0;
+//		dwStatus = TransferData(pucCommand[i], &ucData);
+		ret = saa716x_spi_xfer(saa716x);
+		//tmDBGPRINTEx(4,("Info: Command 0x%x ", pucCommand[i]  ));
+
+		/* If command length > 1, disable CS at the end of each command.
+		 * But after the last command byte CS must be left active!
+		 */
+		if ((dwCommandSize > 1) && (i < dwCommandSize - 1)) {
+
+			saa716x_gpio_write(saa716x, gpio, release);
+			msleep(1); /* 500 nS minimum */
+			saa716x_gpio_write(saa716x, gpio, select);
+		}
+
+		if (ret != 0) {
+			dprintk(SAA716x_ERROR, 1, "ERROR: Command transfer failed");
+			msleep(1); /* 500 nS minimum */
+			saa716x_gpio_write(saa716x, gpio, release); /* release GPIO */
+			spin_unlock(&saa716x->spi_lock);
+			return ret;
+		}
+
+		if (config->LSB_first)
+			dwTransferByte++;
+		else
+			dwTransferByte--;
+	}
+
+// assume that the byte order is the same as the bit order
+
+// send read address
+
+// send data
+
+// wait at least 500ns before releasing slave
+
+// release GPIO pin
+
+	// release spinlock
+	spin_unlock(&saa716x->gpio_lock);
+}
+#endif
+
+#define MODEBITS (SPI_CPOL | SPI_CPHA)
+
+static int saa716x_spi_setup(struct spi_device *spi)
+{
+	struct spi_master *master		= spi->master;
+	struct saa716x_spi_state *saa716x_spi	= spi_master_get_devdata(master);
+	struct saa716x_dev *saa716x		= saa716x_spi->saa716x;
+	struct saa716x_spi_config *config	= &saa716x->spi_config;
+
+	u8 control = 0;
+
+	if (spi->mode & ~MODEBITS) {
+		dprintk(SAA716x_ERROR, 1, "ERROR: Unsupported MODE bits <%x>",
+			spi->mode & ~MODEBITS);
+
+		return -EINVAL;
+	}
+
+	SAA716x_EPWR(SPI, SPI_CLOCK_COUNTER, config->clk_count);
+
+	control |= SPI_MODE_SELECT; /* SPI Master */
+
+	if (config->LSB_first)
+		control |= SPI_LSB_FIRST_ENABLE;
+
+	if (config->clk_pol)
+		control |= SPI_CLOCK_POLARITY;
+
+	if (config->clk_pha)
+		control |= SPI_CLOCK_PHASE;
+
+	SAA716x_EPWR(SPI, SPI_CONTROL_REG, control);
+
+	return 0;
+}
+
+static void saa716x_spi_cleanup(struct spi_device *spi)
+{
+
+}
+
+static int saa716x_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+	struct spi_master *master		= spi->master;
+	struct saa716x_spi_state *saa716x_spi	= spi_master_get_devdata(master);
+	struct saa716x_dev *saa716x		= saa716x_spi->saa716x;
+	unsigned long flags;
+
+	spin_lock_irqsave(&saa716x->gpio_lock, flags);
+#if 0
+	if (saa716x_spi->run == QUEUE_STOPPED) {
+		spin_unlock_irqrestore(&saa716x_spi->lock, flags);
+		return -ESHUTDOWN;
+	}
+
+	msg->actual_length = 0;
+	msg->status = -EINPROGRESS;
+	msg->state = START_STATE;
+
+	list_add_tail(&msg->queue, &saa716x_spi->queue);
+
+	if (saa716x_spi->run == QUEUE_RUNNING && !saa716x_spi->busy)
+		queue_work(saa716x_spi->workqueue, &saa716x_spi->pump_messages);
+#endif
+	spin_unlock_irqrestore(&saa716x->gpio_lock, flags);
+
+	return 0;
+}
+
+int saa716x_spi_init(struct saa716x_dev *saa716x)
+{
+	struct pci_dev *pdev = saa716x->pdev;
+	struct spi_master *master;
+	struct saa716x_spi_state *saa716x_spi;
+	int ret;
+
+	dprintk(SAA716x_DEBUG, 1, "Initializing SAA%02x I2C Core",
+		saa716x->pdev->device);
+
+	master = spi_alloc_master(&pdev->dev, sizeof (struct saa716x_spi_state));
+	if (master == NULL) {
+		dprintk(SAA716x_ERROR, 1, "ERROR: Cannot allocate SPI Master!");
+		return -ENOMEM;
+	}
+
+	saa716x_spi		= spi_master_get_devdata(master);
+	saa716x_spi->master	= master;
+	saa716x_spi->saa716x	= saa716x;
+	saa716x->saa716x_spi	= saa716x_spi;
+
+	master->bus_num		= pdev->bus->number;
+	master->num_chipselect	= 1; /* TODO! use config */
+	master->cleanup		= saa716x_spi_cleanup;
+	master->setup		= saa716x_spi_setup;
+	master->transfer	= saa716x_spi_transfer;
+
+	ret = spi_register_master(master);
+	if (ret != 0) {
+		dprintk(SAA716x_ERROR, 1, "ERROR: registering SPI Master!");
+		goto err;
+	}
+err:
+	spi_master_put(master);
+	return ret;
+}
+EXPORT_SYMBOL(saa716x_spi_init);
+
+void saa716x_spi_exit(struct saa716x_dev *saa716x)
+{
+	struct saa716x_spi_state *saa716x_spi = saa716x->saa716x_spi;
+
+	spi_unregister_master(saa716x_spi->master);
+	dprintk(SAA716x_DEBUG, 1, "SAA%02x SPI succesfully removed", saa716x->pdev->device);
+}
+EXPORT_SYMBOL(saa716x_spi_exit);
+#endif
+
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_spi.h linux/drivers/media/pci/saa716x/saa716x_spi.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_spi.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_spi.h	2017-08-11 23:31:25.873389225 +0200
@@ -0,0 +1,23 @@
+#ifndef __SAA716x_SPI_H
+#define __SAA716x_SPI_H
+
+struct saa716x_dev;
+
+struct saa716x_spi_config {
+	u8 clk_count;
+	u8 clk_pol:1;
+	u8 clk_pha:1;
+	u8 LSB_first:1;
+};
+
+struct saa716x_spi_state {
+	struct spi_master *master;
+	struct saa716x_dev *saa716x;
+};
+
+extern void saa716x_spi_write(struct saa716x_dev *saa716x, const u8 *data, int length);
+
+extern int saa716x_spi_init(struct saa716x_dev *saa716x);
+extern void saa716x_spi_exit(struct saa716x_dev *saa716x);
+
+#endif /* __SAA716x_SPI_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_spi_reg.h linux/drivers/media/pci/saa716x/saa716x_spi_reg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_spi_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_spi_reg.h	2017-08-11 23:31:25.873389225 +0200
@@ -0,0 +1,27 @@
+#ifndef __SAA716x_SPI_REG_H
+#define __SAA716x_SPI_REG_H
+
+/* -------------- SPI Registers -------------- */
+
+#define SPI_CONTROL_REG			0x000
+#define SPI_SERIAL_INTER_ENABLE		(0x00000001 <<  7)
+#define SPI_LSB_FIRST_ENABLE		(0x00000001 <<  6)
+#define SPI_MODE_SELECT			(0x00000001 <<  5)
+#define SPI_CLOCK_POLARITY		(0x00000001 <<  4)
+#define SPI_CLOCK_PHASE			(0x00000001 <<  3)
+
+#define SPI_STATUS			0x004
+#define SPI_TRANSFER_FLAG		(0x00000001 <<  7)
+#define SPI_WRITE_COLLISSION		(0x00000001 <<  6)
+#define SPI_READ_OVERRUN		(0x00000001 <<  5)
+#define SPI_MODE_FAULT			(0x00000001 <<  4)
+#define SPI_SLAVE_ABORT			(0x00000001 <<  3)
+
+#define SPI_DATA			0x008
+#define SPI_BIDI_DATA			(0x000000ff <<  0)
+
+#define SPI_CLOCK_COUNTER		0x00c
+#define SPI_CLOCK			(0x00000001 <<  0)
+
+
+#endif /* __SAA716x_SPI_REG_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_vip.c linux/drivers/media/pci/saa716x/saa716x_vip.c
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_vip.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_vip.c	2017-08-11 23:31:25.885389158 +0200
@@ -0,0 +1,401 @@
+#include <linux/kernel.h>
+
+#include "saa716x_mod.h"
+
+#include "saa716x_vip_reg.h"
+#include "saa716x_dma_reg.h"
+#include "saa716x_msi_reg.h"
+
+#include "saa716x_priv.h"
+
+
+static const u32 vi_ch[] = {
+	VI0,
+	VI1
+};
+
+static const u32 msi_int_tagack[] = {
+	MSI_INT_TAGACK_VI0_0,
+	MSI_INT_TAGACK_VI0_1,
+	MSI_INT_TAGACK_VI0_2,
+	MSI_INT_TAGACK_VI1_0,
+	MSI_INT_TAGACK_VI1_1,
+	MSI_INT_TAGACK_VI1_2
+};
+
+static const u32 msi_int_avint[] = {
+	MSI_INT_AVINT_VI0,
+	MSI_INT_AVINT_VI1
+};
+
+void saa716x_vipint_disable(struct saa716x_dev *saa716x)
+{
+	SAA716x_EPWR(VI0, INT_ENABLE, 0); /* disable VI 0 IRQ */
+	SAA716x_EPWR(VI1, INT_ENABLE, 0); /* disable VI 1 IRQ */
+	SAA716x_EPWR(VI0, INT_CLR_STATUS, 0x3ff); /* clear IRQ */
+	SAA716x_EPWR(VI1, INT_CLR_STATUS, 0x3ff); /* clear IRQ */
+}
+EXPORT_SYMBOL_GPL(saa716x_vipint_disable);
+
+void saa716x_vip_disable(struct saa716x_dev *saa716x)
+{
+       SAA716x_EPWR(VI0, VIP_POWER_DOWN, VI_PWR_DWN);
+       SAA716x_EPWR(VI1, VIP_POWER_DOWN, VI_PWR_DWN);
+}
+EXPORT_SYMBOL_GPL(saa716x_vip_disable);
+
+int saa716x_vip_get_write_index(struct saa716x_dev *saa716x, int port)
+{
+	u32 buf_mode, val;
+
+	buf_mode = BAM_DMA_BUF_MODE(saa716x->vip[port].dma_channel[0]);
+
+	val = SAA716x_EPRD(BAM, buf_mode);
+	return (val >> 3) & 0x7;
+}
+EXPORT_SYMBOL_GPL(saa716x_vip_get_write_index);
+
+static int saa716x_vip_init_ptables(struct saa716x_dmabuf *dmabuf, int channel,
+				    struct vip_stream_params *stream_params)
+{
+	struct saa716x_dev *saa716x = dmabuf->saa716x;
+	u32 config, i;
+
+	for (i = 0; i < VIP_BUFFERS; i++)
+		BUG_ON((dmabuf[i].mem_ptab_phys == 0));
+
+	config = MMU_DMA_CONFIG(channel); /* DMACONFIGx */
+
+	SAA716x_EPWR(MMU, config, (VIP_BUFFERS - 1));
+
+	if ((stream_params->stream_flags & VIP_INTERLACED) &&
+	    (stream_params->stream_flags & VIP_ODD_FIELD) &&
+	    (stream_params->stream_flags & VIP_EVEN_FIELD)) {
+		/* In interlaced mode the same buffer is written twice, once
+		   the odd field and once the even field */
+		SAA716x_EPWR(MMU, MMU_PTA0_LSB(channel), PTA_LSB(dmabuf[0].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA0_MSB(channel), PTA_MSB(dmabuf[0].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA1_LSB(channel), PTA_LSB(dmabuf[0].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA1_MSB(channel), PTA_MSB(dmabuf[0].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA2_LSB(channel), PTA_LSB(dmabuf[1].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA2_MSB(channel), PTA_MSB(dmabuf[1].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA3_LSB(channel), PTA_LSB(dmabuf[1].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA3_MSB(channel), PTA_MSB(dmabuf[1].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA4_LSB(channel), PTA_LSB(dmabuf[2].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA4_MSB(channel), PTA_MSB(dmabuf[2].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA5_LSB(channel), PTA_LSB(dmabuf[2].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA5_MSB(channel), PTA_MSB(dmabuf[2].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA6_LSB(channel), PTA_LSB(dmabuf[3].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA6_MSB(channel), PTA_MSB(dmabuf[3].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA7_LSB(channel), PTA_LSB(dmabuf[3].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA7_MSB(channel), PTA_MSB(dmabuf[3].mem_ptab_phys)); /* High */
+	} else {
+		SAA716x_EPWR(MMU, MMU_PTA0_LSB(channel), PTA_LSB(dmabuf[0].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA0_MSB(channel), PTA_MSB(dmabuf[0].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA1_LSB(channel), PTA_LSB(dmabuf[1].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA1_MSB(channel), PTA_MSB(dmabuf[1].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA2_LSB(channel), PTA_LSB(dmabuf[2].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA2_MSB(channel), PTA_MSB(dmabuf[2].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA3_LSB(channel), PTA_LSB(dmabuf[3].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA3_MSB(channel), PTA_MSB(dmabuf[3].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA4_LSB(channel), PTA_LSB(dmabuf[4].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA4_MSB(channel), PTA_MSB(dmabuf[4].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA5_LSB(channel), PTA_LSB(dmabuf[5].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA5_MSB(channel), PTA_MSB(dmabuf[5].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA6_LSB(channel), PTA_LSB(dmabuf[6].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA6_MSB(channel), PTA_MSB(dmabuf[6].mem_ptab_phys)); /* High */
+		SAA716x_EPWR(MMU, MMU_PTA7_LSB(channel), PTA_LSB(dmabuf[7].mem_ptab_phys)); /* Low */
+		SAA716x_EPWR(MMU, MMU_PTA7_MSB(channel), PTA_MSB(dmabuf[7].mem_ptab_phys)); /* High */
+	}
+	return 0;
+}
+
+static int saa716x_vip_setparams(struct saa716x_dev *saa716x, int port,
+				 struct vip_stream_params *stream_params)
+{
+	u32 vi_port, buf_mode, mid, val, i;
+	u8 dma_channel;
+	u32 num_pages;
+	u32 start_x, start_line, end_line, num_lines;
+	u32 base_address, base_offset, pitch;
+	u32 vin_format;
+
+	vi_port = vi_ch[port];
+	buf_mode = BAM_DMA_BUF_MODE(saa716x->vip[port].dma_channel[0]);
+	dma_channel = saa716x->vip[port].dma_channel[0];
+
+	/* number of pages needed for a buffer */
+	num_pages = (stream_params->bits / 8 * stream_params->samples
+		     * stream_params->lines) / SAA716x_PAGE_SIZE;
+	/* check if these will fit into one page table */
+	if (num_pages > (SAA716x_PAGE_SIZE / 8))
+		saa716x->vip[port].dual_channel = 1;
+	else
+		saa716x->vip[port].dual_channel = 0;
+
+	/* Reset DMA channel */
+	SAA716x_EPWR(BAM, buf_mode, 0x00000040);
+	saa716x_vip_init_ptables(saa716x->vip[port].dma_buf[0],
+				 saa716x->vip[port].dma_channel[0],
+				 stream_params);
+	if (saa716x->vip[port].dual_channel)
+		saa716x_vip_init_ptables(saa716x->vip[port].dma_buf[1],
+					 saa716x->vip[port].dma_channel[1],
+					 stream_params);
+
+	/* get module ID */
+	mid = SAA716x_EPRD(vi_port, VI_MODULE_ID);
+	if (mid != 0x11A5100) {
+		dprintk(SAA716x_ERROR, 1, "VIP Id<%04x> is not supported", mid);
+		return -1;
+	}
+
+	start_x = stream_params->offset_x;
+	start_line = stream_params->offset_y + 1;
+	end_line = 0;
+	num_lines = stream_params->lines;
+	pitch = stream_params->pitch;
+	base_address = saa716x->vip[port].dma_channel[0] << 21;
+	base_offset = 0;
+	vin_format = 0x00004000;
+
+	if ((stream_params->stream_flags & VIP_INTERLACED) &&
+	    (stream_params->stream_flags & VIP_ODD_FIELD) &&
+	    (stream_params->stream_flags & VIP_EVEN_FIELD)) {
+		num_lines /= 2;
+		pitch *= 2;
+		base_offset = stream_params->pitch;
+	}
+	if (stream_params->stream_flags & VIP_HD) {
+		if (stream_params->stream_flags & VIP_INTERLACED) {
+			vin_format |= 0x01000000;
+		} else {
+			/* suppress the windower break message */
+			vin_format |= 0x01000200;
+		}
+	}
+	if (stream_params->stream_flags & VIP_NO_SCALER) {
+		vin_format |= 0x00000400;
+	}
+
+	end_line = stream_params->offset_y + num_lines;
+
+	/* set device to normal operation */
+	SAA716x_EPWR(vi_port, VIP_POWER_DOWN, 0);
+	/* disable ANC bit detection */
+	SAA716x_EPWR(vi_port, ANC_DID_FIELD0, 0);
+	SAA716x_EPWR(vi_port, ANC_DID_FIELD1, 0);
+	/* set line threshold to 0 (interrupt is disabled anyway)*/
+	SAA716x_EPWR(vi_port, VI_LINE_THRESH, 0);
+
+	vin_format |= 2;
+	SAA716x_EPWR(vi_port, VIN_FORMAT, vin_format);
+
+	/* disable dithering */
+	SAA716x_EPWR(vi_port, PRE_DIT_CTRL, 0);
+	SAA716x_EPWR(vi_port, POST_DIT_CTRL, 0);
+	/* set alpha value */
+	SAA716x_EPWR(vi_port, CSM_CKEY, 0);
+
+	SAA716x_EPWR(vi_port, WIN_XYSTART, (start_x << 16) + start_line);
+	SAA716x_EPWR(vi_port, WIN_XYEND,
+		     ((start_x + stream_params->samples - 1) << 16) + end_line);
+
+	/* enable cropping to assure that VIP does not exceed buffer boundaries */
+	SAA716x_EPWR(vi_port, PSU_WINDOW,
+		     (stream_params->samples << 16) + num_lines);
+	/* set packet YUY2 output format */
+	SAA716x_EPWR(vi_port, PSU_FORMAT, 0x800000A1);
+
+	SAA716x_EPWR(vi_port, PSU_BASE1, base_address);
+	SAA716x_EPWR(vi_port, PSU_PITCH1, pitch);
+	SAA716x_EPWR(vi_port, PSU_PITCH2, 0);
+	SAA716x_EPWR(vi_port, PSU_BASE2, 0);
+	SAA716x_EPWR(vi_port, PSU_BASE3, 0);
+	SAA716x_EPWR(vi_port, PSU_BASE4, base_address + base_offset);
+	SAA716x_EPWR(vi_port, PSU_BASE5, 0);
+	SAA716x_EPWR(vi_port, PSU_BASE6, 0);
+
+	/* monitor BAM reset */
+	i = 0;
+	val = SAA716x_EPRD(BAM, buf_mode);
+	while (val && (i < 100)) {
+		msleep(30);
+		val = SAA716x_EPRD(BAM, buf_mode);
+		i++;
+	}
+	if (val) {
+		dprintk(SAA716x_ERROR, 1, "Error: BAM VIP Reset failed!");
+		return -EIO;
+	}
+
+	/* set buffer count */
+	SAA716x_EPWR(BAM, buf_mode, VIP_BUFFERS - 1);
+	/* initialize all available address offsets to 0 */
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_0(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_1(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_2(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_3(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_4(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_5(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_6(dma_channel), 0x0);
+	SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_7(dma_channel), 0x0);
+
+	if (saa716x->vip[port].dual_channel) {
+		buf_mode = BAM_DMA_BUF_MODE(saa716x->vip[port].dma_channel[1]);
+		dma_channel = saa716x->vip[port].dma_channel[1];
+
+		/* set buffer count */
+		SAA716x_EPWR(BAM, buf_mode, VIP_BUFFERS - 1);
+		/* initialize all available address offsets to 0 */
+		SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_0(dma_channel), 0x0);
+		SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_1(dma_channel), 0x0);
+		SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_2(dma_channel), 0x0);
+		SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_3(dma_channel), 0x0);
+		SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_4(dma_channel), 0x0);
+		SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_5(dma_channel), 0x0);
+		SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_6(dma_channel), 0x0);
+		SAA716x_EPWR(BAM, BAM_ADDR_OFFSET_7(dma_channel), 0x0);
+	}
+
+	return 0;
+}
+
+int saa716x_vip_start(struct saa716x_dev *saa716x, int port, int one_shot,
+		      struct vip_stream_params *stream_params)
+{
+	u32 vi_port;
+	u32 config1;
+	u32 config2;
+	u32 val;
+	u32 i;
+
+	vi_port = vi_ch[port];
+	config1 = MMU_DMA_CONFIG(saa716x->vip[port].dma_channel[0]);
+	config2 = MMU_DMA_CONFIG(saa716x->vip[port].dma_channel[1]);
+
+	if (saa716x_vip_setparams(saa716x, port, stream_params) != 0) {
+		return -EIO;
+	}
+
+	val = SAA716x_EPRD(MMU, config1);
+	SAA716x_EPWR(MMU, config1, val & ~0x40);
+	SAA716x_EPWR(MMU, config1, val | 0x40);
+	if (saa716x->vip[port].dual_channel) {
+		val = SAA716x_EPRD(MMU, config2);
+		SAA716x_EPWR(MMU, config2, val & ~0x40);
+		SAA716x_EPWR(MMU, config2, val | 0x40);
+	}
+
+	SAA716x_EPWR(vi_port, INT_ENABLE, 0x33F);
+
+	i = 0;
+	while (i < 500) {
+		val = SAA716x_EPRD(MMU, config1);
+		if (saa716x->vip[port].dual_channel)
+			val &= SAA716x_EPRD(MMU, config2);
+		if (val & 0x80)
+			break;
+		msleep(10);
+		i++;
+	}
+
+	if (!(val & 0x80)) {
+		dprintk(SAA716x_ERROR, 1, "Error: PTE pre-fetch failed!");
+		return -EIO;
+	}
+
+	/* enable video capture path */
+	val = SAA716x_EPRD(vi_port, VI_MODE);
+	val &= ~(VID_CFEN | VID_FSEQ | VID_OSM);
+
+	if ((stream_params->stream_flags & VIP_INTERLACED) &&
+	    (stream_params->stream_flags & VIP_ODD_FIELD) &&
+	    (stream_params->stream_flags & VIP_EVEN_FIELD)) {
+		val |= VID_CFEN_BOTH; /* capture both fields */
+		val |= VID_FSEQ; /* start capture with odd field */
+	} else {
+		val |= VID_CFEN_BOTH; /* capture both fields */
+	}
+
+	if (one_shot)
+		val |= VID_OSM; /* stop capture after receiving one frame */
+
+	saa716x_set_clk_external(saa716x, saa716x->vip[port].dma_channel[0]);
+
+	SAA716x_EPWR(vi_port, VI_MODE, val);
+
+	SAA716x_EPWR(MSI, MSI_INT_ENA_SET_L, msi_int_tagack[port]);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_vip_start);
+
+int saa716x_vip_stop(struct saa716x_dev *saa716x, int port)
+{
+	u32 val;
+
+	SAA716x_EPWR(MSI, MSI_INT_ENA_CLR_L, msi_int_tagack[port]);
+
+	/* disable capture */
+	val = SAA716x_EPRD(vi_ch[port], VI_MODE);
+	val &= ~VID_CFEN;
+	SAA716x_EPWR(vi_ch[port], VI_MODE, val);
+
+	saa716x_set_clk_internal(saa716x, saa716x->vip[port].dma_channel[0]);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_vip_stop);
+
+int saa716x_vip_init(struct saa716x_dev *saa716x, int port,
+		     void (*worker)(unsigned long))
+{
+	int n;
+	int i;
+	int ret;
+
+	/* reset VI */
+	SAA716x_EPWR(vi_ch[port], VI_MODE, SOFT_RESET);
+
+	for (n = 0; n < 2; n++)
+	{
+		saa716x->vip[port].dma_channel[n] = port * 3 + n;
+		for (i = 0; i < VIP_BUFFERS; i++)
+		{
+			ret = saa716x_dmabuf_alloc(
+					saa716x,
+					&saa716x->vip[port].dma_buf[n][i],
+					512 * SAA716x_PAGE_SIZE);
+			if (ret < 0) {
+				return ret;
+			}
+		}
+	}
+	saa716x->vip[port].saa716x = saa716x;
+	tasklet_init(&saa716x->vip[port].tasklet, worker,
+		     (unsigned long)&saa716x->vip[port]);
+	saa716x->vip[port].read_index = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_vip_init);
+
+int saa716x_vip_exit(struct saa716x_dev *saa716x, int port)
+{
+	int n;
+	int i;
+
+	tasklet_kill(&saa716x->vip[port].tasklet);
+	for (n = 0; n < 2; n++)
+	{
+		for (i = 0; i < VIP_BUFFERS; i++)
+		{
+			saa716x_dmabuf_free(
+				saa716x, &saa716x->vip[port].dma_buf[n][i]);
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa716x_vip_exit);
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_vip.h linux/drivers/media/pci/saa716x/saa716x_vip.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_vip.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_vip.h	2017-08-11 23:31:25.885389158 +0200
@@ -0,0 +1,58 @@
+#ifndef __SAA716x_VIP_H
+#define __SAA716x_VIP_H
+
+#include "saa716x_dma.h"
+
+#define VIP_BUFFERS	8
+
+/*
+ * Stream port flags
+ */
+enum vip_stream_flags {
+	VIP_ODD_FIELD		= 0x0001,
+	VIP_EVEN_FIELD		= 0x0002,
+	VIP_INTERLACED		= 0x0004,
+	VIP_HD			= 0x0010,
+	VIP_NO_SCALER		= 0x0100
+};
+
+/*
+ * Stream port parameters
+ * bits: Bits per sample
+ * samples: samples perline
+ * lines: number of lines
+ * pitch: stream pitch in bytes
+ * offset_x: offset to first valid pixel
+ * offset_y: offset to first valid line
+ */
+struct vip_stream_params {
+	u32			bits;
+	u32			samples;
+	u32			lines;
+	s32			pitch;
+	u32			offset_x;
+	u32			offset_y;
+	enum vip_stream_flags	stream_flags;
+};
+
+struct saa716x_vip_stream_port {
+	u8			dual_channel;
+	u8			read_index;
+	u8			dma_channel[2];
+	struct saa716x_dmabuf	dma_buf[2][VIP_BUFFERS];
+	struct saa716x_dev	*saa716x;
+	struct tasklet_struct	tasklet;
+};
+
+extern void saa716x_vipint_disable(struct saa716x_dev *saa716x);
+extern void saa716x_vip_disable(struct saa716x_dev *saa716x);
+extern int saa716x_vip_get_write_index(struct saa716x_dev *saa716x, int port);
+extern int saa716x_vip_start(struct saa716x_dev *saa716x, int port,
+			     int one_shot,
+			     struct vip_stream_params *stream_params);
+extern int saa716x_vip_stop(struct saa716x_dev *saa716x, int port);
+extern int saa716x_vip_init(struct saa716x_dev *saa716x, int port,
+			    void (*worker)(unsigned long));
+extern int saa716x_vip_exit(struct saa716x_dev *saa716x, int port);
+
+#endif /* __SAA716x_VIP_H */
diff -ruN linux.vanilla/drivers/media/pci/saa716x/saa716x_vip_reg.h linux/drivers/media/pci/saa716x/saa716x_vip_reg.h
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_vip_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_vip_reg.h	2017-08-11 23:31:25.885389158 +0200
@@ -0,0 +1,141 @@
+#ifndef __SAA716x_VIP_REG_H
+#define __SAA716x_VIP_REG_H
+
+/* -------------- VIP Registers -------------- */
+
+#define VI_MODE				0x000
+#define VID_CFEN			(0x00000003 << 30)
+#define VID_CFEN_ODD			(0x00000001 << 30)
+#define VID_CFEN_EVEN			(0x00000002 << 30)
+#define VID_CFEN_BOTH			(0x00000003 << 30)
+#define VID_OSM				(0x00000001 << 29)
+#define VID_FSEQ			(0x00000001 << 28)
+#define AUX_CFEN			(0x00000003 << 26)
+#define AUX_OSM				(0x00000001 << 25)
+#define AUX_FSEQ			(0x00000001 << 24)
+#define AUX_ANC_DATA			(0x00000003 << 22)
+#define AUX_ANC_RAW			(0x00000001 << 21)
+#define RST_ON_ERR			(0x00000001 << 17)
+#define SOFT_RESET			(0x00000001 << 16)
+#define IFF_CLAMP			(0x00000001 << 14)
+#define IFF_MODE			(0x00000003 << 12)
+#define DFF_CLAMP			(0x00000001 << 10)
+#define DFF_MODE			(0x00000003 <<  8)
+#define HSP_CLAMP			(0x00000001 <<  3)
+#define HSP_RGB				(0x00000001 <<  2)
+#define HSP_MODE			(0x00000003 <<  0)
+
+#define ANC_DID_FIELD0			0x020
+#define VI_ID_MASK_0			(0x000000ff <<  8)
+#define VI_DATA_ID_0			(0x000000ff <<  0)
+
+#define ANC_DID_FIELD1			0x024
+#define VI_ID_MASK_1			(0x000000ff <<  8)
+#define VI_DATA_ID_1			(0x000000ff <<  0)
+
+#define VI_LINE_THRESH			0x040
+#define VI_LCTHR			(0x000007ff <<  0)
+
+#define VIN_FORMAT			0x100
+#define VI_VSRA				(0x00000003 << 30)
+#define VI_SYNCHD			(0x00000001 << 25)
+#define VI_DUAL_STREAM			(0x00000001 << 24)
+#define VI_NHDAUX			(0x00000001 << 20)
+#define VI_NPAR				(0x00000001 << 19)
+#define VI_VSEL				(0x00000003 << 14)
+#define VI_TWOS				(0x00000001 << 13)
+#define VI_TPG				(0x00000001 << 12)
+#define VI_FREF				(0x00000001 << 10)
+#define VI_FTGL				(0x00000001 <<  9)
+#define VI_SF				(0x00000001 <<  3)
+#define VI_FZERO			(0x00000001 <<  2)
+#define VI_REVS				(0x00000001 <<  1)
+#define VI_REHS				(0x00000001 <<  0)
+
+#define VIN_TESTPGEN			0x104
+
+#define WIN_XYSTART			0x140
+#define WIN_XYEND			0x144
+
+#define PRE_DIT_CTRL			0x160
+#define POST_DIT_CTRL			0x164
+
+#define AUX_XYSTART			0x180
+#define AUX_XYEND			0x184
+
+#define CSM_CKEY			0x284
+
+#define PSU_FORMAT			0x300
+#define PSU_WINDOW			0x304
+#define PSU_BASE1			0x340
+#define PSU_PITCH1			0x344
+#define PSU_BASE2			0x348
+#define PSU_PITCH2			0x34c
+#define PSU_BASE3			0x350
+#define PSU_BASE4			0x354
+#define PSU_BASE5			0x358
+#define PSU_BASE6			0x35c
+
+#define AUX_FORMAT			0x380
+#define AUX_BASE			0x390
+#define AUX_PITCH			0x394
+
+#define INT_STATUS			0xfe0
+#define VI_STAT_FID_AUX			(0x00000001 << 31)
+#define VI_STAT_FID_VID			(0x00000001 << 30)
+#define VI_STAT_FID_VPI			(0x00000001 << 29)
+#define VI_STAT_LINE_COUNT		(0x00000fff << 16)
+#define VI_STAT_AUX_OVRFLW		(0x00000001 <<  9)
+#define VI_STAT_VID_OVRFLW		(0x00000001 <<  8)
+#define VI_STAT_WIN_SEQBRK		(0x00000001 <<  7)
+#define VI_STAT_FID_SEQBRK		(0x00000001 <<  6)
+#define VI_STAT_LINE_THRESH		(0x00000001 <<  5)
+#define VI_STAT_AUX_WRAP		(0x00000001 <<  4)
+#define VI_STAT_AUX_START_IN		(0x00000001 <<  3)
+#define VI_STAT_AUX_END_OUT		(0x00000001 <<  2)
+#define VI_STAT_VID_START_IN		(0x00000001 <<  1)
+#define VI_STAT_VID_END_OUT		(0x00000001 <<  0)
+
+#define INT_ENABLE			0xfe4
+#define VI_ENABLE_AUX_OVRFLW		(0x00000001 <<  9)
+#define VI_ENABLE_VID_OVRFLW		(0x00000001 <<  8)
+#define VI_ENABLE_WIN_SEQBRK		(0x00000001 <<  7)
+#define VI_ENABLE_FID_SEQBRK		(0x00000001 <<  6)
+#define VI_ENABLE_LINE_THRESH		(0x00000001 <<  5)
+#define VI_ENABLE_AUX_WRAP		(0x00000001 <<  4)
+#define VI_ENABLE_AUX_START_IN		(0x00000001 <<  3)
+#define VI_ENABLE_AUX_END_OUT		(0x00000001 <<  2)
+#define VI_ENABLE_VID_START_IN		(0x00000001 <<  1)
+#define VI_ENABLE_VID_END_OUT		(0x00000001 <<  0)
+
+#define INT_CLR_STATUS			0xfe8
+#define VI_CLR_STATUS_AUX_OVRFLW	(0x00000001 <<  9)
+#define VI_CLR_STATUS_VID_OVRFLW	(0x00000001 <<  8)
+#define VI_CLR_STATUS_WIN_SEQBRK	(0x00000001 <<  7)
+#define VI_CLR_STATUS_FID_SEQBRK	(0x00000001 <<  6)
+#define VI_CLR_STATUS_LINE_THRESH	(0x00000001 <<  5)
+#define VI_CLR_STATUS_AUX_WRAP		(0x00000001 <<  4)
+#define VI_CLR_STATUS_AUX_START_IN	(0x00000001 <<  3)
+#define VI_CLR_STATUS_AUX_END_OUT	(0x00000001 <<  2)
+#define VI_CLR_STATUS_VID_START_IN	(0x00000001 <<  1)
+#define VI_CLR_STATUS_VID_END_OUT	(0x00000001 <<  0)
+
+#define INT_SET_STATUS			0xfec
+#define VI_SET_STATUS_AUX_OVRFLW	(0x00000001 <<  9)
+#define VI_SET_STATUS_VID_OVRFLW	(0x00000001 <<  8)
+#define VI_SET_STATUS_WIN_SEQBRK	(0x00000001 <<  7)
+#define VI_SET_STATUS_FID_SEQBRK	(0x00000001 <<  6)
+#define VI_SET_STATUS_LINE_THRESH	(0x00000001 <<  5)
+#define VI_SET_STATUS_AUX_WRAP		(0x00000001 <<  4)
+#define VI_SET_STATUS_AUX_START_IN	(0x00000001 <<  3)
+#define VI_SET_STATUS_AUX_END_OUT	(0x00000001 <<  2)
+#define VI_SET_STATUS_VID_START_IN	(0x00000001 <<  1)
+#define VI_SET_STATUS_VID_END_OUT	(0x00000001 <<  0)
+
+#define VIP_POWER_DOWN			0xff4
+#define VI_PWR_DWN			(0x00000001 << 31)
+
+#define VI_MODULE_ID			0xffc
+
+
+#endif /* __SAA716x_VIP_REG_H */
--- linux/include/uapi/linux/dvb/osd.h	2010-07-03 23:22:13.000000000 +0200
+++ experimental/v4l-dvb-saa716x/linux/include/linux/dvb/osd.h	2011-05-03 02:36:52.000000000 +0200
@@ -141,4 +141,20 @@ typedef struct osd_cap_s {
 #define OSD_SEND_CMD            _IOW('o', 160, osd_cmd_t)
 #define OSD_GET_CAPABILITY      _IOR('o', 161, osd_cap_t)
 
+typedef struct osd_raw_cmd_s {
+	const void __user *cmd_data;
+	int cmd_len;
+	void __user *result_data;
+	int result_len;
+} osd_raw_cmd_t;
+
+typedef struct osd_raw_data_s {
+	const void __user *data_buffer;
+	int data_length;
+	int data_handle;
+} osd_raw_data_t;
+
+#define OSD_RAW_CMD            _IOWR('o', 162, osd_raw_cmd_t)
+#define OSD_RAW_DATA           _IOWR('o', 163, osd_raw_data_t)
+
 #endif
--- linux/drivers/media/pci/Kconfig.old	2012-08-22 05:45:23.000000000 +0200
+++ linux/drivers/media/pci/Kconfig	2012-11-19 04:35:28.000000000 +0100
@@ -44,4 +44,6 @@ source "drivers/media/pci/ngene/Kconfig"
 source "drivers/media/pci/ddbridge/Kconfig"
 endif
 
+source "drivers/media/pci/saa716x/Kconfig"
+
 endif #MEDIA_PCI_SUPPORT
--- linux/drivers/media/pci/Makefile.orig	2014-11-06 22:39:16.154804351 +0100
+++ linux/drivers/media/pci/Makefile	2014-11-06 22:42:14.562808291 +0100
@@ -11,6 +11,7 @@ obj-y        +=	ttpci/		\
 		mantis/		\
 		ngene/		\
 		ddbridge/	\
+		saa716x/	\
 		saa7146/	\
 		smipcie/
 
#
# Korrekturen am Patch selbst
#
--- linux.vanilla/drivers/media/pci/saa716x/Makefile	2015-08-08 11:23:50.892996241 +0100
+++ linux/drivers/media/pci/saa716x/Makefile	2015-08-08 11:23:41.713055915 +0100
@@ -24,4 +24,4 @@
 obj-$(CONFIG_DVB_SAA716X_HYBRID)  += saa716x_hybrid.o
 obj-$(CONFIG_DVB_SAA716X_FF)	  += saa716x_ff.o
 
-EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/dvb-core -Idrivers/media/dvb-frontends/
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_main.c	2013-01-11 11:45:40.744703568 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_ff_main.c	2013-01-11 11:45:59.364586452 +0100
@@ -3,6 +3,7 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/mutex.h>
+#include <linux/version.h>
 
 #include <asm/io.h>
 #include <asm/pgtable.h>
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_ff_ir.c	2013-01-11 11:46:31.104386754 +0100
+++ linux/drivers/media/pci/saa716x/saa716x_ff_ir.c	2013-01-11 11:46:55.874230860 +0100
@@ -22,6 +22,7 @@
 
 #include <linux/types.h>
 #include <linux/input.h>
+#include <linux/version.h>
 
 #include "saa716x_spi.h"
 #include "saa716x_priv.h"
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_pci.c	2017-08-12 11:39:01.000000000 +0200
+++ linux/drivers/media/pci/saa716x/saa716x_pci.c	2017-08-12 12:38:14.420953555 +0200
@@ -1,6 +1,8 @@
+#ifndef CONFIG_X86_64
 #include <asm/io.h>
 #include <asm/pgtable.h>
 #include <asm/page.h>
+#endif
 #include <linux/kmod.h>
 #include <linux/vmalloc.h>
 #include <linux/init.h>
@@ -39,12 +41,9 @@ static int saa716x_enable_msi(struct saa
 static int saa716x_enable_msix(struct saa716x_dev *saa716x)
 {
 	struct pci_dev *pdev = saa716x->pdev;
-	int i, ret = 0;
-
-	for (i = 0; i < SAA716x_MSI_MAX_VECTORS; i++)
-		saa716x->msix_entries[i].entry = i;
+	int ret = 0;
 
-	ret = pci_enable_msix(pdev, saa716x->msix_entries, SAA716x_MSI_MAX_VECTORS);
+	ret = pci_alloc_irq_vectors(pdev, SAA716x_MSI_MAX_VECTORS, SAA716x_MSI_MAX_VECTORS, PCI_IRQ_MSIX);
 	if (ret < 0)
 		dprintk(SAA716x_ERROR, 1, "MSI-X request failed <%d>", ret);
 	if (ret > 0)
@@ -88,7 +85,7 @@ static int saa716x_request_irq(struct sa
 
 	if (saa716x->int_type == MODE_MSI_X) {
 		for (i = 0; SAA716x_MSI_MAX_VECTORS; i++) {
-			ret = request_irq(saa716x->msix_entries[i].vector,
+			ret = request_irq(pci_irq_vector(pdev, i),
 					  saa716x->saa716x_msix_handler[i].handler,
 					  IRQF_SHARED,
 					  saa716x->saa716x_msix_handler[i].desc,
@@ -125,7 +122,7 @@ static void saa716x_free_irq(struct saa7
 	if (saa716x->int_type == MODE_MSI_X) {
 
 		for (i = 0; i < SAA716x_MSI_MAX_VECTORS; i++) {
-			vector = saa716x->msix_entries[i].vector;
+			vector = pci_irq_vector(pdev, i);
 			free_irq(vector, saa716x);
 		}
 
@@ -141,7 +138,7 @@ static void saa716x_free_irq(struct saa7
 int saa716x_pci_init(struct saa716x_dev *saa716x)
 {
 	struct pci_dev *pdev = saa716x->pdev;
-	int err = 0, ret = -ENODEV, i, use_dac, pm_cap;
+	int err = 0, ret = -ENODEV, use_dac, pm_cap;
 	u32 msi_cap;
 	u8 revision;
 
@@ -197,9 +194,6 @@ int saa716x_pci_init(struct saa716x_dev
 		goto fail2;
 	}
 
-	for (i = 0; i < SAA716x_MSI_MAX_VECTORS; i++)
-		saa716x->msix_entries[i].entry = i;
-
 	err = saa716x_request_irq(saa716x);
 	if (err < 0) {
 		dprintk(SAA716x_ERROR, 1, "SAA716x IRQ registration failed, err=%d", err);
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_priv.h	2017-08-12 11:39:01.000000000 +0200
+++ linux/drivers/media/pci/saa716x/saa716x_priv.h	2017-08-12 15:08:18.729941656 +0200
@@ -134,7 +134,6 @@ struct saa716x_dev {
 #define MODE_MSI_X	2
 	u8				int_type;
 
-	struct msix_entry		msix_entries[SAA716x_MSI_MAX_VECTORS];
 	struct saa716x_msix_entry	saa716x_msix_handler[56];
 	u8				handlers; /* no. of active handlers */
 
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_priv.h	2017-08-12 11:39:01.000000000 +0200
+++ linux/drivers/media/pci/saa716x/saa716x_priv.h	2017-08-12 15:08:18.729941656 +0200
@@ -36,13 +36,13 @@
 #define dprintk(__x, __y, __fmt, __arg...) do {								\
 	if (__y) {											\
 		if	((SAA716x_VERBOSE > SAA716x_ERROR) && (SAA716x_VERBOSE > __x))			\
-			printk(KERN_ERR "%s (%d): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\
+			printk(KERN_ERR "%s (%i): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\
 		else if	((SAA716x_VERBOSE > SAA716x_NOTICE) && (SAA716x_VERBOSE > __x))			\
-			printk(KERN_NOTICE "%s (%d): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\
+			printk(KERN_NOTICE "%s (%i): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\
 		else if ((SAA716x_VERBOSE > SAA716x_INFO) && (SAA716x_VERBOSE > __x))			\
-			printk(KERN_INFO "%s (%d): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\
+			printk(KERN_INFO "%s (%i): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\
 		else if ((SAA716x_VERBOSE > SAA716x_DEBUG) && (SAA716x_VERBOSE > __x))			\
-			printk(KERN_DEBUG "%s (%d): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\
+			printk(KERN_DEBUG "%s (%i): " __fmt "\n" , __func__ , SAA716x_DEV , ##__arg);	\
 	} else {											\
 		if (SAA716x_VERBOSE > __x)								\
 			printk(__fmt , ##__arg);							\
--- linux.vanilla/drivers/media/pci/saa716x/saa716x_rom.c	2017-08-12 15:02:05.053690801 +0200
+++ linux/drivers/media/pci/saa716x/saa716x_rom.c	2017-08-12 15:49:19.991838792 +0200
@@ -113,7 +113,7 @@ static int saa716x_eeprom_header(struct
 	memcpy(rom_header, &buf[*offset], sizeof (struct saa716x_romhdr));
 	if (rom_header->header_size != sizeof (struct saa716x_romhdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%li bytes, Expected=%i",
 			sizeof (struct saa716x_romhdr),
 			rom_header->header_size);
 
@@ -237,7 +237,7 @@ static int saa716x_decoder_info(struct s
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_decoder_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_decoder_hdr));
 
@@ -268,7 +268,7 @@ static int saa716x_gpio_info(struct saa7
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_gpio_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_gpio_hdr));
 
@@ -305,7 +305,7 @@ static int saa716x_video_decoder_info(st
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_video_decoder_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_video_decoder_hdr));
 
@@ -386,7 +386,7 @@ static int saa716x_audio_decoder_info(st
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_audio_decoder_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_audio_decoder_hdr));
 
@@ -417,7 +417,7 @@ static int saa716x_event_source_info(str
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_evsrc_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_evsrc_hdr));
 
@@ -448,7 +448,7 @@ static int saa716x_crossbar_info(struct
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_xbar_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_xbar_hdr));
 
@@ -486,7 +486,7 @@ static int saa716x_tuner_info(struct saa
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_tuner_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_tuner_hdr));
 
@@ -516,7 +516,7 @@ static int saa716x_pll_info(struct saa71
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_pll_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_pll_hdr));
 
@@ -546,7 +546,7 @@ static int saa716x_channel_decoder_info(
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_channel_decoder_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_channel_decoder_hdr));
 
@@ -576,7 +576,7 @@ static int saa716x_encoder_info(struct s
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_encoder_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_encoder_hdr));
 
@@ -606,7 +606,7 @@ static int saa716x_ir_info(struct saa716
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_ir_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_ir_hdr));
 
@@ -637,7 +637,7 @@ static int saa716x_eeprom_info(struct sa
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_eeprom_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_eeprom_hdr));
 
@@ -668,7 +668,7 @@ static int saa716x_filter_info(struct sa
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_filter_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_filter_hdr));
 
@@ -699,7 +699,7 @@ static int saa716x_streamdev_info(struct
 	saa716x_descriptor_dbg(saa716x, buf, offset, header.size, header.ext_data);
 	if (header.size != sizeof (struct saa716x_streamdev_hdr)) {
 		dprintk(SAA716x_ERROR, 1,
-			"ERROR: Header size mismatch! Read size=%d bytes, Expected=%d",
+			"ERROR: Header size mismatch! Read size=%i bytes, Expected=%li",
 			header.size,
 			sizeof (struct saa716x_streamdev_hdr));
 
@@ -789,7 +789,7 @@ static int saa716x_device_info(struct sa
 
 	memcpy(device, &buf[*offset], sizeof (struct saa716x_devinfo));
 	if (device->struct_size != sizeof (struct saa716x_devinfo)) {
-		dprintk(SAA716x_ERROR, 1, "ERROR: Device size mismatch! Read=%d bytes, expected=%d bytes",
+		dprintk(SAA716x_ERROR, 1, "ERROR: Device size mismatch! Read=%i bytes, Expected=%li bytes",
 		device->struct_size,
 		sizeof (struct saa716x_devinfo));
 
