[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