Mailing List archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[linux-dvb] [PATCH][2.6] Convert tda1004x to kernel i2c, support kernel i2c forbudget cards



Hello,

2.6.6 currently still has the "errno-problem", so I decided to go one step ahead and prepare a patch that contains the following:

- remove 2.4 cruft from saa7146 i2c code
- make dvb frontend code independend of i2c
- add new register/unregister functions for kernel i2c frontend drivers
- convert Philips tda1004x frontend driver to kernel i2c, use firmware_class for firmware uploading
- documentation and Kconfig update

I'd like to ask all tda1004x budget card users to take a clean 2.6.6 kernel, apply this patch, compile the drivers as usual (the option to specify the firmware file is gone now, because the hotplug system is used now or firmware needs to be uploaded manually) and follow the instructions in Documentation/dvb/firmware.txt

Thanks!
Michael.
- [DVB] remove 2.4 cruft from saa7146 i2c code
- [DVB] make dvb frontend code independend of i2c
- [DVB] add new register/unregister functions for kernel i2c frontend drivers
- [DVB] convert Philips tda1004x frontend driver to kernel i2c, use firmware_class support via sysfs for firmware upload
- [DVB] add kernel i2c registration routines to budget-core (currently only budget cards have the tda1004x)
- [DVB] documentation and Kconfig update
diff -urawB xx-linux-2.6.6/Documentation/dvb/firmware.txt linux-2.6.6/Documentation/dvb/firmware.txt
--- xx-linux-2.6.6/Documentation/dvb/firmware.txt	2004-04-05 12:41:03.000000000 +0200
+++ linux-2.6.6/Documentation/dvb/firmware.txt	2004-05-14 11:11:38.000000000 +0200
@@ -12,19 +12,45 @@
 to load their firmwares, so here's just a short list of the
 current state:
 
-- dvb-ttpci: driver uses firmware hotplug interface
+Drivers using the firmware hotplug interface:
+- dvb-ttpci
+- tda1004x:
+
+Proprietary solutions which need to be converted:
 - ttusb-budget: firmware is compiled in (dvb-ttusb-dspbootcode.h)
 - sp887x: firmware is compiled in (sp887x_firm.h)
 - alps_tdlb7: firmware is loaded from path specified by
 		"mcfile" module parameter; the binary must be
 		extracted from the Windows driver (Sc_main.mc).
-- tda1004x: firmware is loaded from path specified in
-		DVB_TDA1004X_FIRMWARE_FILE kernel config
-		variable (default /usr/lib/hotplug/firmware/tda1004x.bin); the
-		firmware binary must be extracted from the windows
-		driver
 - ttusb-dec: see "ttusb-dec.txt" for details
 
+0) Getting a usable firmware file 
+
+- For the dvb-ttpci driver/av7110 card you can download the firmware files from
+http://linuxtv.org/download/dvb/
+
+Please note that in case of the dvb-ttpci driver this is *not* the "Root"
+file you probably know from the 2.4 DVB releases driver.
+
+The ttpci-firmware utility from linuxtv.org CVS can be used to
+convert Dpram and Root files into a usable firmware image.
+See dvb-kerrnel/scripts/ in http://linuxtv.org/cvs/.
+
+> wget http://www.linuxtv.org/download/dvb/dvb-ttpci-01.fw
+gets you the version 01 of the firmware fot the ttpci driver.
+
+- The tda1004x driver needs a copy of the DLL "ttlcdacc.dll" from the Haupauge or Technotrend
+windows driver. Currently the DLL from v2.15a of the technotrend driver is supported. Other versions can
+added reasonably painlessly.
+
+Windows driver URL: http://www.technotrend.de/
+
+> wget http://www.technotrend.de/new/215/TTweb_215a_budget_20_05_2003.zip
+> unzip -j TTweb_215a_budget_20_05_2003.zip Software/Oem/PCI/App/ttlcdacc.dll
+
+Rename "ttlcdacc.dll" to "tda1004x.bin" -- that's currently the default name
+for the firmware file.
+
 1) Automatic firmware loading
 
 You need to install recent hotplug scripts if your distribution did not do it
@@ -44,8 +70,23 @@
   driver/firmware internal API changes (so users are free to install the
   latest firmware compatible with the driver).
 
+Currently the drivers mentionend above support firmware upload through the
+hotplug manager. If you have such a card, a simple "modprobe" of the driver
+will take care of everything, ie.
+
+> modprobe dvb-ttpci
+or
+> modprobe tda1004x
+
+If you have the hotplug firmware scripts installed, both drivers will ask the hotplug
+daemon for their default firmware. If the scripts are there, but the firmware cannot
+be found, an error message will be printed immediately. Make sure that the firmware
+are in a path where the hotplug manager can find them.
+
+Please note that the default firmware name of the tda1004x doesn't follow the
+naming conventions stated above. It's still called "tda1004x.bin".
+
 2) Manually loading the firmware into a driver
-   (currently only the dvb-ttpci / av7110 driver supports this)
    
 Step a) Mount sysfs-filesystem.
 
@@ -73,29 +114,17 @@
 
 > echo "180" > /sys/class/firmware/timeout
 
-Step c) Getting a usable firmware file for the dvb-ttpci driver/av7110 card.
-
-You can download the firmware files from
-http://linuxtv.org/download/dvb/
-
-Please note that in case of the dvb-ttpci driver this is *not* the "Root"
-file you probably know from the 2.4 DVB releases driver.
-
-The ttpci-firmware utility from linuxtv.org CVS can be used to
-convert Dpram and Root files into a usable firmware image.
-See dvb-kerrnel/scripts/ in http://linuxtv.org/cvs/.
-
-> wget http://www.linuxtv.org/download/dvb/dvb-ttpci-01.fw
-gets you the version 01 of the firmware fot the ttpci driver.
-
-Step d) Loading the dvb-ttpci driver and loading the firmware
+Step c) Loading the driver and uploading the firmware manually
 
 "modprobe" will take care that every needed module will be loaded
-automatically (except the frontend driver)
+automatically 
 
 > modprobe dvb-ttpci
+or
+> modprobe tda1004x
 
-The "modprobe" process will hang until
+If you don't have the hotplug subsystem running, the "modprobe" process will
+now hang until
 a) you upload the firmware or
 b) the timeout occurs.
 
@@ -107,14 +136,24 @@
 drwxr-xr-x    2 root     root            0 Jul 29 11:00 0000:03:05.0
 -rw-r--r--    1 root     root            0 Jul 29 10:41 timeout
 
-"0000:03:05.0" is the id for my dvb-c card. It depends on the pci slot,
-so it changes if you plug the card to different slots.
+"0000:03:05.0" is the id of the device that needs an firmware upload.
+
+In this example, this is the pci id of my dvb-c card. It depends on the pci slot,
+so it changes if you plug the card to different slots. For the tda1004x,
+the id will be an artifical number consisting on the i2c bus the device is on.
 
 You can upload the firmware like that:
 
 > export DEVDIR=/sys/class/firmware/0000\:03\:05.0
 > echo 1 > $DEVDIR/loading
+
+For the dvb-ttpci card:
 > cat dvb-ttpci-01.fw > $DEVDIR/data
+
+For the tda1004x frontend, the path above might be different, but the other things
+are the same:
+> cat tda1004x.bin > $DEVDIR/data
+
 > echo 0 > $DEVDIR/loading
 
 That's it. The driver should be up and running now.
diff -urawB xx-linux-2.6.6/drivers/media/common/saa7146_i2c.c linux-2.6.6/drivers/media/common/saa7146_i2c.c
--- xx-linux-2.6.6/drivers/media/common/saa7146_i2c.c	2004-05-13 11:58:29.000000000 +0200
+++ linux-2.6.6/drivers/media/common/saa7146_i2c.c	2004-05-14 10:38:31.000000000 +0200
@@ -413,12 +413,8 @@
 	if( NULL != i2c_adapter ) {
 		memset(i2c_adapter,0,sizeof(struct i2c_adapter));
 		strcpy(i2c_adapter->name, dev->name);	
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
-		i2c_adapter->data = dev;
-#else
 		i2c_set_adapdata(i2c_adapter,dev);
 		i2c_adapter->class = class;
-#endif
 		i2c_adapter->algo	   = &saa7146_algo;
 		i2c_adapter->algo_data     = NULL;
 		i2c_adapter->id 	   = I2C_ALGO_SAA7146;
diff -urawB xx-linux-2.6.6/drivers/media/dvb/dvb-core/dvb_frontend.c linux-2.6.6/drivers/media/dvb/dvb-core/dvb_frontend.c
--- xx-linux-2.6.6/drivers/media/dvb/dvb-core/dvb_frontend.c	2004-05-13 11:58:29.000000000 +0200
+++ linux-2.6.6/drivers/media/dvb/dvb-core/dvb_frontend.c	2004-05-14 09:50:32.000000000 +0200
@@ -174,7 +174,7 @@
 {
 	struct list_head *entry;
 	int stepsize = this_fe->info->frequency_stepsize;
-	int this_fe_adap_num = this_fe->frontend.i2c->adapter->num;
+	int this_fe_adap_num = this_fe->frontend.dvb_adapter->num;
 	int frequency;
 
 	if (!stepsize || recursive > 10) {
@@ -198,7 +198,7 @@
 
 		fe = list_entry (entry, struct dvb_frontend_data, list_head);
 
-		if (fe->frontend.i2c->adapter->num != this_fe_adap_num)
+		if (fe->frontend.dvb_adapter->num != this_fe_adap_num)
 			continue;
 
 		f = fe->parameters.frequency;
@@ -333,8 +333,8 @@
 {
 	struct dvb_frontend *frontend = &fe->frontend;
 
-	dprintk ("DVB: initialising frontend %i:%i (%s)...\n",
-		 frontend->i2c->adapter->num, frontend->i2c->id,
+	dprintk ("DVB: initialising frontend %i (%s)...\n",
+		 frontend->dvb_adapter->num,
 		 fe->info->name);
 
 	dvb_frontend_internal_ioctl (frontend, FE_INIT, NULL);
@@ -490,8 +490,8 @@
 
 	dprintk ("%s\n", __FUNCTION__);
 
-	snprintf (name, sizeof(name), "kdvb-fe-%i:%i",
-		  fe->frontend.i2c->adapter->num, fe->frontend.i2c->id);
+	snprintf (name, sizeof(name), "kdvb-fe-%i",
+		  fe->frontend.dvb_adapter->num);
 
 	dvb_kernel_thread_setup (name);
 
@@ -897,7 +899,7 @@
 
 		fe = list_entry (entry, struct dvb_frontend_data, list_head);
 
-		if (fe->frontend.i2c->adapter == adapter &&
+		if (fe->frontend.dvb_adapter == adapter &&
 		    fe->frontend.before_ioctl == NULL &&
 		    fe->frontend.after_ioctl == NULL)
 		{
@@ -931,7 +933,7 @@
 
 		fe = list_entry (entry, struct dvb_frontend_data, list_head);
 
-		if (fe->frontend.i2c->adapter == adapter &&
+		if (fe->frontend.dvb_adapter == adapter &&
 		    fe->frontend.before_ioctl == before_ioctl &&
 		    fe->frontend.after_ioctl == after_ioctl)
 		{
@@ -992,7 +994,7 @@
 
 		fe = list_entry (entry, struct dvb_frontend_data, list_head);
 
-		if (fe->frontend.i2c->adapter == adapter &&
+		if (fe->frontend.dvb_adapter == adapter &&
 		    fe->frontend.notifier_callback == NULL)
 		{
 			fe->frontend.notifier_callback = callback;
@@ -1021,7 +1023,7 @@
 
 		fe = list_entry (entry, struct dvb_frontend_data, list_head);
 
-		if (fe->frontend.i2c->adapter == adapter &&
+		if (fe->frontend.dvb_adapter == adapter &&
 		    fe->frontend.notifier_callback == callback)
 		{
 			fe->frontend.notifier_callback = NULL;
@@ -1096,6 +1096,7 @@
 
 	fe->frontend.ioctl = ioctl;
 	fe->frontend.i2c = i2c;
+	fe->frontend.dvb_adapter = i2c->adapter;
 	fe->frontend.data = data;
 	fe->info = info;
 	fe->inversion = INVERSION_OFF;
@@ -1176,6 +1177,123 @@
 	return -EINVAL;
 }
 
+int
+dvb_register_frontend_new (int (*ioctl) (struct dvb_frontend *frontend,
+				     unsigned int cmd, void *arg),
+		       struct dvb_adapter *dvb_adapter,
+		       void *data,
+		       struct dvb_frontend_info *info)
+{
+	struct list_head *entry;
+	struct dvb_frontend_data *fe;
+	static const struct dvb_device dvbdev_template = {
+		.users = ~0,
+		.writers = 1,
+		.readers = (~0)-1,
+		.fops = &dvb_frontend_fops,
+		.kernel_ioctl = dvb_frontend_ioctl
+	};
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (down_interruptible (&frontend_mutex))
+		return -ERESTARTSYS;
+
+	if (!(fe = kmalloc (sizeof (struct dvb_frontend_data), GFP_KERNEL))) {
+		up (&frontend_mutex);
+		return -ENOMEM;
+	}
+
+	memset (fe, 0, sizeof (struct dvb_frontend_data));
+
+	init_MUTEX (&fe->sem);
+	init_waitqueue_head (&fe->wait_queue);
+	init_waitqueue_head (&fe->events.wait_queue);
+	init_MUTEX (&fe->events.sem);
+	fe->events.eventw = fe->events.eventr = 0;
+	fe->events.overflow = 0;
+
+	fe->frontend.ioctl = ioctl;
+	fe->frontend.dvb_adapter = dvb_adapter;
+	fe->frontend.i2c = NULL;
+	fe->frontend.data = data;
+	fe->info = info;
+	fe->inversion = INVERSION_OFF;
+
+	list_for_each (entry, &frontend_ioctl_list) {
+		struct dvb_frontend_ioctl_data *ioctl;
+
+		ioctl = list_entry (entry,
+				    struct dvb_frontend_ioctl_data,
+				    list_head);
+
+		if (ioctl->adapter == dvb_adapter) {
+			fe->frontend.before_ioctl = ioctl->before_ioctl;
+			fe->frontend.after_ioctl = ioctl->after_ioctl;
+			fe->frontend.before_after_data = ioctl->before_after_data;
+			break;
+		}
+	}
+
+	list_for_each (entry, &frontend_notifier_list) {
+		struct dvb_frontend_notifier_data *notifier;
+
+		notifier = list_entry (entry,
+				       struct dvb_frontend_notifier_data,
+				       list_head);
+
+		if (notifier->adapter == dvb_adapter) {
+			fe->frontend.notifier_callback = notifier->callback;
+			fe->frontend.notifier_data = notifier->data;
+			break;
+		}
+	}
+
+	list_add_tail (&fe->list_head, &frontend_list);
+
+	printk ("DVB: registering frontend %i (%s)...\n",
+		fe->frontend.dvb_adapter->num,
+		fe->info->name);
+
+	dvb_register_device (dvb_adapter, &fe->dvbdev, &dvbdev_template,
+			     fe, DVB_DEVICE_FRONTEND);
+
+	if ((info->caps & FE_NEEDS_BENDING) || (dvb_override_frequency_bending == 2))
+		do_frequency_bending = 1;
+
+	up (&frontend_mutex);
+	return 0;
+}
+
+int dvb_unregister_frontend_new (int (*ioctl) (struct dvb_frontend *frontend,
+					   unsigned int cmd, void *arg),
+			     struct dvb_adapter *dvb_adapter)
+{
+	struct list_head *entry, *n;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	down (&frontend_mutex);
+
+	list_for_each_safe (entry, n, &frontend_list) {
+		struct dvb_frontend_data *fe;
+
+		fe = list_entry (entry, struct dvb_frontend_data, list_head);
+
+		if (fe->frontend.ioctl == ioctl && fe->frontend.dvb_adapter == dvb_adapter) {
+			dvb_unregister_device (fe->dvbdev);
+			list_del (entry);
+			up (&frontend_mutex);
+			dvb_frontend_stop (fe);
+			kfree (fe);
+			return 0;
+		}
+	}
+
+	up (&frontend_mutex);
+	return -EINVAL;
+}
+
 MODULE_PARM(dvb_frontend_debug,"i");
 MODULE_PARM(dvb_shutdown_timeout,"i");
 MODULE_PARM(dvb_override_frequency_bending,"i");
diff -urawB xx-linux-2.6.6/drivers/media/dvb/dvb-core/dvb_frontend.h linux-2.6.6/drivers/media/dvb/dvb-core/dvb_frontend.h
--- xx-linux-2.6.6/drivers/media/dvb/dvb-core/dvb_frontend.h	2004-05-13 11:58:30.000000000 +0200
+++ linux-2.6.6/drivers/media/dvb/dvb-core/dvb_frontend.h	2004-05-14 09:49:40.000000000 +0200
@@ -1,9 +1,12 @@
 /* 
  * dvb_frontend.h
  *
- * Copyright (C) 2001 Ralph Metzler for convergence integrated media GmbH
- *                    overhauled by Holger Waechtler for Convergence GmbH
+ * Copyright (C) 2001 convergence integrated media GmbH
+ * Copyright (C) 2004 convergence GmbH
  *
+ * Written by Ralph Metzler
+ * Overhauled by Holger Waechtler
+ * Kernel I2C stuff by Michael Hunold <hunold@convergence.de>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
@@ -34,11 +37,8 @@
 
 #include <linux/dvb/frontend.h>
 
-#include "dvb_i2c.h"
 #include "dvbdev.h"
-
-
-
+#include "dvb_i2c.h"
 
 /**
  *   when before_ioctl is registered and returns value 0, ioctl and after_ioctl
@@ -50,10 +50,12 @@
 	int (*ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg);
 	int (*after_ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg);
 	void (*notifier_callback) (fe_status_t s, void *data);
-	struct dvb_i2c_bus *i2c;
+	struct dvb_adapter *dvb_adapter;
 	void *before_after_data;   /*  can be used by hardware module... */
 	void *notifier_data;       /*  can be used by hardware module... */
 	void *data;                /*  can be used by hardware module... */
+	
+	struct dvb_i2c_bus *i2c;	/* legacy cruft, currently fe drivers depend on this */
 };
 
 struct dvb_frontend_tune_settings {
@@ -75,7 +77,8 @@
 #define FE_SLEEP              _IO('v', 80)
 #define FE_INIT               _IO('v', 81)
 #define FE_GET_TUNE_SETTINGS  _IOWR('v', 83, struct dvb_frontend_tune_settings)
-
+#define FE_REGISTER	      _IO  ('v', 84)
+#define FE_UNREGISTER	      _IO  ('v', 85)
 
 extern int
 dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend,
@@ -89,6 +92,18 @@
 				       unsigned int cmd, void *arg),
 			 struct dvb_i2c_bus *i2c);
 
+extern int
+dvb_register_frontend_new (int (*ioctl) (struct dvb_frontend *frontend,
+				     unsigned int cmd, void *arg),
+		       struct dvb_adapter *dvb_adapter,
+		       void *data,
+		       struct dvb_frontend_info *info);
+
+extern int
+dvb_unregister_frontend_new (int (*ioctl) (struct dvb_frontend *frontend,
+				       unsigned int cmd, void *arg),
+			 struct dvb_adapter *dvb_adapter);
+
 
 /**
  *  Add special ioctl code performed before and after the main ioctl
diff -urawB xx-linux-2.6.6/drivers/media/dvb/dvb-core/dvb_ksyms.c linux-2.6.6/drivers/media/dvb/dvb-core/dvb_ksyms.c
--- xx-linux-2.6.6/drivers/media/dvb/dvb-core/dvb_ksyms.c	2004-05-13 11:58:30.000000000 +0200
+++ linux-2.6.6/drivers/media/dvb/dvb-core/dvb_ksyms.c	2004-05-14 09:51:43.000000000 +0200
@@ -23,17 +23,20 @@
 EXPORT_SYMBOL(dvbdmx_connect_frontend);
 EXPORT_SYMBOL(dvbdmx_disconnect_frontend);
 
-EXPORT_SYMBOL(dvb_register_frontend);
-EXPORT_SYMBOL(dvb_unregister_frontend);
+EXPORT_SYMBOL(dvb_register_frontend_new);
+EXPORT_SYMBOL(dvb_unregister_frontend_new);
 EXPORT_SYMBOL(dvb_add_frontend_ioctls);
 EXPORT_SYMBOL(dvb_remove_frontend_ioctls);
 EXPORT_SYMBOL(dvb_add_frontend_notifier);
 EXPORT_SYMBOL(dvb_remove_frontend_notifier);
 
+#include "dvb_i2c.h"
 EXPORT_SYMBOL(dvb_register_i2c_bus);
 EXPORT_SYMBOL(dvb_unregister_i2c_bus);
 EXPORT_SYMBOL(dvb_register_i2c_device);
 EXPORT_SYMBOL(dvb_unregister_i2c_device);
+EXPORT_SYMBOL(dvb_register_frontend);
+EXPORT_SYMBOL(dvb_unregister_frontend);
 
 EXPORT_SYMBOL(dvb_net_init);
 EXPORT_SYMBOL(dvb_net_release);
diff -urawB xx-linux-2.6.6/drivers/media/dvb/frontends/Kconfig linux-2.6.6/drivers/media/dvb/frontends/Kconfig
--- xx-linux-2.6.6/drivers/media/dvb/frontends/Kconfig	2004-05-13 11:58:30.000000000 +0200
+++ linux-2.6.6/drivers/media/dvb/frontends/Kconfig	2004-05-14 11:12:21.000000000 +0200
@@ -163,19 +162,6 @@
 	  DVB adapter simply enable all supported frontends, the
 	  right one will get autodetected.
 
-config DVB_TDA1004X_FIRMWARE_FILE
-        string "Full pathname of tda1004x.bin firmware file"
-        depends on DVB_TDA1004X
-        default "/usr/lib/hotplug/firmware/tda1004x.bin"
-        help
-          The TDA1004X requires additional firmware in order to function.
-          The firmware file can obtained as follows:
-            wget http://www.technotrend.de/new/215/TTweb_215a_budget_20_05_2003.zip
-            unzip -j TTweb_215a_budget_20_05_2003.zip Software/Oem/PCI/App/ttlcdacc.dll
-            mv ttlcdacc.dll /usr/lib/hotplug/firmware/tda1004x.bin
-	  Note: even if you're using a USB device, you MUST get the file from the
-	  TechnoTrend PCI drivers.
-
 config DVB_NXT6000
 	tristate "Frontends with NxtWave Communications NXT6000 demodulator (OFDM)"
 	depends on DVB_CORE
diff -urawB xx-linux-2.6.6/drivers/media/dvb/frontends/tda1004x.c linux-2.6.6/drivers/media/dvb/frontends/tda1004x.c
--- xx-linux-2.6.6/drivers/media/dvb/frontends/tda1004x.c	2004-05-13 11:58:30.000000000 +0200
+++ linux-2.6.6/drivers/media/dvb/frontends/tda1004x.c	2004-05-14 09:47:42.000000000 +0200
@@ -22,38 +22,30 @@
 
 /*
     This driver needs a copy of the DLL "ttlcdacc.dll" from the Haupauge or Technotrend
-    windows driver saved as '/usr/lib/hotplug/firmware/tda1004x.bin'.
-    You can also pass the complete file name with the module parameter 'tda1004x_firmware'.
+	windows driver.
 
     Currently the DLL from v2.15a of the technotrend driver is supported. Other versions can
     be added reasonably painlessly.
 
     Windows driver URL: http://www.technotrend.de/
- */
 
+	wget http://www.technotrend.de/new/215/TTweb_215a_budget_20_05_2003.zip
+	unzip -j TTweb_215a_budget_20_05_2003.zip Software/Oem/PCI/App/ttlcdacc.dll
+*/
+#define TDA1004X_DEFAULT_FIRMWARE "tda1004x.bin"
 
-#define __KERNEL_SYSCALLS__
-#include <linux/kernel.h>
-#include <linux/vmalloc.h>
-#include <linux/module.h>
 #include <linux/init.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/unistd.h>
-#include <linux/fcntl.h>
-#include <linux/errno.h>
-#include <linux/syscalls.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+/* fixme: add this to i2c-id.h */
+#define I2C_DRIVERID_TDA1004X I2C_DRIVERID_EXP2
 
 #include "dvb_frontend.h"
 #include "dvb_functions.h"
 
-#ifndef DVB_TDA1004X_FIRMWARE_FILE
-#define DVB_TDA1004X_FIRMWARE_FILE "/usr/lib/hotplug/firmware/tda1004x.bin"
-#endif
-
 static int tda1004x_debug = 0;
-static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE;
 
 #define MC44BC374_ADDRESS        0x65
 
@@ -173,12 +164,17 @@
 struct tda1004x_state {
 	u8 tda1004x_address;
 	u8 tuner_address;
-	u8 initialised:1;
-        u8 tuner_type:2;
-        u8 fe_type:2;
+	u8 initialised;
+	u8 tuner_type;
+	u8 fe_type;
+	struct i2c_adapter *i2c;
+	struct dvb_adapter *dvb;
+
+	int dspCodeCounterReg;
+	int dspCodeInReg;
+	int dspVersion;
 };
 
-
 struct fwinfo {
 	int file_size;
 	int fw_offset;
@@ -184,15 +180,28 @@
 	int fw_offset;
 	int fw_size;
 };
-static struct fwinfo tda10045h_fwinfo[] = { {.file_size = 286720,.fw_offset = 0x34cc5,.fw_size = 30555} };
-static int tda10045h_fwinfo_count = sizeof(tda10045h_fwinfo) / sizeof(struct fwinfo);
 
-static struct fwinfo tda10046h_fwinfo[] = { {.file_size = 286720,.fw_offset = 0x3c4f9,.fw_size = 24479} };
-static int tda10046h_fwinfo_count = sizeof(tda10046h_fwinfo) / sizeof(struct fwinfo);
+static struct fwinfo tda10045h_fwinfo[] = {
+	{
+		.file_size = 286720,
+		.fw_offset = 0x34cc5,
+		.fw_size = 30555
+	},
+};
 
+static int tda10045h_fwinfo_count = sizeof(tda10045h_fwinfo) / sizeof(struct fwinfo);
+
+static struct fwinfo tda10046h_fwinfo[] = {
+	{
+		.file_size = 286720,
+		.fw_offset = 0x3c4f9,
+		.fw_size = 24479
+	}
+};
 
+static int tda10046h_fwinfo_count = sizeof(tda10046h_fwinfo) / sizeof(struct fwinfo);
 
-static int tda1004x_write_byte(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state, int reg, int data)
+static int tda1004x_write_byte(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg, int data)
 {
 	int ret;
 	u8 buf[] = { reg, data };
@@ -201,7 +210,7 @@
 	dprintk("%s: reg=0x%x, data=0x%x\n", __FUNCTION__, reg, data);
 
         msg.addr = tda_state->tda1004x_address;
-	ret = i2c->xfer(i2c, &msg, 1);
+	ret = i2c_transfer(i2c, &msg, 1);
 
 	if (ret != 1)
 		dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
@@ -212,7 +221,7 @@
 	return (ret != 1) ? -1 : 0;
 }
 
-static int tda1004x_read_byte(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state, int reg)
+static int tda1004x_read_byte(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg)
 {
 	int ret;
 	u8 b0[] = { reg };
@@ -224,7 +233,7 @@
 
         msg[0].addr = tda_state->tda1004x_address;
         msg[1].addr = tda_state->tda1004x_address;
-	ret = i2c->xfer(i2c, msg, 2);
+	ret = i2c_transfer(i2c, msg, 2);
 
 	if (ret != 2) {
 		dprintk("%s: error reg=0x%x, ret=%i\n", __FUNCTION__, reg,
@@ -237,7 +246,7 @@
 	return b1[0];
 }
 
-static int tda1004x_write_mask(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state, int reg, int mask, int data)
+static int tda1004x_write_mask(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg, int mask, int data)
 {
         int val;
 	dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __FUNCTION__, reg,
@@ -256,7 +265,7 @@
 	return tda1004x_write_byte(i2c, tda_state, reg, val);
 }
 
-static int tda1004x_write_buf(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state, int reg, unsigned char *buf, int len)
+static int tda1004x_write_buf(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg, unsigned char *buf, int len)
 {
 	int i;
 	int result;
@@ -273,7 +282,7 @@
 	return result;
 }
 
-static int tda1004x_enable_tuner_i2c(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state)
+static int tda1004x_enable_tuner_i2c(struct i2c_adapter *i2c, struct tda1004x_state *tda_state)
 {
         int result;
 	dprintk("%s\n", __FUNCTION__);
@@ -283,9 +292,8 @@
 	return result;
 }
 
-static int tda1004x_disable_tuner_i2c(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state)
+static int tda1004x_disable_tuner_i2c(struct i2c_adapter *i2c, struct tda1004x_state *tda_state)
 {
-
 	dprintk("%s\n", __FUNCTION__);
 
 	return tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 2, 0);
@@ -291,8 +299,7 @@
 	return tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 2, 0);
 }
 
-
-static int tda10045h_set_bandwidth(struct dvb_i2c_bus *i2c,
+static int tda10045h_set_bandwidth(struct i2c_adapter *i2c,
 	                           struct tda1004x_state *tda_state,
 		                   fe_bandwidth_t bandwidth)
 {
@@ -322,12 +329,10 @@
 
         tda1004x_write_byte(i2c, tda_state, TDA10045H_IOFFSET, 0);
 
-        // done
         return 0;
 }
 
-
-static int tda10046h_set_bandwidth(struct dvb_i2c_bus *i2c,
+static int tda10046h_set_bandwidth(struct i2c_adapter *i2c,
                                    struct tda1004x_state *tda_state,
                                    fe_bandwidth_t bandwidth)
 {
@@ -355,162 +360,145 @@
                 return -EINVAL;
         }
 
-        // done
         return 0;
 }
 
-
-static int tda1004x_fwupload(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state)
+static int tda1004x_do_upload(struct i2c_adapter *i2c, struct tda1004x_state *state, unsigned char *mem, unsigned int len)
 {
-	u8 fw_buf[65];
-	struct i2c_msg fw_msg = {.addr = 0,.flags = 0,.buf = fw_buf,.len = 0 };
-	unsigned char *firmware = NULL;
-	int filesize;
-	int fd;
-	int fwinfo_idx;
-	int fw_size = 0;
-        int fw_pos, fw_offset;
+	u8 buf[65];
+	struct i2c_msg fw_msg = {.addr = 0,.flags = 0,.buf = buf,.len = 0 };
 	int tx_size;
-	mm_segment_t fs = get_fs();
-        int dspCodeCounterReg=0, dspCodeInReg=0, dspVersion=0;
-        int fwInfoCount=0;
-        struct fwinfo* fwInfo = NULL;
-        unsigned long timeout;
+	int pos = 0;
 
-        // DSP parameters
-        switch(tda_state->fe_type) {
-        case FE_TYPE_TDA10045H:
-                dspCodeCounterReg = TDA10045H_FWPAGE;
-                dspCodeInReg = TDA10045H_CODE_IN;
-                dspVersion = 0x2c;
-                fwInfoCount = tda10045h_fwinfo_count;
-                fwInfo = tda10045h_fwinfo;
-                break;
+	/* clear code counter */
+	tda1004x_write_byte(i2c, state, state->dspCodeCounterReg, 0);
+	fw_msg.addr = state->tda1004x_address;
 
-        case FE_TYPE_TDA10046H:
-                dspCodeCounterReg = TDA10046H_CODE_CPT;
-                dspCodeInReg = TDA10046H_CODE_IN;
-                dspVersion = 0x20;
-                fwInfoCount = tda10046h_fwinfo_count;
-                fwInfo = tda10046h_fwinfo;
-                break;
+	buf[0] = state->dspCodeInReg;
+	while (pos != len) {
+
+		// work out how much to send this time
+		tx_size = len - pos;
+		if (tx_size > 0x10) {
+			tx_size = 0x10;
         }
 
-	// Load the firmware
-	set_fs(get_ds());
-	fd = open(tda1004x_firmware, 0, 0);
-	if (fd < 0) {
-		printk("%s: Unable to open firmware %s\n", __FUNCTION__,
-		       tda1004x_firmware);
+		// send the chunk
+		memcpy(buf + 1, mem + pos, tx_size);
+		fw_msg.len = tx_size + 1;
+		if (i2c_transfer(i2c, &fw_msg, 1) != 1) {
+			printk("tda1004x: Error during firmware upload\n");
 		return -EIO;
 	}
-	filesize = lseek(fd, 0L, 2);
-	if (filesize <= 0) {
-		printk("%s: Firmware %s is empty\n", __FUNCTION__,
-		       tda1004x_firmware);
-		sys_close(fd);
-		return -EIO;
+		pos += tx_size;
+
+		dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, pos);
+	}
+	return 0;
 	}
 
-        // find extraction parameters for firmware
+static int tda1004x_find_extraction_params(struct fwinfo* fwInfo, int fwInfoCount, int size)
+{
+	int fwinfo_idx;
+
         for (fwinfo_idx = 0; fwinfo_idx < fwInfoCount; fwinfo_idx++) {
-                if (fwInfo[fwinfo_idx].file_size == filesize)
+		if (fwInfo[fwinfo_idx].file_size == size)
 			break;
 	}
         if (fwinfo_idx >= fwInfoCount) {
-		printk("%s: Unsupported firmware %s\n", __FUNCTION__, tda1004x_firmware);
-		sys_close(fd);
+		printk("tda1004x: Unsupported firmware uploaded.\n");
 		return -EIO;
 	}
-        fw_size = fwInfo[fwinfo_idx].fw_size;
-        fw_offset = fwInfo[fwinfo_idx].fw_offset;
 
-	// allocate buffer for it
-	firmware = vmalloc(fw_size);
-	if (firmware == NULL) {
-		printk("%s: Out of memory loading firmware\n",
-		       __FUNCTION__);
-		sys_close(fd);
-		return -EIO;
+	return fwinfo_idx;
 	}
 
-	// read it!
-	lseek(fd, fw_offset, 0);
-	if (read(fd, firmware, fw_size) != fw_size) {
-		printk("%s: Failed to read firmware\n", __FUNCTION__);
-		vfree(firmware);
-		sys_close(fd);
+static int tda1004x_check_upload_ok(struct i2c_adapter *i2c, struct tda1004x_state *state)
+{
+	u8 data1, data2;
+
+	// check upload was OK
+	tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP
+	tda1004x_write_byte(i2c, state, TDA1004X_DSP_CMD, 0x67);
+
+	data1 = tda1004x_read_byte(i2c, state, TDA1004X_DSP_DATA1);
+	data2 = tda1004x_read_byte(i2c, state, TDA1004X_DSP_DATA2);
+	if (data1 != 0x67 || data2 != state->dspVersion) {
+		printk("tda1004x: firmware upload failed!\n");
 		return -EIO;
 	}
-	sys_close(fd);
-	set_fs(fs);
 
-        // set some valid bandwith parameters before uploading
-        switch(tda_state->fe_type) {
-        case FE_TYPE_TDA10045H:
-                // reset chip
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x10, 0);
-                tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8);
-                tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 0);
-                dvb_delay(10);
+	return 0;
+}
 
-                // set parameters
-                tda10045h_set_bandwidth(i2c, tda_state, BANDWIDTH_8_MHZ);
-                break;
 
-        case FE_TYPE_TDA10046H:
-                // reset chip
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 1, 0);
-                tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_TRISTATE1, 1, 0);
+static int tda10045_fwupload(struct i2c_adapter *i2c, struct tda1004x_state *state, const struct firmware *fw)
+{
+	int index;
+	int ret;
+
+	index = tda1004x_find_extraction_params(tda10045h_fwinfo, tda10045h_fwinfo_count, fw->size);
+	if (index < 0)
+		return index;
+
+	/* set some valid bandwith parameters before uploading */
+
+	/* reset chip */
+	tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 0x10, 0);
+	tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 8, 8);
+	tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 8, 0);
                 dvb_delay(10);
 
-                // set parameters
-                tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL2, 10);
-                tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL3, 0);
-                tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_OFFSET, 99);
-                tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_MSB, 0xd4);
-                tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_LSB, 0x2c);
-                tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
-                break;
-        }
+	/* set parameters */
+	tda10045h_set_bandwidth(i2c, state, BANDWIDTH_8_MHZ);
 
-	// do the firmware upload
-        tda1004x_write_byte(i2c, tda_state, dspCodeCounterReg, 0); // clear code counter
-        fw_msg.addr = tda_state->tda1004x_address;
-	fw_pos = 0;
-	while (fw_pos != fw_size) {
+	ret = tda1004x_do_upload(i2c, state, fw->data + tda10045h_fwinfo[index].fw_offset, tda10045h_fwinfo[index].fw_size);
+	if (ret)
+		return ret;
 
-		// work out how much to send this time
-		tx_size = fw_size - fw_pos;
-                if (tx_size > 0x10) {
-                        tx_size = 0x10;
-		}
+	/* wait for DSP to initialise */
+	/* DSPREADY doesn't seem to work on the TDA10045H */
+	dvb_delay(100);
 
-		// send the chunk
-                fw_buf[0] = dspCodeInReg;
-		memcpy(fw_buf + 1, firmware + fw_pos, tx_size);
-		fw_msg.len = tx_size + 1;
-		if (i2c->xfer(i2c, &fw_msg, 1) != 1) {
-                        printk("tda1004x: Error during firmware upload\n");
-			vfree(firmware);
-			return -EIO;
-		}
-		fw_pos += tx_size;
+	ret = tda1004x_check_upload_ok(i2c, state);
+	if (ret)
+		return ret;
 
-		dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, fw_pos);
+	return 0;
 	}
-	vfree(firmware);
 
-        // wait for DSP to initialise
-        switch(tda_state->fe_type) {
-        case FE_TYPE_TDA10045H:
-                // DSPREADY doesn't seem to work on the TDA10045H
-                dvb_delay(100);
-                break;
+static int tda10046_fwupload(struct i2c_adapter *i2c, struct tda1004x_state *state, const struct firmware *fw)
+{
+	unsigned long timeout;
+	int index;
+	int ret;
 
-        case FE_TYPE_TDA10046H:
+	index = tda1004x_find_extraction_params(tda10046h_fwinfo, tda10046h_fwinfo_count, fw->size);
+	if (index < 0)
+		return index;
+
+	/* set some valid bandwith parameters before uploading */
+
+	/* reset chip */
+	tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 1, 0);
+	tda1004x_write_mask(i2c, state, TDA10046H_CONF_TRISTATE1, 1, 0);
+	dvb_delay(10);
+
+	/* set parameters */
+	tda1004x_write_byte(i2c, state, TDA10046H_CONFPLL2, 10);
+	tda1004x_write_byte(i2c, state, TDA10046H_CONFPLL3, 0);
+	tda1004x_write_byte(i2c, state, TDA10046H_FREQ_OFFSET, 99);
+	tda1004x_write_byte(i2c, state, TDA10046H_FREQ_PHY2_MSB, 0xd4);
+	tda1004x_write_byte(i2c, state, TDA10046H_FREQ_PHY2_LSB, 0x2c);
+	tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
+
+	ret = tda1004x_do_upload(i2c, state, fw->data + tda10046h_fwinfo[index].fw_offset, tda10046h_fwinfo[index].fw_size);
+	if (ret)
+		return ret;
+
+	/* wait for DSP to initialise */
                 timeout = jiffies + HZ;
-                while(!(tda1004x_read_byte(i2c, tda_state, TDA1004X_STATUS_CD) & 0x20)) {
+	while(!(tda1004x_read_byte(i2c, state, TDA1004X_STATUS_CD) & 0x20)) {
                         if (time_after(jiffies, timeout)) {
                                 printk("tda1004x: DSP failed to initialised.\n");
                                 return -EIO;
@@ -515,27 +503,17 @@
                                 printk("tda1004x: DSP failed to initialised.\n");
                                 return -EIO;
                         }
-
                         dvb_delay(1);
                 }
-                break;
-        }
 
-        // check upload was OK
-        tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP
-	tda1004x_write_byte(i2c, tda_state, TDA1004X_DSP_CMD, 0x67);
-	if ((tda1004x_read_byte(i2c, tda_state, TDA1004X_DSP_DATA1) != 0x67) ||
-            (tda1004x_read_byte(i2c, tda_state, TDA1004X_DSP_DATA2) != dspVersion)) {
-		printk("%s: firmware upload failed!\n", __FUNCTION__);
-		return -EIO;
-	}
+	ret = tda1004x_check_upload_ok(i2c, state);
+	if (ret)
+		return ret;
 
-        // success
         return 0;
 }
 
-
-static int tda10045h_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state)
+static int tda10045h_init(struct i2c_adapter *i2c, struct tda1004x_state *tda_state)
 {
         struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = 0,.len = 0 };
         static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
@@ -549,8 +527,8 @@
         tuner_msg.addr = MC44BC374_ADDRESS;
         tuner_msg.buf = disable_mc44BC374c;
         tuner_msg.len = sizeof(disable_mc44BC374c);
-        if (i2c->xfer(i2c, &tuner_msg, 1) != 1) {
-                i2c->xfer(i2c, &tuner_msg, 1);
+	if (i2c_transfer(i2c, &tuner_msg, 1) != 1) {
+		i2c_transfer(i2c, &tuner_msg, 1);
         }
         tda1004x_disable_tuner_i2c(i2c, tda_state);
 
@@ -567,13 +545,10 @@
         tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity
         tda1004x_write_byte(i2c, tda_state, TDA1004X_CONFADC1, 0x2e);
 
-	// done
 	return 0;
 }
 
-
-
-static int tda10046h_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state)
+static int tda10046h_init(struct i2c_adapter *i2c, struct tda1004x_state *tda_state)
 {
         struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = 0,.len = 0 };
         static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
@@ -587,8 +562,8 @@
         tuner_msg.addr = MC44BC374_ADDRESS;
         tuner_msg.buf = disable_mc44BC374c;
         tuner_msg.len = sizeof(disable_mc44BC374c);
-        if (i2c->xfer(i2c, &tuner_msg, 1) != 1) {
-                i2c->xfer(i2c, &tuner_msg, 1);
+	if (i2c_transfer(i2c, &tuner_msg, 1) != 1) {
+		i2c_transfer(i2c, &tuner_msg, 1);
         }
         tda1004x_disable_tuner_i2c(i2c, tda_state);
 
@@ -619,7 +594,6 @@
         tda1004x_write_mask(i2c, tda_state, TDA10046H_GPIO_SELECT, 8, 8); // GPIO select
         tda10046h_set_bandwidth(i2c, tda_state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz
 
-        // done
         return 0;
 }
 
@@ -665,7 +637,7 @@
 	return -1;
 }
 
-static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c,
+static int tda1004x_set_frequency(struct i2c_adapter *i2c,
 			   struct tda1004x_state *tda_state,
 			   struct dvb_frontend_parameters *fe_params)
 {
@@ -698,7 +670,7 @@
 		tda1004x_enable_tuner_i2c(i2c, tda_state);
 		tuner_msg.addr = tda_state->tuner_address;
 		tuner_msg.len = 4;
-		i2c->xfer(i2c, &tuner_msg, 1);
+		i2c_transfer(i2c, &tuner_msg, 1);
 
 		// wait for it to finish
 		tuner_msg.len = 1;
@@ -706,7 +678,7 @@
 		counter = 0;
 		counter2 = 0;
 		while (counter++ < 100) {
-			if (i2c->xfer(i2c, &tuner_msg, 1) == 1) {
+			if (i2c_transfer(i2c, &tuner_msg, 1) == 1) {
 				if (tuner_buf[0] & 0x40) {
 					counter2++;
 				} else {
@@ -803,7 +775,7 @@
 		tda1004x_enable_tuner_i2c(i2c, tda_state);
 		tuner_msg.addr = tda_state->tuner_address;
 		tuner_msg.len = 4;
-                if (i2c->xfer(i2c, &tuner_msg, 1) != 1) {
+		if (i2c_transfer(i2c, &tuner_msg, 1) != 1) {
 			return -EIO;
 		}
 		dvb_delay(1);
@@ -818,11 +790,10 @@
 
 	dprintk("%s: success\n", __FUNCTION__);
 
-	// done
 	return 0;
 }
 
-static int tda1004x_set_fe(struct dvb_i2c_bus *i2c,
+static int tda1004x_set_fe(struct i2c_adapter *i2c,
 	 	           struct tda1004x_state *tda_state,
 		           struct dvb_frontend_parameters *fe_params)
 {
@@ -1002,14 +973,11 @@
                 break;
         }
 
-	// done
 	return 0;
 }
 
-
-static int tda1004x_get_fe(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_state, struct dvb_frontend_parameters *fe_params)
+static int tda1004x_get_fe(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, struct dvb_frontend_parameters *fe_params)
 {
-
 	dprintk("%s\n", __FUNCTION__);
 
 	// inversion status
@@ -1111,12 +1079,10 @@
 		break;
 	}
 
-	// done
 	return 0;
 }
 
-
-static int tda1004x_read_status(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_state, fe_status_t * fe_status)
+static int tda1004x_read_status(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, fe_status_t * fe_status)
 {
 	int status;
         int cber;
@@ -1178,7 +1144,7 @@
 	return 0;
 }
 
-static int tda1004x_read_signal_strength(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_state, u16 * signal)
+static int tda1004x_read_signal_strength(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u16 * signal)
 {
 	int tmp;
         int reg = 0;
@@ -1201,14 +1167,12 @@
 	if (tmp < 0)
 		return -EIO;
 
-	// done
 	*signal = (tmp << 8) | tmp;
 	dprintk("%s: signal=0x%x\n", __FUNCTION__, *signal);
 	return 0;
 }
 
-
-static int tda1004x_read_snr(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_state, u16 * snr)
+static int tda1004x_read_snr(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u16 * snr)
 {
 	int tmp;
 
@@ -1222,13 +1186,12 @@
                 tmp = 255 - tmp;
         }
 
-        // done
 	*snr = ((tmp << 8) | tmp);
 	dprintk("%s: snr=0x%x\n", __FUNCTION__, *snr);
 	return 0;
 }
 
-static int tda1004x_read_ucblocks(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_state, u32* ucblocks)
+static int tda1004x_read_ucblocks(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u32* ucblocks)
 {
 	int tmp;
 	int tmp2;
@@ -1255,7 +1218,6 @@
 			break;
 	}
 
-	// done
 	if (tmp != 0x7f) {
 		*ucblocks = tmp;
 	} else {
@@ -1265,7 +1227,7 @@
 	return 0;
 }
 
-static int tda1004x_read_ber(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_state, u32* ber)
+static int tda1004x_read_ber(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u32* ber)
 {
         int tmp;
 
@@ -1280,12 +1242,11 @@
         *ber |= (tmp << 9);
         tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_RESET);
 
-	// done
 	dprintk("%s: ber=0x%x\n", __FUNCTION__, *ber);
 	return 0;
 }
 
-static int tda1004x_sleep(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_state)
+static int tda1004x_sleep(struct i2c_adapter *i2c, struct tda1004x_state* tda_state)
 {
 	switch(tda_state->fe_type) {
 	case FE_TYPE_TDA10045H:
@@ -1300,12 +1261,11 @@
 	return 0;
 }
 
-
 static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
 {
-	int status = 0;
-	struct dvb_i2c_bus *i2c = fe->i2c;
 	struct tda1004x_state *tda_state = (struct tda1004x_state *) fe->data;
+	struct i2c_adapter *i2c = tda_state->i2c;
+	int status = 0;
 
 	dprintk("%s: cmd=0x%x\n", __FUNCTION__, cmd);
 
@@ -1383,27 +1343,23 @@
 	return 0;
 }
 
-
-static int tda1004x_attach(struct dvb_i2c_bus *i2c, void **data)
+static int tda1004x_attach(struct i2c_adapter *i2c, struct tda1004x_state* state)
 {
         int tda1004x_address = -1;
 	int tuner_address = -1;
         int fe_type = -1;
         int tuner_type = -1;
-	struct tda1004x_state tda_state;
-	struct tda1004x_state* ptda_state;
 	struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=0, .len=0 };
         static u8 td1344_init[] = { 0x0b, 0xf5, 0x88, 0xab };
         static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab };
         static u8 td1316_init_tda10046h[] = { 0x0b, 0xf5, 0x80, 0xab };
-        int status;
 
 	dprintk("%s\n", __FUNCTION__);
 
         // probe for tda10045h
         if (tda1004x_address == -1) {
-                tda_state.tda1004x_address = 0x08;
-	if (tda1004x_read_byte(i2c, &tda_state, TDA1004X_CHIPID) == 0x25) {
+		state->tda1004x_address = 0x08;
+		if (tda1004x_read_byte(i2c, state, TDA1004X_CHIPID) == 0x25) {
                         tda1004x_address = 0x08;
                         fe_type = FE_TYPE_TDA10045H;
                 printk("tda1004x: Detected Philips TDA10045H.\n");
@@ -1412,8 +1368,8 @@
 
         // probe for tda10046h
         if (tda1004x_address == -1) {
-                tda_state.tda1004x_address = 0x08;
-                if (tda1004x_read_byte(i2c, &tda_state, TDA1004X_CHIPID) == 0x46) {
+		state->tda1004x_address = 0x08;
+		if (tda1004x_read_byte(i2c, state, TDA1004X_CHIPID) == 0x46) {
                         tda1004x_address = 0x08;
                         fe_type = FE_TYPE_TDA10046H;
                         printk("tda1004x: Detected Philips TDA10046H.\n");
@@ -1426,14 +1382,14 @@
         }
 
         // enable access to the tuner
-	tda1004x_enable_tuner_i2c(i2c, &tda_state);
+	tda1004x_enable_tuner_i2c(i2c, state);
 
         // check for a TD1344 first
         if (tuner_address == -1) {
                 tuner_msg.addr = 0x61;
 	tuner_msg.buf = td1344_init;
 	tuner_msg.len = sizeof(td1344_init);
-	if (i2c->xfer(i2c, &tuner_msg, 1) == 1) {
+		if (i2c_transfer(i2c, &tuner_msg, 1) == 1) {
                 dvb_delay(1);
                         tuner_address = 0x61;
                         tuner_type = TUNER_TYPE_TD1344;
@@ -1446,7 +1402,7 @@
                 tuner_msg.addr = 0x63;
                 tuner_msg.buf = td1316_init;
                 tuner_msg.len = sizeof(td1316_init);
-                if (i2c->xfer(i2c, &tuner_msg, 1) == 1) {
+		if (i2c_transfer(i2c, &tuner_msg, 1) == 1) {
                         dvb_delay(1);
                         tuner_address = 0x63;
                         tuner_type = TUNER_TYPE_TD1316;
@@ -1459,14 +1415,14 @@
                 tuner_msg.addr = 0x60;
                 tuner_msg.buf = td1316_init_tda10046h;
                 tuner_msg.len = sizeof(td1316_init_tda10046h);
-                if (i2c->xfer(i2c, &tuner_msg, 1) == 1) {
+		if (i2c_transfer(i2c, &tuner_msg, 1) == 1) {
                         dvb_delay(1);
                         tuner_address = 0x60;
                         tuner_type = TUNER_TYPE_TD1316;
                         printk("tda1004x: Detected Philips TD1316 tuner.\n");
 		}
 	}
-	tda1004x_disable_tuner_i2c(i2c, &tda_state);
+	tda1004x_disable_tuner_i2c(i2c, state);
 
 	// did we find a tuner?
 	if (tuner_address == -1) {
@@ -1475,57 +1431,168 @@
 	}
 
         // create state
-        tda_state.tda1004x_address = tda1004x_address;
-        tda_state.fe_type = fe_type;
-	tda_state.tuner_address = tuner_address;
-        tda_state.tuner_type = tuner_type;
-	tda_state.initialised = 0;
+	state->tda1004x_address = tda1004x_address;
+	state->fe_type = fe_type;
+	state->tuner_address = tuner_address;
+	state->tuner_type = tuner_type;
+	state->initialised = 0;
 
-        // upload firmware
-        if ((status = tda1004x_fwupload(i2c, &tda_state)) != 0) return status;
+	return 0;
+}
+
+static struct i2c_client client_template;
 
-	// create the real state we'll be passing about
-	if ((ptda_state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL)) == NULL) {
+static int attach_adapter(struct i2c_adapter *adapter)
+{
+	struct i2c_client *client;
+	struct tda1004x_state *state;
+	const struct firmware *fw;
+	int ret;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
 		return -ENOMEM;
 	}
-	memcpy(ptda_state, &tda_state, sizeof(struct tda1004x_state));
-	*data = ptda_state;
 
-	// register
-        switch(tda_state.fe_type) {
+	if (NULL == (state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL))) {
+		kfree(client);
+		return -ENOMEM;
+	}
+	state->i2c = adapter;
+
+	ret = tda1004x_attach(adapter, state);
+	if (ret) {
+		kfree(state);
+		kfree(client);
+		return -ENODEV;
+	}
+
+	memcpy(client, &client_template, sizeof(struct i2c_client));
+	client->adapter = adapter;
+	client->addr = state->tda1004x_address;
+	i2c_set_clientdata(client, (void*)state);
+
+	ret = i2c_attach_client(client);
+	if (ret) {
+		kfree(client);
+		kfree(state);
+		return ret;
+	}
+
+	// upload firmware
+	BUG_ON(!state->dvb);
+
+	/* request the firmware, this will block until someone uploads it */
+	printk("tda1004x: waiting for firmware upload...\n");
+	ret = request_firmware(&fw, TDA1004X_DEFAULT_FIRMWARE, &client->dev);
+	if (ret) {
+		printk("tda1004x: no firmware upload (timeout or file not found?)\n");
+		goto out;
+	}
+
+	switch(state->fe_type) {
         case FE_TYPE_TDA10045H:
-		return dvb_register_frontend(tda1004x_ioctl, i2c, ptda_state, &tda10045h_info);
+		state->dspCodeCounterReg = TDA10045H_FWPAGE;
+		state->dspCodeInReg =  TDA10045H_CODE_IN;
+		state->dspVersion = 0x2c;
+
+		ret = tda10045_fwupload(adapter, state, fw);
+		if (ret) {
+			printk("tda1004x: firmware upload failed\n");
+			goto out;
+		}
 
+		ret = dvb_register_frontend_new (tda1004x_ioctl, state->dvb, (void*) state, &tda10045h_info);
+		break;
         case FE_TYPE_TDA10046H:
-		return dvb_register_frontend(tda1004x_ioctl, i2c, ptda_state, &tda10046h_info);
+		state->dspCodeCounterReg = TDA10046H_CODE_CPT;
+		state->dspCodeInReg =  TDA10046H_CODE_IN;
+		state->dspVersion = 0x20;
+
+		ret = tda10046_fwupload(adapter, state, fw);
+		if (ret) {
+			printk("tda1004x: firmware upload failed\n");
+			goto out;
         }
 
-        // should not get here
-        return -EINVAL;
+		ret = dvb_register_frontend_new (tda1004x_ioctl, state->dvb, (void*) state, &tda10046h_info);
+		break;
+	default:
+		BUG_ON(1);
 }
 
+	if (ret) {
+		printk("tda1004x: registering frontend failed\n");
+		goto out;
+	}
 
-static
-void tda1004x_detach(struct dvb_i2c_bus *i2c, void *data)
+	return 0;
+out:
+	i2c_detach_client(client);
+	kfree(client);
+	kfree(state);
+	return ret;
+}
+
+static int detach_client(struct i2c_client *client)
 {
+	struct tda1004x_state *state = (struct tda1004x_state*)i2c_get_clientdata(client);
+
 	dprintk("%s\n", __FUNCTION__);
 
-	kfree(data);
-	dvb_unregister_frontend(tda1004x_ioctl, i2c);
+	dvb_unregister_frontend_new (tda1004x_ioctl, state->dvb);
+	i2c_detach_client(client);
+	BUG_ON(state->dvb);
+	kfree(client);
+	kfree(state);
+	return 0;
 }
 
-
-static
-int __init init_tda1004x(void)
+static int command (struct i2c_client *client, unsigned int cmd, void *arg)
 {
-	return dvb_register_i2c_device(THIS_MODULE, tda1004x_attach, tda1004x_detach);
+	struct tda1004x_state *state = (struct tda1004x_state*)i2c_get_clientdata(client);
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	switch (cmd) {
+	case FE_REGISTER:
+		state->dvb = (struct dvb_adapter*)arg;
+		break;
+	case FE_UNREGISTER:
+		state->dvb = NULL;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+	return 0;
 }
 
+static struct i2c_driver driver = {
+	.owner 		= THIS_MODULE,
+	.name 		= "tda1004x",
+	.id 		= I2C_DRIVERID_TDA1004X,
+	.flags 		= I2C_DF_NOTIFY,
+	.attach_adapter = attach_adapter,
+	.detach_client 	= detach_client,
+	.command 	= command,
+};
+
+static struct i2c_client client_template = {
+	I2C_DEVNAME("tda1004x"),
+	.flags 		= I2C_CLIENT_ALLOW_USE,
+	.driver  	= &driver,
+};
 
-static
-void __exit exit_tda1004x(void)
+static int __init init_tda1004x(void)
 {
-	dvb_unregister_i2c_device(tda1004x_attach);
+	return i2c_add_driver(&driver);
+}
+
+static void __exit exit_tda1004x(void)
+{
+	if (i2c_del_driver(&driver))
+		printk("tda1004x: driver deregistration failed\n");
 }
 
 module_init(init_tda1004x);
@@ -1537,6 +1604,3 @@
 
 MODULE_PARM(tda1004x_debug, "i");
 MODULE_PARM_DESC(tda1004x_debug, "enable verbose debug messages");
-
-MODULE_PARM(tda1004x_firmware, "s");
-MODULE_PARM_DESC(tda1004x_firmware, "Where to find the firmware file");
diff -urawB xx-linux-2.6.6/drivers/media/dvb/ttpci/budget-core.c linux-2.6.6/drivers/media/dvb/ttpci/budget-core.c
--- xx-linux-2.6.6/drivers/media/dvb/ttpci/budget-core.c	2004-05-13 11:58:30.000000000 +0200
+++ linux-2.6.6/drivers/media/dvb/ttpci/budget-core.c	2004-05-14 10:35:16.000000000 +0200
@@ -265,6 +265,22 @@
 	return saa7146_i2c_transfer(dev, msgs, num, 6);
 }
 
+/* fixme: can this be unified among all saa7146 based dvb cards? */
+int client_register(struct i2c_client *client)
+{
+	struct saa7146_dev *dev = (struct saa7146_dev*)i2c_get_adapdata(client->adapter);
+	struct budget *budget = (struct budget*)dev->ext_priv;
+
+	return client->driver->command(client, FE_REGISTER, budget->dvb_adapter);
+}
+
+int client_unregister(struct i2c_client *client)
+{
+	struct saa7146_dev *dev = (struct saa7146_dev*)i2c_get_adapdata(client->adapter);
+	struct budget *budget = (struct budget*)dev->ext_priv;
+
+	return client->driver->command(client, FE_UNREGISTER, budget->dvb_adapter);
+}
 
 int ttpci_budget_init (struct budget *budget,
 		       struct saa7146_dev* dev,
@@ -301,7 +317,9 @@
 	if (bi->type != BUDGET_FS_ACTIVY)
 		saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */
 	
-	saa7146_i2c_adapter_prepare(dev, NULL, 0, SAA7146_I2C_BUS_BIT_RATE_120);
+	saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, I2C_ADAP_CLASS_TV_DIGITAL, SAA7146_I2C_BUS_BIT_RATE_120);
+	budget->i2c_adap.client_register = client_register;
+	budget->i2c_adap.client_unregister = client_unregister;
 
 	budget->i2c_bus = dvb_register_i2c_bus (master_xfer, dev,
 						budget->dvb_adapter, 0);
@@ -311,6 +329,12 @@
 		return -ENOMEM;
 	}
 
+	if (i2c_add_adapter(&budget->i2c_adap) < 0) {
+		dvb_unregister_i2c_bus (master_xfer, budget->i2c_bus->adapter, budget->i2c_bus->id);
+		dvb_unregister_adapter (budget->dvb_adapter);
+		return -ENOMEM;
+	}
+
 	ttpci_eeprom_parse_mac(budget->i2c_bus);
 
 	if( NULL == (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci,length,&budget->pt))) {
@@ -334,6 +358,8 @@
 		return 0;
 	}
 err:
+	i2c_del_adapter(&budget->i2c_adap);
+
 	if (budget->grabbing)
 		vfree(budget->grabbing);
 
@@ -354,6 +380,8 @@
 
 	budget_unregister (budget);
 
+	i2c_del_adapter(&budget->i2c_adap);
+
 	dvb_unregister_i2c_bus (master_xfer, budget->i2c_bus->adapter,
 				budget->i2c_bus->id);
 
diff -urawB xx-linux-2.6.6/drivers/media/dvb/ttpci/budget.h linux-2.6.6/drivers/media/dvb/ttpci/budget.h
--- xx-linux-2.6.6/drivers/media/dvb/ttpci/budget.h	2004-05-13 11:58:30.000000000 +0200
+++ linux-2.6.6/drivers/media/dvb/ttpci/budget.h	2004-05-14 10:34:50.000000000 +0200
@@ -1,9 +1,6 @@
 #ifndef __BUDGET_DVB__
 #define __BUDGET_DVB__
 
-#include <media/saa7146.h>
-
-#include "dvb_i2c.h"
 #include "dvb_frontend.h"
 #include "dvbdev.h"
 #include "demux.h"
@@ -11,6 +8,9 @@
 #include "dmxdev.h"
 #include "dvb_filter.h"
 #include "dvb_net.h"
+#include "dvb_functions.h"
+
+#include <media/saa7146.h>
 
 extern int budget_debug;
 
@@ -28,7 +28,10 @@
 
         struct saa7146_dev	*dev;
 
+#include "dvb_i2c.h"
 	struct dvb_i2c_bus	*i2c_bus;	
+
+	struct i2c_adapter	i2c_adap;	
 	struct budget_info	*card;
 
 	unsigned char		*grabbing;

Home | Main Index | Thread Index