Mailing List archive

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

[linux-dvb] Re: [patch-rfc] frontend firmware



Hello Kenneth,

while watching the European soccer championship, I converted the remaining drivers sp877x and alps_tdlb7 to the kernel i2c interface.

I attached the patch to this mail. If nobody objects (testers welcome!) I'll submit it to CVS within the next days.

CU
Michael.
? a
Index: drivers/media/dvb/frontends/Kconfig
===================================================================
RCS file: /cvs/linuxtv/dvb-kernel/linux/drivers/media/dvb/frontends/Kconfig,v
retrieving revision 1.24
diff -u -r1.24 Kconfig
--- drivers/media/dvb/frontends/Kconfig	28 May 2004 11:08:43 -0000	1.24
+++ drivers/media/dvb/frontends/Kconfig	21 Jun 2004 20:34:31 -0000
@@ -1,14 +1,3 @@
-comment "Misc. Frontend Modules"
-	depends on DVB_CORE
-
-config DVB_TWINHAN_DST
-	tristate "Twinhan DST based DVB-S/-T frontend"
-	depends on DVB_CORE && DVB_BT8XX
-	help
-	  Used in such cards as the VP-1020/1030, Twinhan DST,
-	  VVmer TV@SAT. Say Y when you want to support frontends
-	  using this asic.
-
 comment "DVB-S (satellite) frontends"
 	depends on DVB_CORE
 
@@ -74,35 +63,27 @@
  	help
  	  A DVB-T tuner module. Say Y when you want to support this frontend.
 
+	  This driver needs a copy of the Avermedia firmware. The version tested
+	  is part of the Avermedia DVB-T 1.3.26.3 Application. If the software is
+	  installed in Windoze the file will be in the /Program Files/AVerTV DVB-T/
+	  directory and is called sc_main.mc. Alternatively it can "extracted" from
+	  the install cab files.
+   
+   	  Copy this file to '/usr/lib/hotplug/firmware/dvb-fe-sp887x.fw'.
+
  	  If you don't know what tuner module is soldered on your
  	  DVB adapter simply enable all supported frontends, the
  	  right one will get autodetected.
 
-config DVB_SP887X_FIRMWARE_FILE
-        string "Full pathname of sp887x firmware file"
-        depends on DVB_SP887X
-        default "/usr/lib/hotplug/firmware/sc_main.mc"
-        help
-          This driver needs a copy of the Avermedia firmware. The version tested
-	  is part of the Avermedia DVB-T 1.3.26.3 Application. This can be downloaded
-	  from the Avermedia web site.
-	  If the software is installed in Windows the file will be in the
-	  /Program Files/AVerTV DVB-T/ directory and is called sc_main.mc.
-	  Alternatively it can "extracted" from the install cab files but this will have
-	  to be done in windows as I don't know of a linux version of extract.exe.
-	  Copy this file to /usr/lib/hotplug/firmware/sc_main.mc.
-	  With this version of the file the first 10 bytes are discarded and the next
-	  0x4000 loaded. This may change in future versions.
-
 config DVB_ALPS_TDLB7
 	tristate "Alps TDLB7 based"
 	depends on DVB_CORE
 	help
 	  A DVB-T tuner module. Say Y when you want to support this frontend.
 
-	  This tuner module needs some microcode located in a file called
-	  "Sc_main.mc" in the windows driver. Please pass the module parameter
-	  mcfile="/PATH/FILENAME" when loading alps_tdlb7.o.
+	  This driver needs a copy of the firmware file from the Haupauge
+	  Windoze driver. Copy 'Sc_main.mc' to
+	  '/usr/lib/hotplug/firmware/dvb-fe-tdlb7.fw'.
 
 	  If you don't know what tuner module is soldered on your
 	  DVB adapter simply enable all supported frontends, the
@@ -180,3 +161,14 @@
 	  If you don't know what tuner module is soldered on your
 	  DVB adapter simply enable all supported frontends, the
 	  right one will get autodetected.
+
+comment "Misc. Frontend Modules"
+	depends on DVB_CORE
+
+config DVB_TWINHAN_DST
+	tristate "Twinhan DST based DVB-S/-T frontend"
+	depends on DVB_CORE && DVB_BT8XX
+	help
+	  Used in such cards as the VP-1020/1030, Twinhan DST,
+	  VVmer TV@SAT. Say Y when you want to support frontends
+	  using this asic.
Index: drivers/media/dvb/frontends/alps_tdlb7.c
===================================================================
RCS file: /cvs/linuxtv/dvb-kernel/linux/drivers/media/dvb/frontends/alps_tdlb7.c,v
retrieving revision 1.19
diff -u -r1.19 alps_tdlb7.c
--- drivers/media/dvb/frontends/alps_tdlb7.c	25 Mar 2004 19:07:25 -0000	1.19
+++ drivers/media/dvb/frontends/alps_tdlb7.c	21 Jun 2004 20:34:31 -0000
@@ -20,31 +20,25 @@
 
 */
 
-
 /*
-    This driver needs a copy of the firmware file 'Sc_main.mc' from the Haupauge
-    windows driver in the '/usr/lib/DVB/driver/frontends' directory.
-    You can also pass the complete file name with the module parameter 'firmware_file'.
-
+    This driver needs a copy of the firmware file from the Haupauge
+    Windoze driver.
+    
+    Copy 'Sc_main.mc'  to '/usr/lib/hotplug/firmware/dvb-fe-tdlb7.fw'.
 */
+#define SP887X_DEFAULT_FIRMWARE "dvb-fe-tdlb7.fw"
 
-
-#define __KERNEL_SYSCALLS__
-#include <linux/module.h>
 #include <linux/init.h>
-#include <linux/vmalloc.h>
-#include <linux/fs.h>
-#include <linux/unistd.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
 #include <linux/delay.h>
 
 #include "dvb_frontend.h"
 #include "dvb_functions.h"
 
-#ifndef CONFIG_ALPS_TDLB7_FIRMWARE_LOCATION
-#define CONFIG_ALPS_TDLB7_FIRMWARE_LOCATION "/usr/lib/DVB/driver/frontends/Sc_main.mc"
-#endif
+/* fixme: add this to i2c-id.h */
+#define I2C_DRIVERID_TDLB7 I2C_DRIVERID_EXP3
 
-static char * firmware_file = CONFIG_ALPS_TDLB7_FIRMWARE_LOCATION;
 static int debug = 0;
 
 #define dprintk	if (debug) printk
@@ -55,9 +49,6 @@
 /* starting point for firmware in file 'Sc_main.mc' */
 #define SP8870_FIRMWARE_OFFSET 0x0A
 
-
-static int errno;
-
 static struct dvb_frontend_info tdlb7_info = {
 	.name			= "Alps TDLB7",
 	.type			= FE_OFDM,
@@ -72,13 +63,18 @@
 				  FE_CAN_HIERARCHY_AUTO |  FE_CAN_RECOVER
 };
 
-static int sp8870_writereg (struct dvb_i2c_bus *i2c, u16 reg, u16 data)
+struct tdlb7_state {
+	struct i2c_adapter *i2c;
+	struct dvb_adapter *dvb;
+};
+
+static int sp8870_writereg (struct i2c_adapter *i2c, u16 reg, u16 data)
 {
         u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff };
 	struct i2c_msg msg = { .addr = 0x71, .flags = 0, .buf = buf, .len = 4 };
 	int err;
 
-        if ((err = i2c->xfer (i2c, &msg, 1)) != 1) {
+        if ((err = i2c_transfer (i2c, &msg, 1)) != 1) {
 		dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data);
 		return -EREMOTEIO;
 	}
@@ -86,8 +82,7 @@
         return 0;
 }
 
-
-static u16 sp8870_readreg (struct dvb_i2c_bus *i2c, u16 reg)
+static u16 sp8870_readreg (struct i2c_adapter *i2c, u16 reg)
 {
 	int ret;
 	u8 b0 [] = { reg >> 8 , reg & 0xff };
@@ -95,7 +90,7 @@
 	struct i2c_msg msg [] = { { .addr = 0x71, .flags = 0, .buf = b0, .len = 2 },
 			   { .addr = 0x71, .flags = I2C_M_RD, .buf = b1, .len = 2 } };
 
-	ret = i2c->xfer (i2c, msg, 2);
+	ret = i2c_transfer (i2c, msg, 2);
 
 	if (ret != 2) {
 		dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
@@ -105,13 +100,12 @@
 	return (b1[0] << 8 | b1[1]);
 }
 
-
-static int sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4])
+static int sp5659_write (struct i2c_adapter *i2c, u8 data [4])
 {
         int ret;
         struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = 4 };
 
-        ret = i2c->xfer (i2c, &msg, 1);
+        ret = i2c_transfer (i2c, &msg, 1);
 
         if (ret != 1)
                 printk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret);
@@ -119,8 +113,7 @@
         return (ret != 1) ? -1 : 0;
 }
 
-
-static void sp5659_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq)
+static void sp5659_set_tv_freq (struct i2c_adapter *i2c, u32 freq)
 {
         u32 div = (freq + 36200000) / 166666;
         u8 buf [4];
@@ -142,60 +135,20 @@
 	sp8870_writereg(i2c, 0x206, 0x000);
 }
 
-
-static int sp8870_read_firmware_file (const char *fn, char **fp)
-{
-        int fd;
-	loff_t filesize;
-	char *dp;
-
-	fd = open(fn, 0, 0);
-	if (fd == -1) {
-                printk("%s: unable to open '%s'.\n", __FUNCTION__, fn);
-		return -EIO;
-	}
-
-	filesize = lseek(fd, 0L, 2);
-	if (filesize <= 0 || filesize < SP8870_FIRMWARE_OFFSET + SP8870_FIRMWARE_SIZE) {
-	        printk("%s: firmware filesize to small '%s'\n", __FUNCTION__, fn);
-		sys_close(fd);
-		return -EIO;
-	}
-
-	*fp= dp = vmalloc(SP8870_FIRMWARE_SIZE);
-	if (dp == NULL)	{
-		printk("%s: out of memory loading '%s'.\n", __FUNCTION__, fn);
-		sys_close(fd);
-		return -EIO;
-	}
-
-	lseek(fd, SP8870_FIRMWARE_OFFSET, 0);
-	if (read(fd, dp, SP8870_FIRMWARE_SIZE) != SP8870_FIRMWARE_SIZE) {
-		printk("%s: failed to read '%s'.\n",__FUNCTION__, fn);
-		vfree(dp);
-		sys_close(fd);
-		return -EIO;
-	}
-
-	sys_close(fd);
-	*fp = dp;
-
-	return 0;
-}
-
-
-static int sp8870_firmware_upload (struct dvb_i2c_bus *i2c)
+static int sp8870_firmware_upload (struct i2c_adapter *i2c, const struct firmware *fw)
 {
 	struct i2c_msg msg;
-	char *fw_buf = NULL;
+	char *fw_buf = fw->data;
 	int fw_pos;
 	u8 tx_buf[255];
 	int tx_len;
 	int err = 0;
-	mm_segment_t fs = get_fs();
 
 	dprintk ("%s: ...\n", __FUNCTION__);
 
+	if (fw->size < SP8870_FIRMWARE_SIZE)
+		return -ENODEV;
+
 	// system controller stop
 	sp8870_writereg(i2c, 0x0F00, 0x0000);
 
@@ -205,15 +158,6 @@
 	// instruction RAM MWR
 	sp8870_writereg(i2c, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16));
 
-	// reading firmware file to buffer
-	set_fs(get_ds());
-        err = sp8870_read_firmware_file(firmware_file, (char**) &fw_buf);
-	set_fs(fs);
-	if (err != 0) {
-		printk("%s: reading firmware file failed!\n", __FUNCTION__);
-		return err;
-	}
-
 	// do firmware upload
 	fw_pos = 0;
 	while (fw_pos < SP8870_FIRMWARE_SIZE){
@@ -226,23 +170,19 @@
 		msg.flags = 0;
 		msg.buf = tx_buf;
 		msg.len = tx_len + 2;
-        	if ((err = i2c->xfer (i2c, &msg, 1)) != 1) {
+        	if ((err = i2c_transfer (i2c, &msg, 1)) != 1) {
 			printk("%s: firmware upload failed!\n", __FUNCTION__);
 			printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err);
-        		vfree(fw_buf);
 			return err;
 		}
 		fw_pos += tx_len;
 	}
 
-	vfree(fw_buf);
-
 	dprintk ("%s: done!\n", __FUNCTION__);
 	return 0;
 };
 
-
-static void sp8870_microcontroller_stop (struct dvb_i2c_bus *i2c)
+static void sp8870_microcontroller_stop (struct i2c_adapter *i2c)
 {
 	sp8870_writereg(i2c, 0x0F08, 0x000);
 	sp8870_writereg(i2c, 0x0F09, 0x000);
@@ -251,8 +191,7 @@
 	sp8870_writereg(i2c, 0x0F00, 0x000);
 }
 
-
-static void sp8870_microcontroller_start (struct dvb_i2c_bus *i2c)
+static void sp8870_microcontroller_start (struct i2c_adapter *i2c)
 {
 	sp8870_writereg(i2c, 0x0F08, 0x000);
 	sp8870_writereg(i2c, 0x0F09, 0x000);
@@ -264,8 +203,7 @@
 	sp8870_readreg(i2c, 0x0D01);
 }
 
-
-static int sp8870_init (struct dvb_i2c_bus *i2c)
+static int sp8870_init (struct i2c_adapter *i2c)
 {
 	dprintk ("%s\n", __FUNCTION__);
 
@@ -291,8 +229,7 @@
 	return 0;
 }
 
-
-static int sp8870_read_status (struct dvb_i2c_bus *i2c,  fe_status_t * fe_status)
+static int sp8870_read_status (struct i2c_adapter *i2c,  fe_status_t * fe_status)
 {
 	int status;
 	int signal;
@@ -317,8 +254,7 @@
 	return 0;
 }
 
-
-static int sp8870_read_ber (struct dvb_i2c_bus *i2c, u32 * ber)
+static int sp8870_read_ber (struct i2c_adapter *i2c, u32 * ber)
 {
 	int ret;
 	u32 tmp;
@@ -345,8 +281,7 @@
 	return 0;
 }
 
-
-static int sp8870_read_signal_strength (struct dvb_i2c_bus *i2c,  u16 * signal)
+static int sp8870_read_signal_strength (struct i2c_adapter *i2c,  u16 * signal)
 {
 	int ret;
 	u16 tmp;
@@ -371,15 +306,13 @@
 	return 0;
 }
 
-
-static int sp8870_read_snr(struct dvb_i2c_bus *i2c, u32* snr)
+static int sp8870_read_snr(struct i2c_adapter *i2c, u32* snr)
 {
 	*snr = 0;
 	return -EOPNOTSUPP;
 }
 
-
-static int sp8870_read_uncorrected_blocks (struct dvb_i2c_bus *i2c, u32* ublocks)
+static int sp8870_read_uncorrected_blocks (struct i2c_adapter *i2c, u32* ublocks)
 {
 		int ret;
 
@@ -397,13 +330,11 @@
 		return 0;
 }
 
-
-static int sp8870_read_data_valid_signal(struct dvb_i2c_bus *i2c)
+static int sp8870_read_data_valid_signal(struct i2c_adapter *i2c)
 {
 	return (sp8870_readreg(i2c, 0x0D02) > 0);
 }
 
-
 static
 int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05)
 {
@@ -476,8 +407,7 @@
 	return 0;
 }
 
-
-static int sp8870_set_frontend_parameters (struct dvb_i2c_bus *i2c,
+static int sp8870_set_frontend_parameters (struct i2c_adapter *i2c,
 				      struct dvb_frontend_parameters *p)
 {
 	int  err;
@@ -529,7 +459,6 @@
 	return 0;
 }
 
-
 // number of trials to recover from lockup
 #define MAXTRIALS 5
 // maximum checks for data valid signal
@@ -540,7 +469,7 @@
 // only for debugging: counter for channel switches
 static int switches = 0;
 
-static int sp8870_set_frontend (struct dvb_i2c_bus *i2c, struct dvb_frontend_parameters *p)
+static int sp8870_set_frontend (struct i2c_adapter *i2c, struct dvb_frontend_parameters *p)
 {
 	/*
 	    The firmware of the sp8870 sometimes locks up after setting frontend parameters.
@@ -595,24 +524,22 @@
 	return 0;
 }
 
-
-static int sp8870_sleep(struct dvb_i2c_bus *i2c)
+static int sp8870_sleep(struct i2c_adapter *i2c)
 {
 	// tristate TS output and disable interface pins
 	return sp8870_writereg(i2c, 0xC18, 0x000);
 }
 
-
-static int sp8870_wake_up(struct dvb_i2c_bus *i2c)
+static int sp8870_wake_up(struct i2c_adapter *i2c)
 {
 	// enable TS output and interface pins
 	return sp8870_writereg(i2c, 0xC18, 0x00D);
 }
 
-
 static int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
 {
-	struct dvb_i2c_bus *i2c = fe->i2c;
+	struct tdlb7_state *state = (struct tdlb7_state *) fe->data;
+	struct i2c_adapter *i2c = state->i2c;
 
         switch (cmd) {
         case FE_GET_INFO:
@@ -667,61 +594,138 @@
         return 0;
 }
 
+static struct i2c_client client_template;
 
-static int tdlb7_attach (struct dvb_i2c_bus *i2c, void **data)
+static int attach_adapter(struct i2c_adapter *adapter)
 {
-        u8 b0 [] = { 0x02 , 0x00 };
+	struct i2c_client *client;
+	struct tdlb7_state *state;
+	const struct firmware *fw;
+	int ret;
+
+	u8 b0 [] = { 0x02 , 0x00 };
         u8 b1 [] = { 0, 0 };
         struct i2c_msg msg [] = { { .addr = 0x71, .flags = 0, .buf = b0, .len = 2 },
                                   { .addr = 0x71, .flags = I2C_M_RD, .buf = b1, .len = 2 } };
 
-        dprintk ("%s\n", __FUNCTION__);
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	if (NULL == (state = kmalloc(sizeof(struct tdlb7_state), GFP_KERNEL))) {
+		kfree(client);
+		return -ENOMEM;
+	}
+	state->i2c = adapter;
 
-        if (i2c->xfer (i2c, msg, 2) != 2)
+        if (i2c_transfer (adapter, msg, 2) != 2) {
+		kfree(state);
+		kfree(client);
                 return -ENODEV;
+	}
 
-        sp8870_firmware_upload(i2c);
+	memcpy(client, &client_template, sizeof(struct i2c_client));
+	client->adapter = adapter;
+	i2c_set_clientdata(client, (void*)state);
+
+	ret = i2c_attach_client(client);
+	if (ret) {
+		kfree(client);
+		kfree(state);
+		return ret;
+	}
+
+	/* request the firmware, this will block until someone uploads it */
+	printk("tdlb7: waiting for firmware upload...\n");
+	ret = request_firmware(&fw, SP887X_DEFAULT_FIRMWARE, &client->dev);
+	if (ret) {
+		printk("tdlb7: no firmware upload (timeout or file not found?)\n");
+		goto out;
+	}
+
+	ret = sp8870_firmware_upload(adapter, fw);
+	if (ret) {
+		printk("tdlb7: writing firmware to device failed\n");
+		goto out;
+	}
 
-        return dvb_register_frontend (tdlb7_ioctl, i2c, NULL, &tdlb7_info);
+	return 0;
+out:
+	release_firmware(fw);
+	i2c_detach_client(client);
+	kfree(client);
+	kfree(state);
+	return ret;
 }
 
-
-static void tdlb7_detach (struct dvb_i2c_bus *i2c, void *data)
+static int detach_client(struct i2c_client *client)
 {
+	struct tdlb7_state *state = (struct tdlb7_state*)i2c_get_clientdata(client);
+
 	dprintk ("%s\n", __FUNCTION__);
 
-	dvb_unregister_frontend (tdlb7_ioctl, i2c);
+	dvb_unregister_frontend_new (tdlb7_ioctl, state->dvb);
+	i2c_detach_client(client);
+	BUG_ON(state->dvb);
+	kfree(client);
+	kfree(state);
+	return 0;
 }
 
-
-static int __init init_tdlb7 (void)
+static int command (struct i2c_client *client, unsigned int cmd, void *arg)
 {
+	struct tdlb7_state *state = (struct tdlb7_state*)i2c_get_clientdata(client);
+
 	dprintk ("%s\n", __FUNCTION__);
 
-	return dvb_register_i2c_device (THIS_MODULE, tdlb7_attach, tdlb7_detach);
+	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 		= "tdlb7",
+	.id 		= I2C_DRIVERID_TDLB7,
+	.flags 		= I2C_DF_NOTIFY,
+	.attach_adapter = attach_adapter,
+	.detach_client 	= detach_client,
+	.command 	= command,
+};
+
+static struct i2c_client client_template = {
+	I2C_DEVNAME("tdlb7"),
+	.flags 		= I2C_CLIENT_ALLOW_USE,
+	.driver  	= &driver,
+};
 
-static void __exit exit_tdlb7 (void)
+static int __init init_tdlb7(void)
 {
-	dprintk ("%s\n", __FUNCTION__);
-
-	dvb_unregister_i2c_device (tdlb7_attach);
+	return i2c_add_driver(&driver);
 }
 
+static void __exit exit_tdlb7(void)
+{
+	if (i2c_del_driver(&driver))
+		printk("tdlb7: driver deregistration failed\n");
+}
 
 module_init(init_tdlb7);
 module_exit(exit_tdlb7);
 
-
 MODULE_PARM(debug,"i");
 MODULE_PARM_DESC(debug, "enable verbose debug messages");
 
-MODULE_PARM(firmware_file,"s");
-MODULE_PARM_DESC(firmware_file, "where to find the firmware file");
-
 MODULE_DESCRIPTION("TDLB7 DVB-T Frontend");
 MODULE_AUTHOR("Juergen Peitz");
 MODULE_LICENSE("GPL");
-
-
Index: drivers/media/dvb/frontends/sp887x.c
===================================================================
RCS file: /cvs/linuxtv/dvb-kernel/linux/drivers/media/dvb/frontends/sp887x.c,v
retrieving revision 1.11
diff -u -r1.11 sp887x.c
--- drivers/media/dvb/frontends/sp887x.c	11 Mar 2004 18:40:44 -0000	1.11
+++ drivers/media/dvb/frontends/sp887x.c	21 Jun 2004 20:34:31 -0000
@@ -5,35 +5,26 @@
 /*
    This driver needs a copy of the Avermedia firmware. The version tested
    is part of the Avermedia DVB-T 1.3.26.3 Application. If the software is
-   installed in Windows the file will be in the /Program Files/AVerTV DVB-T/
+   installed in Windoze the file will be in the /Program Files/AVerTV DVB-T/
    directory and is called sc_main.mc. Alternatively it can "extracted" from
-   the install cab files. Copy this file to '/usr/lib/hotplug/firmware/sc_main.mc'.
+   the install cab files.
+   
+   Copy this file to '/usr/lib/hotplug/firmware/dvb-fe-sp887x.fw'.
+   
    With this version of the file the first 10 bytes are discarded and the
    next 0x4000 loaded. This may change in future versions.
  */
+#define SP887X_DEFAULT_FIRMWARE "dvb-fe-sp887x.fw"
 
-#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/i2c.h>
-
+#include <linux/module.h>
+#include <linux/firmware.h>
 
 #include "dvb_frontend.h"
 #include "dvb_functions.h"
 
-#ifndef DVB_SP887X_FIRMWARE_FILE
-#define DVB_SP887X_FIRMWARE_FILE "/usr/lib/hotplug/firmware/sc_main.mc"
-#endif
-
-static char *sp887x_firmware = DVB_SP887X_FIRMWARE_FILE;
+/* fixme: add this to i2c-id.h */
+#define I2C_DRIVERID_SP887X I2C_DRIVERID_EXP3
 
 #if 0
 #define dprintk(x...) printk(x)
@@ -54,9 +45,7 @@
 #define LOG(dir,addr,buf,len)
 #endif
 
-
-static
-struct dvb_frontend_info sp887x_info = {
+static struct dvb_frontend_info sp887x_info = {
 	.name = "Microtune MT7202DTF",
 	.type = FE_OFDM,
 	.frequency_min =  50500000,
@@ -68,18 +57,19 @@
                 FE_CAN_RECOVER
 };
 
-static int errno;
+struct sp887x_state {
+	struct i2c_adapter *i2c;
+	struct dvb_adapter *dvb;
+};
 
-static
-int i2c_writebytes (struct dvb_frontend *fe, u8 addr, u8 *buf, u8 len)
+static int i2c_writebytes (struct i2c_adapter *i2c, u8 addr, u8 *buf, u8 len)
 {
-	struct dvb_i2c_bus *i2c = fe->i2c;
 	struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = len };
 	int err;
 
 	LOG("i2c_writebytes", msg.addr, msg.buf, msg.len);
 
-	if ((err = i2c->xfer (i2c, &msg, 1)) != 1) {
+	if ((err = i2c_transfer (i2c, &msg, 1)) != 1) {
 		printk ("%s: i2c write error (addr %02x, err == %i)\n",
 			__FUNCTION__, addr, err);
 		return -EREMOTEIO;
@@ -88,19 +78,15 @@
 	return 0;
 }
 
-
-
-static
-int sp887x_writereg (struct dvb_frontend *fe, u16 reg, u16 data)
+static int sp887x_writereg (struct i2c_adapter *i2c, u16 reg, u16 data)
 {
-	struct dvb_i2c_bus *i2c = fe->i2c;
 	u8 b0 [] = { reg >> 8 , reg & 0xff, data >> 8, data & 0xff };
 	struct i2c_msg msg = { .addr = 0x70, .flags = 0, .buf = b0, .len = 4 };
 	int ret;
 
 	LOG("sp887x_writereg", msg.addr, msg.buf, msg.len);
 
-	if ((ret = i2c->xfer(i2c, &msg, 1)) != 1) {
+	if ((ret = i2c_transfer(i2c, &msg, 1)) != 1) {
 		/**
 		 *  in case of soft reset we ignore ACK errors...
 		 */
@@ -117,11 +103,8 @@
 	return 0;
 }
 
-
-static
-u16 sp887x_readreg (struct dvb_frontend *fe, u16 reg)
+static u16 sp887x_readreg (struct i2c_adapter *i2c, u16 reg)
 {
-	struct dvb_i2c_bus *i2c = fe->i2c;
 	u8 b0 [] = { reg >> 8 , reg & 0xff };
 	u8 b1 [2];
 	int ret;
@@ -131,15 +114,13 @@
 	LOG("sp887x_readreg (w)", msg[0].addr, msg[0].buf, msg[0].len);
 	LOG("sp887x_readreg (r)", msg[1].addr, msg[1].buf, msg[1].len);
 
-	if ((ret = i2c->xfer(i2c, msg, 2)) != 2)
+	if ((ret = i2c_transfer(i2c, msg, 2)) != 2)
 		printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
 
 	return (((b1[0] << 8) | b1[1]) & 0xfff);
 }
 
-
-static
-void sp887x_microcontroller_stop (struct dvb_frontend *fe)
+static void sp887x_microcontroller_stop (struct i2c_adapter *fe)
 {
 	dprintk("%s\n", __FUNCTION__);
 	sp887x_writereg(fe, 0xf08, 0x000);
@@ -149,9 +130,7 @@
 	sp887x_writereg(fe, 0xf00, 0x000);
 }
 
-
-static
-void sp887x_microcontroller_start (struct dvb_frontend *fe)
+static void sp887x_microcontroller_start (struct i2c_adapter *fe)
 {
 	dprintk("%s\n", __FUNCTION__);
 	sp887x_writereg(fe, 0xf08, 0x000);
@@ -161,9 +140,7 @@
 	sp887x_writereg(fe, 0xf00, 0x001);
 }
 
-
-static
-void sp887x_setup_agc (struct dvb_frontend *fe)
+static void sp887x_setup_agc (struct i2c_adapter *fe)
 {
 	/* setup AGC parameters */
 	dprintk("%s\n", __FUNCTION__);
@@ -183,72 +160,31 @@
 	sp887x_writereg(fe, 0x303, 0x000);
 }
 
-
 #define BLOCKSIZE 30
-
+#define FW_SIZE 0x4000
 /**
  *  load firmware and setup MPEG interface...
  */
-static
-int sp887x_initial_setup (struct dvb_frontend *fe)
+static int sp887x_initial_setup (struct i2c_adapter *fe, const struct firmware *fw)
 {
 	u8 buf [BLOCKSIZE+2];
-	unsigned char *firmware = NULL;
 	int i;
-	int fd;
-	int filesize;
-	int fw_size;
-	mm_segment_t fs;
+	int fw_size = fw->size;
+	unsigned char *mem = fw->data;
 
 	dprintk("%s\n", __FUNCTION__);
 
+	/* ignore the first 10 bytes, then we expect 0x4000 bytes of firmware */
+	if (fw_size < FW_SIZE+10)
+		return -ENODEV;
+
+	mem = fw->data + 10;
+
 	/* soft reset */
 	sp887x_writereg(fe, 0xf1a, 0x000);
 
 	sp887x_microcontroller_stop (fe);
 
-	fs = get_fs();
-
-	// Load the firmware
-	set_fs(get_ds());
-	fd = open(sp887x_firmware, 0, 0);
-	if (fd < 0) {
-		printk(KERN_WARNING "%s: Unable to open firmware %s\n", __FUNCTION__,
-		       sp887x_firmware);
-		return -EIO;
-	}
-	filesize = lseek(fd, 0L, 2);
-	if (filesize <= 0) {
-		printk(KERN_WARNING "%s: Firmware %s is empty\n", __FUNCTION__,
-		       sp887x_firmware);
-		sys_close(fd);
-		return -EIO;
-	}
-
-	fw_size = 0x4000;
-
-	// allocate buffer for it
-	firmware = vmalloc(fw_size);
-	if (firmware == NULL) {
-		printk(KERN_WARNING "%s: Out of memory loading firmware\n",
-		       __FUNCTION__);
-		sys_close(fd);
-		return -EIO;
-	}
-
-	// read it!
-	// read the first 16384 bytes from the file
-	// ignore the first 10 bytes
-	lseek(fd, 10, 0);
-	if (read(fd, firmware, fw_size) != fw_size) {
-		printk(KERN_WARNING "%s: Failed to read firmware\n", __FUNCTION__);
-		vfree(firmware);
-		sys_close(fd);
-		return -EIO;
-	}
-	sys_close(fd);
-	set_fs(fs);
-
 	printk ("%s: firmware upload... ", __FUNCTION__);
 
 	/* setup write pointer to -1 (end of memory) */
@@ -258,7 +194,7 @@
 	/* dummy write (wrap around to start of memory) */
 	sp887x_writereg(fe, 0x8f0a, 0x0000);
 
-	for (i=0; i<fw_size; i+=BLOCKSIZE) {
+	for (i = 0; i < FW_SIZE; i += BLOCKSIZE) {
 		int c = BLOCKSIZE;
 		int err;
 
@@ -271,18 +207,15 @@
 		buf[0] = 0xcf;
 		buf[1] = 0x0a;
 
-		memcpy(&buf[2], firmware + i, c);
+		memcpy(&buf[2], mem + i, c);
 
 		if ((err = i2c_writebytes (fe, 0x70, buf, c+2)) < 0) {
 			printk ("failed.\n");
 			printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err);
-			vfree(firmware);
 			return err;
 		}
 	}
 
-	vfree(firmware);
-
 	/* don't write RS bytes between packets */
 	sp887x_writereg(fe, 0xc13, 0x001);
 
@@ -308,13 +241,11 @@
 	return 0;
 };
 
-
 /**
  *  returns the actual tuned center frequency which can be used
  *  to initialise the AFC registers
  */
-static
-int tsa5060_setup_pll (struct dvb_frontend *fe, int freq)
+static int tsa5060_setup_pll (struct i2c_adapter *fe, int freq)
 {
 	u8 cfg, cpump, band_select;
 	u8 buf [4];
@@ -341,13 +272,10 @@
 	return (div * 166666 - 36000000);
 }
 
-
-
-static
-int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05)
+static int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05)
 {
 	int known_parameters = 1;
-	
+
 	*reg0xc05 = 0x000;
 
 	switch (p->u.ofdm.constellation) {
@@ -415,13 +343,11 @@
 	return 0;
 }
 
-
 /**
  *  estimates division of two 24bit numbers,
  *  derived from the ves1820/stv0299 driver code
  */
-static
-void divide (int n, int d, int *quotient_i, int *quotient_f)
+static void divide (int n, int d, int *quotient_i, int *quotient_f)
 {
 	unsigned int q, r;
 
@@ -439,9 +365,7 @@
 	}
 }
 
-
-static
-void sp887x_correct_offsets (struct dvb_frontend *fe,
+static void sp887x_correct_offsets (struct i2c_adapter *fe,
 			     struct dvb_frontend_parameters *p,
 			     int actual_freq)
 {
@@ -472,9 +396,7 @@
 	sp887x_writereg(fe, 0x30a, frequency_shift & 0xfff);
 }
 
-
-static
-int sp887x_setup_frontend_parameters (struct dvb_frontend *fe,
+static int sp887x_setup_frontend_parameters (struct i2c_adapter *fe,
 				      struct dvb_frontend_parameters *p)
 {
 	int actual_freq, err;
@@ -484,7 +406,7 @@
 	    p->u.ofdm.bandwidth != BANDWIDTH_7_MHZ &&
 	    p->u.ofdm.bandwidth != BANDWIDTH_6_MHZ)
 		return -EINVAL;
-	
+
 	if ((err = configure_reg0xc05(p, &reg0xc05)))
 		return err;
 
@@ -532,10 +454,11 @@
 	return 0;
 }
 
-
-static
-int sp887x_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
+static int sp887x_ioctl(struct dvb_frontend *f, unsigned int cmd, void *arg)
 {
+	struct sp887x_state *state = (struct sp887x_state *) f->data;
+	struct i2c_adapter *fe = state->i2c;
+
         switch (cmd) {
         case FE_GET_INFO:
 		memcpy (arg, &sp887x_info, sizeof(struct dvb_frontend_info));
@@ -625,10 +548,6 @@
 		break;
 
         case FE_INIT:
-		if (fe->data == NULL) {	  /* first time initialisation... */
-			fe->data = (void*) ~0;
-			sp887x_initial_setup (fe);
-		}
 		/* enable TS output and interface pins */
 		sp887x_writereg(fe, 0xc18, 0x00d);
 		break;
@@ -640,7 +559,7 @@
 	        fesettings->step_size = 0;
 	        fesettings->max_drift = 0;
 	        return 0;
-	}	    
+	}
 
 	default:
 		return -EOPNOTSUPP;
@@ -649,51 +568,132 @@
         return 0;
 }
 
+static struct i2c_client client_template;
 
-
-static
-int sp887x_attach (struct dvb_i2c_bus *i2c, void **data)
+static int attach_adapter(struct i2c_adapter *adapter)
 {
+	struct i2c_client *client;
+	struct sp887x_state *state;
+	const struct firmware *fw;
+	int ret;
+
 	struct i2c_msg msg = {.addr = 0x70, .flags = 0, .buf = NULL, .len = 0 };
 
 	dprintk ("%s\n", __FUNCTION__);
 
-	if (i2c->xfer (i2c, &msg, 1) != 1)
+	if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	if (NULL == (state = kmalloc(sizeof(struct sp887x_state), GFP_KERNEL))) {
+		kfree(client);
+		return -ENOMEM;
+	}
+	state->i2c = adapter;
+
+	if (i2c_transfer (adapter, &msg, 1) != 1) {
+		kfree(state);
+		kfree(client);
                 return -ENODEV;
+	}
 
-	return dvb_register_frontend (sp887x_ioctl, i2c, NULL, &sp887x_info);
-}
+	memcpy(client, &client_template, sizeof(struct i2c_client));
+	client->adapter = adapter;
+	i2c_set_clientdata(client, (void*)state);
+
+	ret = i2c_attach_client(client);
+	if (ret) {
+		kfree(client);
+		kfree(state);
+		return ret;
+	}
+
+	/* request the firmware, this will block until someone uploads it */
+	printk("sp887x: waiting for firmware upload...\n");
+	ret = request_firmware(&fw, SP887X_DEFAULT_FIRMWARE, &client->dev);
+	if (ret) {
+		printk("sp887x: no firmware upload (timeout or file not found?)\n");
+		goto out;
+	}
+
+	ret = sp887x_initial_setup(adapter, fw);
+	if (ret) {
+		printk("sp887x: writing firmware to device failed\n");
+		goto out;
+	}
 
+	return 0;
+out:
+	release_firmware(fw);
+	i2c_detach_client(client);
+	kfree(client);
+	kfree(state);
+	return ret;
+}
 
-static
-void sp887x_detach (struct dvb_i2c_bus *i2c, void *data)
+static int detach_client(struct i2c_client *client)
 {
+	struct sp887x_state *state = (struct sp887x_state*)i2c_get_clientdata(client);
+
 	dprintk ("%s\n", __FUNCTION__);
-	dvb_unregister_frontend (sp887x_ioctl, i2c);
-}
 
+	dvb_unregister_frontend_new (sp887x_ioctl, state->dvb);
+	i2c_detach_client(client);
+	BUG_ON(state->dvb);
+	kfree(client);
+	kfree(state);
+	return 0;
+}
 
-static
-int __init init_sp887x (void)
+static int command (struct i2c_client *client, unsigned int cmd, void *arg)
 {
+	struct sp887x_state *state = (struct sp887x_state*)i2c_get_clientdata(client);
+
 	dprintk ("%s\n", __FUNCTION__);
-	return dvb_register_i2c_device (NULL, sp887x_attach, sp887x_detach);
+
+	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 		= "sp887x",
+	.id 		= I2C_DRIVERID_SP887X,
+	.flags 		= I2C_DF_NOTIFY,
+	.attach_adapter = attach_adapter,
+	.detach_client 	= detach_client,
+	.command 	= command,
+};
+
+static struct i2c_client client_template = {
+	I2C_DEVNAME("sp887x"),
+	.flags 		= I2C_CLIENT_ALLOW_USE,
+	.driver  	= &driver,
+};
 
-static
-void __exit exit_sp887x (void)
+static int __init init_sp887x(void)
 {
-	dprintk ("%s\n", __FUNCTION__);
-	dvb_unregister_i2c_device (sp887x_attach);
+	return i2c_add_driver(&driver);
 }
 
+static void __exit exit_sp887x(void)
+{
+	if (i2c_del_driver(&driver))
+		printk("sp887x: driver deregistration failed\n");
+}
 
 module_init(init_sp887x);
 module_exit(exit_sp887x);
 
-
 MODULE_DESCRIPTION("sp887x DVB-T demodulator driver");
 MODULE_LICENSE("GPL");
 
-

Home | Main Index | Thread Index