diff options
Diffstat (limited to 'drivers/media/radio/radio-terratec.c')
-rw-r--r-- | drivers/media/radio/radio-terratec.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c new file mode 100644 index 000000000..8b8ce2b46 --- /dev/null +++ b/drivers/media/radio/radio-terratec.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Terratec ActiveRadio ISA Standalone card driver for Linux radio support + * (c) 1999 R. Offermanns (rolf@offermanns.de) + * based on the aimslab radio driver from M. Kirkwood + * many thanks to Michael Becker and Friedhelm Birth (from TerraTec) + * + * + * History: + * 1999-05-21 First preview release + * + * Notes on the hardware: + * There are two "main" chips on the card: + * - Philips OM5610 (http://www-us.semiconductors.philips.com/acrobat/datasheets/OM5610_2.pdf) + * - Philips SAA6588 (http://www-us.semiconductors.philips.com/acrobat/datasheets/SAA6588_1.pdf) + * (you can get the datasheet at the above links) + * + * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); + * Volume Control is done digitally + * + * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@kernel.org> + */ + +#include <linux/module.h> /* Modules */ +#include <linux/init.h> /* Initdata */ +#include <linux/ioport.h> /* request_region */ +#include <linux/videodev2.h> /* kernel radio structs */ +#include <linux/mutex.h> +#include <linux/io.h> /* outb, outb_p */ +#include <linux/slab.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include "radio-isa.h" + +MODULE_AUTHOR("R. Offermans & others"); +MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card."); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1.99"); + +/* Note: there seems to be only one possible port (0x590), but without + hardware this is hard to verify. For now, this is the only one we will + support. */ +static int io = 0x590; +static int radio_nr = -1; + +module_param(radio_nr, int, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device number"); + +#define WRT_DIS 0x00 +#define CLK_OFF 0x00 +#define IIC_DATA 0x01 +#define IIC_CLK 0x02 +#define DATA 0x04 +#define CLK_ON 0x08 +#define WRT_EN 0x10 + +static struct radio_isa_card *terratec_alloc(void) +{ + return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL); +} + +static int terratec_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) +{ + int i; + + if (mute) + vol = 0; + vol = vol + (vol * 32); /* change both channels */ + for (i = 0; i < 8; i++) { + if (vol & (0x80 >> i)) + outb(0x80, isa->io + 1); + else + outb(0x00, isa->io + 1); + } + return 0; +} + + +/* this is the worst part in this driver */ +/* many more or less strange things are going on here, but hey, it works :) */ + +static int terratec_s_frequency(struct radio_isa_card *isa, u32 freq) +{ + int i; + int p; + int temp; + long rest; + unsigned char buffer[25]; /* we have to bit shift 25 registers */ + + freq = freq / 160; /* convert the freq. to a nice to handle value */ + memset(buffer, 0, sizeof(buffer)); + + rest = freq * 10 + 10700; /* I once had understood what is going on here */ + /* maybe some wise guy (friedhelm?) can comment this stuff */ + i = 13; + p = 10; + temp = 102400; + while (rest != 0) { + if (rest % temp == rest) + buffer[i] = 0; + else { + buffer[i] = 1; + rest = rest - temp; + } + i--; + p--; + temp = temp / 2; + } + + for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */ + if (buffer[i] == 1) { + outb(WRT_EN | DATA, isa->io); + outb(WRT_EN | DATA | CLK_ON, isa->io); + outb(WRT_EN | DATA, isa->io); + } else { + outb(WRT_EN | 0x00, isa->io); + outb(WRT_EN | 0x00 | CLK_ON, isa->io); + } + } + outb(0x00, isa->io); + return 0; +} + +static u32 terratec_g_signal(struct radio_isa_card *isa) +{ + /* bit set = no signal present */ + return (inb(isa->io) & 2) ? 0 : 0xffff; +} + +static const struct radio_isa_ops terratec_ops = { + .alloc = terratec_alloc, + .s_mute_volume = terratec_s_mute_volume, + .s_frequency = terratec_s_frequency, + .g_signal = terratec_g_signal, +}; + +static const int terratec_ioports[] = { 0x590 }; + +static struct radio_isa_driver terratec_driver = { + .driver = { + .match = radio_isa_match, + .probe = radio_isa_probe, + .remove = radio_isa_remove, + .driver = { + .name = "radio-terratec", + }, + }, + .io_params = &io, + .radio_nr_params = &radio_nr, + .io_ports = terratec_ioports, + .num_of_io_ports = ARRAY_SIZE(terratec_ioports), + .region_size = 2, + .card = "TerraTec ActiveRadio", + .ops = &terratec_ops, + .has_stereo = true, + .max_volume = 10, +}; + +static int __init terratec_init(void) +{ + return isa_register_driver(&terratec_driver.driver, 1); +} + +static void __exit terratec_exit(void) +{ + isa_unregister_driver(&terratec_driver.driver); +} + +module_init(terratec_init); +module_exit(terratec_exit); + |