[lm-sensors] feedback request: i2c bus and v4l driver for PCRadio Spase-003 (source code)

Szymon Bieganski S.Bieganski at tue.nl
Thu Oct 20 00:18:00 CEST 2005


Hello again,

Done:

/usr/src$> diffstat spase.diff
  i2c/busses/Kconfig            |   11
  i2c/busses/Makefile           |    1
  i2c/busses/i2c-spase.c        |  213 +++++++++++
  media/radio/Kconfig           |    7
  media/radio/Makefile          |    1
  media/radio/radio-spase-i2c.c |  754  
++++++++++++++++++++++++++++++++++++++++++
  6 files changed, 987 insertions(+)
/usr/src$>

Jean, thanks for your tips.

with kind regards
Szymon
-- 
----                                                                                          
----
Szymon Bieganski, PhD candidate S.Bieganski at tue.nl
TU/e EE dept ICS group, (+31) 40 247 3394
----                                                                                          
----

--------------- spase.diff ----------------
diff -urN kernel-source-2.6.8/drivers/i2c/busses/Kconfig  
linux/drivers/i2c/busses/Kconfig
--- kernel-source-2.6.8/drivers/i2c/busses/Kconfig	2004-08-14  
07:37:40.000000000 +0200
+++ linux/drivers/i2c/busses/Kconfig	2005-03-06 18:13:12.000000000 +0100
@@ -70,6 +70,17 @@
  	  This support is also available as a module.  If so, the module
  	  will be called i2c-elektor.

+config I2C_SPASE
+	tristate "PC-Radio ISA card with i2c bus"
+	depends on I2C && ISA && EXPERIMENTAL
+	select I2C_ALGOBIT
+	help
+	  This supports the PC-Radio Spase ISA bus I2C adapter.  Say Y if you own
+	  such an adapter.
+
+	  This support is also available as a module.  If so, the module
+	  will be called i2c-spase.
+
  config I2C_HYDRA
  	tristate "CHRP Apple Hydra Mac I/O I2C interface"
  	depends on I2C && PCI && PPC_CHRP && EXPERIMENTAL
diff -urN kernel-source-2.6.8/drivers/i2c/busses/Makefile  
linux/drivers/i2c/busses/Makefile
--- kernel-source-2.6.8/drivers/i2c/busses/Makefile	2004-08-14  
07:36:14.000000000 +0200
+++ linux/drivers/i2c/busses/Makefile	2005-03-06 18:08:07.000000000 +0100
@@ -32,6 +32,7 @@
  obj-$(CONFIG_I2C_VOODOO3)	+= i2c-voodoo3.o
  obj-$(CONFIG_SCx200_ACB)	+= scx200_acb.o
  obj-$(CONFIG_SCx200_I2C)	+= scx200_i2c.o
+obj-$(CONFIG_I2C_SPASE)	+= i2c-spase.o

  ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
  EXTRA_CFLAGS += -DDEBUG
diff -urN kernel-source-2.6.8/drivers/i2c/busses/i2c-spase.c  
linux/drivers/i2c/busses/i2c-spase.c
--- kernel-source-2.6.8/drivers/i2c/busses/i2c-spase.c	1970-01-01  
01:00:00.000000000 +0100
+++ linux/drivers/i2c/busses/i2c-spase.c	2005-10-19 23:30:39.000000000  
+0200
@@ -0,0 +1,213 @@
+/*  
------------------------------------------------------------------------ *
+ * i2c-spase-bit.c I2C bus present on the board of PCRadio  
Spase-003            *
+ *  
------------------------------------------------------------------------ *
+   Copyright (C) 2004 Szymon Bieganski <S.Bieganski at tue.nl>
+
+   Based on older i2c-parport.c driver and radio-spase.c by Michiel  
Ronsse <ronsse at elis.rug.ac.be>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  
------------------------------------------------------------------------ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>	/* udelay			*/
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <asm/io.h>
+#include <linux/proc_fs.h>    /* /proc interface              */
+
+#define DEFAULT_BASE 0x1B0
+#define SPASE_SCLK	0x00000001
+#define SPASE_SDAT	0x00000002
+#define SPASE_SDAT_OE	0x00000004
+
+/* #define PROC_READ */
+
+#define I2C_HW_B_SPASE	0xff	/* Parallel port Philips style adapter */
+
+static int base;
+static int proc = 1;
+
+static int cur_state;
+
+struct proc_dir_entry *proc_entry;
+
+MODULE_PARM(base, "i");
+MODULE_PARM_DESC(base, "Base I/O address");
+MODULE_PARM(proc, "i");
+MODULE_PARM_DESC(proc, "enable /proc/i2c status entry  (set to 1 to  
enable)");
+
+static inline void port_write(unsigned char d)
+{
+	outb(d, base);
+}
+
+static inline unsigned char port_read(void)
+{
+	return inb(base);
+}
+
+/* ----- Unified line operation functions  
--------------------------------- */
+
+static inline void line_set(int state, unsigned char d)
+{
+	if(state)
+		cur_state |= d;
+	else
+		cur_state &= ~d;
+
+	port_write(cur_state);
+}
+
+static inline int line_get(void)
+{
+	return (SPASE_SDAT_OE & port_read()) == SPASE_SDAT_OE;
+}
+
+/* ----- I2C algorithm call-back functions and structures  
----------------- */
+
+static void spase_setscl(void *data, int state)
+{
+	line_set(state, SPASE_SCLK);
+}
+
+static void spase_setsda(void *data, int state)
+{
+	line_set(state, SPASE_SDAT);
+}
+
+static int spase_getsda(void *data)
+{
+	return line_get();
+}
+
+/* Encapsulate the functions above in the correct structure
+	 Note that getscl will be set to NULL by the attaching code for adapters
+	 that cannot read SCL back */
+static struct i2c_algo_bit_data spase_algo_data = {
+	.setsda		= spase_setsda,
+	.setscl		= spase_setscl,
+	.getsda		= spase_getsda,
+	.getscl		= NULL, /* spase_getscl,*/
+	.udelay		= 5,
+	.mdelay		= 50,
+	.timeout		= HZ,
+};
+
+/* ----- I2c structure  
---------------------------------------------------- */
+
+static struct i2c_adapter spase_adapter = {
+	.owner		= THIS_MODULE,
+	.class		= I2C_CLASS_HWMON,
+	.id		= -1, /* FIXME !!!!! I2C_HW_B_SPASE, */
+	.algo_data	= &spase_algo_data,
+	I2C_DEVNAME("PCRadio Spase-003 adapter"),
+};
+
+/*---------------------------------------------------------------------*/
+
+static int read_proc(char *buf, char **start, off_t offset,
+		     int len, int *eof, void *data )
+{
+	*start = 0;
+	*eof   = 1;
+
+	return sprintf(buf,
+		"Type:       SPASE PCRadio-003 i2c port device\n"
+		"IO-port: 0x%x\n"
+		"\n"
+		"state:   0x%x\n"
+		"  SDA   %s   |\\           \n"
+		"      ______| \\_____ ____  \n"
+		"            | /     T     \n"
+		"            |/      |     \n"
+		"  SDA   %s     /|    |     \n"
+		"      _______/ |____|    \n"
+		"             \\ |         \n"
+		"              \\|         \n"
+		"                         \n"
+		"  SCL   %s   |\\           \n"
+		"      ______| \\_____     \n"
+		"            | /          \n"
+		"            |/           \n"
+		"SDA line:       %s -> %s\n"
+		"SCL line:       %s\n",
+		base,
+		cur_state,
+		(cur_state & SPASE_SDAT)?("H"):("L"),
+#ifdef PROC_READ
+		(line_get()?("H"):("L")),
+#else
+		"*",
+#endif
+		(cur_state & SPASE_SCLK)?("H"):("L"),
+		(cur_state & SPASE_SDAT)?("H"):("L"),
+#ifdef PROC_READ
+		(line_get()?("H"):("L")),
+#else
+		"*",
+#endif
+		(cur_state & SPASE_SCLK)?("H"):("L")
+		);
+}
+
+/* ----- Module loading, unloading and information  
------------------------ */
+
+static int __init i2c_spasebit_init(void)
+{
+	if (base == 0) {
+		printk(KERN_INFO "i2c-spase: using default base 0x%x\n", DEFAULT_BASE);
+		base = DEFAULT_BASE;
+	}
+
+	if (!request_region(base, 1, "i2c-spase"))
+	return -EBUSY;
+
+	cur_state = 0;
+	/* Reset hardware to a sane state (SCL and SDA high) */
+	spase_setsda(NULL, 1);
+	spase_setscl(NULL, 1);
+	/* Other init if needed (power on...) */
+
+	if (i2c_bit_add_bus(&spase_adapter) < 0) {
+		printk(KERN_ERR "i2c-spase: Unable to register with I2C\n");
+		release_region(base, 1);
+		return -ENODEV;
+	}
+
+	if(proc)
+		if((proc_entry = create_proc_entry("i2c", 0, NULL )))
+			proc_entry->read_proc = read_proc;
+
+	return 0;
+}
+
+static void __exit i2c_spasebit_exit(void)
+{
+	i2c_bit_del_bus(&spase_adapter);
+	if (proc_entry)
+		remove_proc_entry("i2c", NULL);
+	release_region(base, 1);
+}
+
+MODULE_AUTHOR("Szymon Bieganski <S.Bieganski at tue.nl>");
+MODULE_DESCRIPTION("I2C bus on PC-Radio Spase-003 board");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_spasebit_init);
+module_exit(i2c_spasebit_exit);
diff -urN kernel-source-2.6.8/drivers/media/radio/Kconfig  
linux/drivers/media/radio/Kconfig
--- kernel-source-2.6.8/drivers/media/radio/Kconfig	2004-08-14  
07:37:15.000000000 +0200
+++ linux/drivers/media/radio/Kconfig	2005-02-26 17:26:13.000000000 +0100
@@ -49,6 +49,13 @@
  	  To compile this driver as a module, choose M here: the
  	  module will be called radio-aimslab.

+config RADIO_SPASE
+	tristate "Radio Spase support (EXPERIMENTAL)"
+	depends on ISA && VIDEO_DEV && EXPERIMENTAL
+	---help---
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-spase.
+
  config RADIO_RTRACK_PORT
  	hex "RadioTrack i/o port (0x20f or 0x30f)"
  	depends on RADIO_RTRACK=y
diff -urN kernel-source-2.6.8/drivers/media/radio/Makefile  
linux/drivers/media/radio/Makefile
--- kernel-source-2.6.8/drivers/media/radio/Makefile	2004-08-14  
07:37:14.000000000 +0200
+++ linux/drivers/media/radio/Makefile	2005-03-06 22:20:13.000000000 +0100
@@ -20,3 +20,4 @@
  obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o
  obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
  obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
+obj-$(CONFIG_RADIO_SPASE) += radio-spase-i2c.o
diff -urN kernel-source-2.6.8/drivers/media/radio/radio-spase-i2c.c  
linux/drivers/media/radio/radio-spase-i2c.c
--- kernel-source-2.6.8/drivers/media/radio/radio-spase-i2c.c	1970-01-01  
01:00:00.000000000 +0100
+++ linux/drivers/media/radio/radio-spase-i2c.c	2005-10-19  
23:54:22.000000000 +0200
@@ -0,0 +1,754 @@
+/* Spase driver for Linux radio support (C) 2004 Szymon Bieganski
+ * S.Bieganski at tue.nl
+ *
+ * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
+ *  and radio-spase.c by Michiel Ronsse <ronsse at elis.rug.ac.be>
+ *
+ */
+
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <linux/config.h>	/* CONFIG_RADIO_RTRACK2_PORT 	*/
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>    /* /proc interface              */
+
+/* #include <linux/i2c-dev.h> */
+#include <linux/i2c.h>
+
+#ifndef CONFIG_RADIO_SPASE_PORT
+#define CONFIG_RADIO_SPASE_PORT 0
+#endif
+
+static int io = CONFIG_RADIO_SPASE_PORT;
+static int volume_max = -1; /* unrestricted */
+static int volume_rear = 0;
+static int radio_nr = -1;
+static int display = 0;
+static spinlock_t lock;
+
+struct proc_dir_entry *proc_entry;
+static int proc = 1;
+
+struct i2c_adapter* spase_i2c_adap = 0;
+struct i2c_client* sound = 0;
+struct i2c_client* synth = 0;
+struct i2c_client* fmif = 0;
+
+struct sp_device
+{
+	unsigned int port;
+	// in v4l units (0 ... 65535)
+	unsigned int cur_vol;
+	unsigned int cur_bass;
+	unsigned int cur_treble;
+	// in v4l units and range 0 - left; 65535 - right
+	unsigned int cur_bal;
+	// in 1/16th of kHz steps
+	unsigned long cur_freq;
+	unsigned int mode_muted;
+	/*   unsigned int mode_stereo; */
+};
+
+static struct sp_device spase_unit;
+
+/* local things */
+
+#define FALSE 0
+#define TRUE  1
+
+#define SOUND_ADDRESS 64 /* 0x80 -> 0x40 = 64 */
+#define SYNTH_ADDRESS 98 /* 0xC4 -> 0x62 = 98 */
+#define FMIF_ADDRESS  97 /* 0xC2 -> 0x61 = 97 */
+
+#define STEREO 0x60
+#define MONO   0x64
+
+#define MUTE   0x80
+#define NOMUTE 0x00
+
+// in kHz units
+#define IF_FREQ (10700)
+#define LOW_RANGE   (87500)
+#define HIGH_RANGE (108000)
+
+/*---------------------------------------------------------------------*/
+static void Rotate(unsigned char* byte)
+{
+	int i;
+	unsigned char c; c = *byte & 0xff;
+	*byte = 0;
+
+	for(i = 7; i; --i) {
+		if(c & 0x1)
+			*byte |= 0x1;
+
+		*byte <<= 1;
+		c >>= 1;
+	}
+
+	if(c & 0x1)
+		*byte |= 0x1;
+}
+
+static int GetTuningInfo(char * Level, char * Stereo, int * Deviation)
+{
+	char buf[2];
+
+	int error = 0;
+
+	error = i2c_master_recv(fmif, buf, 2);
+
+	if(error != 2)
+		return -1;
+	
+	/* just to help computation -> this IC returns values which are  
transferred by I2C LSB-first */
+	Rotate(&buf[0]);
+	Rotate(&buf[1]);
+
+	if(Stereo) {
+#if 1
+		*Stereo = (buf[1] == 127);  /* 0..1     */
+#else
+		*Stereo = ((buf[0] & 0x0F) >> 1) > 5;
+#endif
+	}
+
+	if(Level)
+		*Level = (buf[0] & 0x0F) >> 1;         /* 0..7     */
+
+	if(Deviation)
+		*Deviation = (buf[1] - 127) >> 1;          /* -64..+64 */
+
+/*   printk(KERN_INFO "spase: MP %d/ Lev %d/ Dev %d.\n", (buf[0] & 0xF0)  
>> 4, buf[0] & 0x0F, buf[1] >> 1); */
+
+	return error;
+}
+
+/* Layer 3: spase layer ************************************************/
+
+int Sound(int Mode)
+{
+	char buf[2];
+	buf[0] = 0x05;
+	buf[1] = Mode ? NOMUTE : MUTE;
+
+	if(2 != i2c_master_send(sound, buf, 2))
+		return -1;
+	return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+int SetStereo(int Stereo)
+{
+	// it does NOTHING here
+	return 0;
+
+	char buf[2];
+	buf[0] = 0x02;
+	buf[1] = Stereo ? STEREO : MONO;
+
+	if(2 != i2c_master_send(synth, buf, 2))
+		return -1;
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+int SetFrequency(unsigned long Frequency)
+{
+	unsigned long DividingNumber = Frequency / 10;
+	DividingNumber += IF_FREQ / 10;
+	char buf[6];
+
+/*   DividingNumber = (unsigned long)(200*Frequency + 2140); */
+	buf[0] = 0x0;
+	buf[1] = 1 | (DividingNumber << 1);      /* set the LSB */
+	buf[2] = DividingNumber >> 7;
+	/* Set 10kHz stepsize, FM Band */
+	buf[3] = STEREO | ((DividingNumber >> 15) & 3);
+
+	if(4 != i2c_master_send(synth, buf, 4))
+		return -1;
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+int SetAudio(unsigned int v4l_Volume, unsigned int v4l_Balance, unsigned  
int v4l_Treble, unsigned int v4l_Bass)
+{
+	char buf[6];
+
+	int half = 1 << 4;
+/*   int full = 1 << 5; */
+/*   int quarter = 1 << 3; */
+
+	int Dummy,
+		Volume = v4l_Volume >> 10,
+		Balance = (v4l_Balance >> 11) - half,
+		Treble = (v4l_Treble >> 12),
+		Bass = (v4l_Bass >> 12);
+	
+	/* volume :
+	   63: +20db
+	   62: +18db
+	   ...
+	   53: 0 db
+	   52: -2db
+	   ...
+	   20: -66db
+	   <20: mute
+	*/
+
+	/* volume */
+	if(Volume < 0)
+		Volume = 0;
+	if(Volume > volume_max)
+		Volume = volume_max;
+
+	/* mapping volume/balance to volume_left/volume_right */
+	buf[1] = Volume;       /* Left volume:  0..63 */
+	buf[2] = Volume;       /* Right volume: 0..63 */
+
+	Dummy = (abs(Balance) * Volume) / volume_max;
+	/*   Dummy = (Dummy < 0) ? 0 : Dummy; */
+	/*   Dummy = (Dummy > full) ? full : Dummy; */
+
+	if(Balance < 0)
+		buf[1] = buf[1] - abs(Dummy);
+	if(buf[1] < 0)
+		buf[1] = 0;
+
+	if(Balance > 0)
+		buf[2] = buf[2] - abs(Dummy);
+	if(buf[2] < 0)
+		buf[2] = 0;
+
+	/* bass */
+	/*   Bass += 7;  */
+	/* 3..11 */
+	if(Bass < 3)
+		Bass = 3;
+	if(Bass > 11)
+		Bass = 11;
+	buf[3] = Bass;
+
+	/* treble */
+	/*   Treble += 7; */
+	/* 3..11 */
+	if(Treble < 3)
+		Treble = 3;
+	if(Treble > 11)
+		Treble = 11;
+	buf[4] = Treble;
+
+#if 0
+	buf[0] = 0x0;
+#else
+	buf[0] = 0x0;
+	buf[5] += volume_rear;
+	buf[5] += 0; /* 4th bit -> select rear fader */
+	buf[5] += 32; /* 5th bit -> enable fader */
+#endif
+
+	if(6 != i2c_master_send(sound, buf, 6))
+		return -1;
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+static int InitRadio(void)
+{
+	char buf[2];
+	int ret = 0;
+
+	buf[0] = 0xFE;
+	if(1 != (ret = i2c_master_send(fmif, buf, 1)) ) {
+		printk(KERN_INFO "spase: fmif chip initialization failed (returned  
0x%x).\n", ret);
+		return -EINVAL;
+	}
+
+	buf[0] = 0x04;
+	buf[1] = 0x0;
+	/* 4th byte -> fader */
+	if(volume_rear > 0) {
+		buf[1] += volume_rear;
+		buf[1] += 0; /* 4th bit -> select rear fader */
+		buf[1] += 32; /* 5th bit -> enable fader */
+		int ret = buf[1];
+		printk(KERN_INFO "spase: fader driven with value 0x%x.\n", ret);
+		if(2 != (ret = i2c_master_send(sound, buf, 2)) ) {
+			printk(KERN_INFO "spase: sound chip initialization failed (returned  
0x%x).\n", ret);
+			return -EINVAL;
+		}
+	}
+	/*   else */
+	/*     buf[1] = 0xFF; */
+
+	SetStereo(TRUE);
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+static int sp_do_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct sp_device *sp = dev->priv;
+
+	switch(cmd) {
+	case VIDIOCGCAP: {
+		struct video_capability *v = arg;
+		memset(v,0,sizeof(*v));
+		v->type=VID_TYPE_TUNER;
+		v->channels=1;
+		v->audios=1;
+		strcpy(v->name, "SPASE PCRadio-003");
+		return 0;
+	}
+	case VIDIOCGTUNER: {
+		struct video_tuner *v = arg;
+		if(v->tuner)	/* Only 1 tuner */
+			return -EINVAL;
+		char stereo, signal;
+		GetTuningInfo(&signal, &stereo, (int*)NULL /*deviation*/ );
+		v->signal = (signal * 65536) / 7;
+		v->rangelow  = LOW_RANGE * 16;
+		v->rangehigh = HIGH_RANGE * 16;
+		v->flags = VIDEO_TUNER_LOW | (stereo ? VIDEO_TUNER_STEREO_ON : 0);
+		v->mode = VIDEO_MODE_AUTO;
+		strcpy(v->name, "FM");
+		return 0;
+	}
+	case VIDIOCSTUNER: {
+		struct video_tuner *v = arg;
+		if(v->tuner!=0)
+			return -EINVAL;
+		/* Only 1 tuner so no setting needed ! */
+		return 0;
+	}
+	case VIDIOCGFREQ: {
+		unsigned long *freq = arg;
+		*freq = sp->cur_freq;
+		return 0;
+	}
+	case VIDIOCSFREQ: {
+		unsigned long *freq = arg;
+		sp->cur_freq = *freq;
+/* 		  Sound(FALSE); */
+		SetFrequency(sp->cur_freq / 16);
+/* 		  Sound(TRUE); */
+		return 0;
+	}
+	case VIDIOCGAUDIO: {
+		struct video_audio *v = arg;
+		memset(v,0, sizeof(*v));
+		v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME
+			| VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE /*| VIDEO_AUDIO_BALANCE */;
+		v->flags  |= sp->mode_muted ? VIDEO_AUDIO_MUTE : 0;
+		v->volume = sp->cur_vol;
+		v->balance = sp->cur_bal;
+		v->bass    = sp->cur_bass;
+		v->treble  = sp->cur_treble;
+		v->step = 1024; //65535/64
+#if 1
+		char stereo;
+		GetTuningInfo((char*)NULL, &stereo, (int*)NULL);
+		v->mode    = stereo ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
+#else
+		v->mode    = sp->mode_stereo ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
+#endif
+		strcpy(v->name, "Radio");
+		return 0;
+	}
+	case VIDIOCSAUDIO: {
+		struct video_audio *v = arg;
+		if(v->audio)
+			return -EINVAL;
+		if(v->flags & VIDEO_AUDIO_MUTE) {
+			sp->mode_muted = TRUE;
+			Sound(FALSE);
+		} else {
+			sp->mode_muted = FALSE;
+			sp->cur_vol    = v->volume;
+			sp->cur_bass   = v->bass;
+			sp->cur_treble = v->treble;
+			sp->cur_bal    = v->balance;
+			SetAudio(v->volume,
+				 v->balance,
+				 v->treble,
+				 v->bass);
+			Sound(TRUE);
+		}
+/*  		  char stereo; */
+/*  		  GetTuningInfo((char*)NULL, &stereo, (int*)NULL); */
+/*  		  sp->mode_stereo = (v->mode & VIDEO_SOUND_STEREO) ? (stereo ? TRUE  
: FALSE) : FALSE; */
+/* 	SetStereo(sp->mode_stereo); */
+		return 0;
+	}
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+static int sp_ioctl(struct inode *inode, struct file *file,
+		    unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, sp_do_ioctl);
+}
+
+#if 0
+static int sp_close(struct inode *inode, struct file *file)
+{
+	struct  video_device *dev = file->private_data;
+	struct cam_data *cam = dev->priv;
+
+	if(cam->proc_entry)
+		cam->proc_entry->uid = 0;
+	return 0;
+}
+#endif
+
+static struct file_operations spase_fops = {
+	.owner          = THIS_MODULE,
+	.open           = video_exclusive_open,
+	.release        = video_exclusive_release, /* sp_close, */
+	.ioctl          = sp_ioctl,
+	.llseek         = no_llseek,
+};
+
+static struct video_device spase_radio = {
+	.owner        = THIS_MODULE,
+	.name	        = "SPASE PCRadio-003",
+	.type	        = VID_TYPE_TUNER,
+	.hardware     = VID_HARDWARE_RTRACK2, /* just borrowed ;) */
+	.fops         = &spase_fops,
+};
+
+/*---------------------------------------------------------------------*/
+
+static int read_proc(char *buf, char **start, off_t offset,
+		     int len, int *eof, void *data )
+{
+	*start = 0;
+	*eof   = 1;
+
+	char stereo, signal; int deviation;
+	GetTuningInfo(&signal, &stereo, &deviation);
+
+	deviation += 64;
+	int megas = (int)(spase_unit.cur_freq/16000);
+	int kilos = (int)(spase_unit.cur_freq/16 - 1000 * megas);
+	int h_kilos = (int)(kilos / 100);
+	int t_kilos = (int)((kilos / 10) - (h_kilos * 10));
+	int u_kilos = (int)(kilos - (t_kilos * 10) - (h_kilos * 100));
+	return sprintf(buf,
+		       "Type:       SPASE PCRadio-003\n"
+		       "I2C-port:   %s\n"
+		       "Frequency:  %3d.%1d%1d%1d MHz\n"
+		       "Volume:     %d%%\n"
+		       "Balance:    %d%% [%.*s^%.*s]\n"
+		       "Bass:       %d%% [%.*s^%.*s]\n"
+		       "Treble:     %d%% [%.*s^%.*s]\n"
+		       "Power:      %s\n\n"
+		       "Signal: %s%s%s  %3d%% [%.*s%.*s]\n",
+		       spase_i2c_adap->name,
+		       megas, h_kilos, t_kilos, u_kilos,
+		       (spase_unit.cur_vol * 100)/65536,
+		       (spase_unit.cur_bal * 100)/65536,
+		       (spase_unit.cur_bal * 10)/65536,      "................",
+		       10 - (spase_unit.cur_bal * 10)/65536, "................",
+		       (spase_unit.cur_bass * 100)/65536,
+		       (spase_unit.cur_bass * 10)/65536,      "................",
+		       10 - (spase_unit.cur_bass * 10)/65536, "................",
+		       (spase_unit.cur_treble * 100)/65536,
+		       (spase_unit.cur_treble * 10)/65536,      "................",
+		       10 - (spase_unit.cur_treble * 10)/65536, "................",
+		       spase_unit.mode_muted ? "off" : "on",
+		       stereo ? "O" : " ",
+		       signal > 3 ? "-" : " ",
+		       stereo ? "O" : " ",
+		       (signal * 100) / 7,
+		       (signal << 1),     "################",
+		       (7 - signal) << 1, "................"
+		);
+}
+
+/*---------------------------------------------------------------------*/
+
+static int spase_attach_adapter(struct i2c_adapter *adapter)
+{
+	return 0; /* I know it is dangerous but check if the chip is actually  
connected is performed anyway */
+}
+
+static int spase_detach_client(struct i2c_client *client)
+{
+	int err;
+	if((err = i2c_detach_client(client))) {
+		printk("spase: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+	kfree(client);
+	return 0;
+}
+
+struct i2c_driver sound_driver = {
+	.owner          = THIS_MODULE,
+	.name = "sound",
+	.id = -1,
+	.flags = I2C_DF_NOTIFY,
+	.attach_adapter = &spase_attach_adapter,
+	.detach_client  = &spase_detach_client,
+	.command        = NULL
+};
+
+struct i2c_driver synth_driver = {
+	.owner          = THIS_MODULE,
+	.name = "synth",
+	.id = -1,
+	.flags = I2C_DF_NOTIFY,
+	.attach_adapter = &spase_attach_adapter,
+	.detach_client  = &spase_detach_client,
+	.command        = NULL
+};
+
+struct i2c_driver fmif_driver = {
+	.owner          = THIS_MODULE,
+	.name = "fmif", /* !!!!! dont use slash in the name field !!!! */
+	.id = -1,
+	.flags = I2C_DF_NOTIFY,
+	.attach_adapter = &spase_attach_adapter,
+	.detach_client  = &spase_detach_client,
+	.command        = NULL
+};
+
+struct i2c_driver display_driver = {
+	.owner          = THIS_MODULE,
+	.name = "display", /* !!!!! dont use slash in the name field !!!! */
+	.id = -1,
+	.flags = I2C_DF_NOTIFY,
+	.attach_adapter = &spase_attach_adapter,
+	.detach_client  = &spase_detach_client,
+	.command        = NULL
+};
+
+static struct i2c_client client_template =
+{
+	I2C_DEVNAME("(chip unset)"),
+	.flags      = I2C_CLIENT_ALLOW_USE,
+        .driver     = &sound_driver,
+};
+
+static int __init spase_init(void)
+{
+	int r = request_module("i2c-spase");
+	if(r < 0) {
+		printk(KERN_INFO "spase: i2c adapter module is not loadable (returned  
0x%x).\n", r);
+		return -EINVAL;
+	}
+	spase_i2c_adap = i2c_get_adapter(io);
+
+	if(spase_i2c_adap != 0) {
+		if(i2c_adapter_id(spase_i2c_adap) == -1) {
+			i2c_put_adapter(spase_i2c_adap);
+			printk(KERN_INFO "spase: adapter 0x%x was not registered.\n", io);
+			return -EINVAL;
+		}
+	} else {
+		printk(KERN_INFO "spase: i2c bus driver 0x%x was not loaded.\n", io);
+		return -EINVAL;
+	}
+
+	i2c_add_driver(&sound_driver);
+
+	client_template.adapter = spase_i2c_adap;
+
+	client_template.addr = SOUND_ADDRESS;
+	client_template.driver = &sound_driver;
+	sprintf(client_template.name, "tea6310t sound processor");
+	if (NULL == (sound = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)))
+		return -ENOMEM;
+	memcpy(sound, &client_template, sizeof(struct i2c_client));
+
+	if(i2c_attach_client(sound) == -EBUSY) {
+		printk(KERN_INFO "spase: SOUND chip not detected at address 0x%x.\n",  
SOUND_ADDRESS);
+		i2c_del_driver(&sound_driver);
+
+		i2c_put_adapter(spase_i2c_adap);
+		return -ENODEV;
+	}
+
+	i2c_add_driver(&synth_driver);
+
+	client_template.addr = SYNTH_ADDRESS;
+	client_template.driver = &synth_driver;
+	sprintf(client_template.name, "tsa6057 PLL synth");
+	if (NULL == (synth = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)))
+		return -ENOMEM;
+	memcpy(synth, &client_template, sizeof(struct i2c_client));
+
+	if(i2c_attach_client(synth) == -EBUSY) {
+		if(sound)
+			i2c_detach_client(sound);
+		i2c_del_driver(&sound_driver);
+
+		printk(KERN_INFO "spase: SYNTH chip not detected at address 0x%x.\n",  
SYNTH_ADDRESS);
+		i2c_del_driver(&synth_driver);
+
+		i2c_put_adapter(spase_i2c_adap);
+		return -ENODEV;
+	}
+
+	i2c_add_driver(&fmif_driver);
+
+	client_template.addr = FMIF_ADDRESS;
+	client_template.driver = &fmif_driver;
+	sprintf(client_template.name, "tea6100 FM/IF");
+	if (NULL == (fmif = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)))
+		return -ENOMEM;
+	memcpy(fmif, &client_template, sizeof(struct i2c_client));
+
+	if(i2c_attach_client(fmif) == -EBUSY) {
+		if(synth)
+			i2c_detach_client(synth);
+		i2c_del_driver(&synth_driver);
+
+		if(sound)
+			i2c_detach_client(sound);
+		i2c_del_driver(&sound_driver);
+
+		printk(KERN_INFO "spase: FMIF chip not detected at address 0x%x.\n",  
FMIF_ADDRESS);
+		i2c_del_driver(&fmif_driver);
+
+		i2c_put_adapter(spase_i2c_adap);
+		return -ENODEV;
+	}
+
+/*   printk(KERN_INFO "spase: FMIF (0x%x), SYNTH (0x%x) and SOUND (0x%x)  
chips detected.\n", FMIF_ADDRESS, SYNTH_ADDRESS, SOUND_ADDRESS); */
+	spase_radio.priv = &spase_unit;
+
+	spin_lock_init(&lock);
+	if(video_register_device(&spase_radio, VFL_TYPE_RADIO, radio_nr) == -1) {
+		if(fmif)
+			i2c_detach_client(fmif);
+		i2c_del_driver(&fmif_driver);
+
+		if(synth)
+			i2c_detach_client(synth);
+		i2c_del_driver(&synth_driver);
+
+		if(sound)
+			i2c_detach_client(sound);
+		i2c_del_driver(&sound_driver);
+
+		i2c_put_adapter(spase_i2c_adap);
+		return -EINVAL;
+	}
+
+	spase_unit.mode_muted = TRUE;
+/* 	spase_unit.mode_stereo = TRUE; */
+	spase_unit.port = io;
+
+	if (InitRadio() == -EINVAL) {
+		video_unregister_device(&spase_radio);
+		if(fmif)
+			i2c_detach_client(fmif);
+		i2c_del_driver(&fmif_driver);
+
+		if(synth)
+			i2c_detach_client(synth);
+		i2c_del_driver(&synth_driver);
+
+		if(sound)
+			i2c_detach_client(sound);
+		i2c_del_driver(&sound_driver);
+		return -EINVAL;
+	}
+
+	printk(KERN_INFO "Spase-003 PCRadio card registered at I2C bus  
\"%s\".\n", spase_i2c_adap->name);
+
+	if(volume_max < 0)
+		volume_max = 63;
+	else {
+		if(volume_max > 63)
+			volume_max = 63;
+		printk(KERN_ERR "By the user request volume_max set to %d\n",  
volume_max);
+	}
+
+	if(volume_rear > 15)
+		volume_rear = 15;
+
+	if(volume_rear < 0)
+		volume_rear = 0;
+
+	printk(KERN_ERR "By the user request volume_rear set to %d\n",  
volume_rear);
+
+	if(proc)
+		if((proc_entry = create_proc_entry( "radio", 0444, NULL )))
+			proc_entry->read_proc = read_proc;
+
+	spase_unit.cur_vol = 0;
+	spase_unit.cur_bass = (1 << 15);
+	spase_unit.cur_treble = (1 << 15);
+	spase_unit.cur_bal = (1 << 15);
+
+	SetAudio(spase_unit.cur_vol, spase_unit.cur_bal, spase_unit.cur_treble,  
spase_unit.cur_bass);
+
+	return 0;
+}
+
+MODULE_AUTHOR("Szymon Bieganski <S.Bieganski at tue.nl>");
+MODULE_DESCRIPTION("A driver for the PCRadio Spase radio card.");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "I2C bus address (0, 1, 2 ...)");
+
+MODULE_PARM(radio_nr, "i");
+
+MODULE_PARM(volume_max, "i");
+MODULE_PARM_DESC(volume_max, "Maximum volume for Philips TEA6310T audio  
processor (0..63)");
+
+MODULE_PARM(volume_rear, "i");
+MODULE_PARM_DESC(volume_rear, "The volume of extra rear output of Philips  
TEA6310T audio processor (0..15)");
+
+MODULE_PARM(proc, "i");
+MODULE_PARM_DESC(proc, "Enable /proc/radio entry (set to 1 to enable)");
+
+static void __exit spase_cleanup_module(void)
+{
+	video_unregister_device(&spase_radio);
+	if(proc_entry)
+		remove_proc_entry("radio", NULL);
+
+	if(fmif)
+		i2c_detach_client(fmif);
+	i2c_del_driver(&fmif_driver);
+
+	if(synth)
+		i2c_detach_client(synth);
+	i2c_del_driver(&synth_driver);
+
+	if(sound)
+		i2c_detach_client(sound);
+	i2c_del_driver(&sound_driver);
+
+	if(spase_i2c_adap != 0)
+		i2c_put_adapter(spase_i2c_adap);
+}
+
+module_init(spase_init);
+module_exit(spase_cleanup_module);
+
+/*
+  Local variables:
+  compile-command: "mmake"
+  End:
+*/




More information about the lm-sensors mailing list