Path: blob/master/drivers/media/radio/radio-terratec.c
15111 views
/* Terratec ActiveRadio ISA Standalone card driver for Linux radio support1* (c) 1999 R. Offermanns ([email protected])2* based on the aimslab radio driver from M. Kirkwood3* many thanks to Michael Becker and Friedhelm Birth (from TerraTec)4*5*6* History:7* 1999-05-21 First preview release8*9* Notes on the hardware:10* There are two "main" chips on the card:11* - Philips OM5610 (http://www-us.semiconductors.philips.com/acrobat/datasheets/OM5610_2.pdf)12* - Philips SAA6588 (http://www-us.semiconductors.philips.com/acrobat/datasheets/SAA6588_1.pdf)13* (you can get the datasheet at the above links)14*15* Frequency control is done digitally -- ie out(port,encodefreq(95.8));16* Volume Control is done digitally17*18* there is a I2C controlled RDS decoder (SAA6588) onboard, which i would like to support someday19* (as soon i have understand how to get started :)20* If you can help me out with that, please contact me!!21*22*23* Converted to V4L2 API by Mauro Carvalho Chehab <[email protected]>24*/2526#include <linux/module.h> /* Modules */27#include <linux/init.h> /* Initdata */28#include <linux/ioport.h> /* request_region */29#include <linux/videodev2.h> /* kernel radio structs */30#include <linux/mutex.h>31#include <linux/version.h> /* for KERNEL_VERSION MACRO */32#include <linux/io.h> /* outb, outb_p */33#include <media/v4l2-device.h>34#include <media/v4l2-ioctl.h>3536MODULE_AUTHOR("R.OFFERMANNS & others");37MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");38MODULE_LICENSE("GPL");3940#ifndef CONFIG_RADIO_TERRATEC_PORT41#define CONFIG_RADIO_TERRATEC_PORT 0x59042#endif4344static int io = CONFIG_RADIO_TERRATEC_PORT;45static int radio_nr = -1;4647module_param(io, int, 0);48MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");49module_param(radio_nr, int, 0);5051#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)5253static struct v4l2_queryctrl radio_qctrl[] = {54{55.id = V4L2_CID_AUDIO_MUTE,56.name = "Mute",57.minimum = 0,58.maximum = 1,59.default_value = 1,60.type = V4L2_CTRL_TYPE_BOOLEAN,61},{62.id = V4L2_CID_AUDIO_VOLUME,63.name = "Volume",64.minimum = 0,65.maximum = 0xff,66.step = 1,67.default_value = 0xff,68.type = V4L2_CTRL_TYPE_INTEGER,69}70};7172#define WRT_DIS 0x0073#define CLK_OFF 0x0074#define IIC_DATA 0x0175#define IIC_CLK 0x0276#define DATA 0x0477#define CLK_ON 0x0878#define WRT_EN 0x107980struct terratec81{82struct v4l2_device v4l2_dev;83struct video_device vdev;84int io;85int curvol;86unsigned long curfreq;87int muted;88struct mutex lock;89};9091static struct terratec terratec_card;9293/* local things */9495static void tt_write_vol(struct terratec *tt, int volume)96{97int i;9899volume = volume + (volume * 32); /* change both channels */100mutex_lock(&tt->lock);101for (i = 0; i < 8; i++) {102if (volume & (0x80 >> i))103outb(0x80, tt->io + 1);104else105outb(0x00, tt->io + 1);106}107mutex_unlock(&tt->lock);108}109110111112static void tt_mute(struct terratec *tt)113{114tt->muted = 1;115tt_write_vol(tt, 0);116}117118static int tt_setvol(struct terratec *tt, int vol)119{120if (vol == tt->curvol) { /* requested volume = current */121if (tt->muted) { /* user is unmuting the card */122tt->muted = 0;123tt_write_vol(tt, vol); /* enable card */124}125return 0;126}127128if (vol == 0) { /* volume = 0 means mute the card */129tt_write_vol(tt, 0); /* "turn off card" by setting vol to 0 */130tt->curvol = vol; /* track the volume state! */131return 0;132}133134tt->muted = 0;135tt_write_vol(tt, vol);136tt->curvol = vol;137return 0;138}139140141/* this is the worst part in this driver */142/* many more or less strange things are going on here, but hey, it works :) */143144static int tt_setfreq(struct terratec *tt, unsigned long freq1)145{146int freq;147int i;148int p;149int temp;150long rest;151unsigned char buffer[25]; /* we have to bit shift 25 registers */152153mutex_lock(&tt->lock);154155tt->curfreq = freq1;156157freq = freq1 / 160; /* convert the freq. to a nice to handle value */158memset(buffer, 0, sizeof(buffer));159160rest = freq * 10 + 10700; /* I once had understood what is going on here */161/* maybe some wise guy (friedhelm?) can comment this stuff */162i = 13;163p = 10;164temp = 102400;165while (rest != 0) {166if (rest % temp == rest)167buffer[i] = 0;168else {169buffer[i] = 1;170rest = rest - temp;171}172i--;173p--;174temp = temp / 2;175}176177for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */178if (buffer[i] == 1) {179outb(WRT_EN | DATA, tt->io);180outb(WRT_EN | DATA | CLK_ON, tt->io);181outb(WRT_EN | DATA, tt->io);182} else {183outb(WRT_EN | 0x00, tt->io);184outb(WRT_EN | 0x00 | CLK_ON, tt->io);185}186}187outb(0x00, tt->io);188189mutex_unlock(&tt->lock);190191return 0;192}193194static int tt_getsigstr(struct terratec *tt)195{196if (inb(tt->io) & 2) /* bit set = no signal present */197return 0;198return 1; /* signal present */199}200201static int vidioc_querycap(struct file *file, void *priv,202struct v4l2_capability *v)203{204strlcpy(v->driver, "radio-terratec", sizeof(v->driver));205strlcpy(v->card, "ActiveRadio", sizeof(v->card));206strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));207v->version = RADIO_VERSION;208v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;209return 0;210}211212static int vidioc_g_tuner(struct file *file, void *priv,213struct v4l2_tuner *v)214{215struct terratec *tt = video_drvdata(file);216217if (v->index > 0)218return -EINVAL;219220strlcpy(v->name, "FM", sizeof(v->name));221v->type = V4L2_TUNER_RADIO;222v->rangelow = 87 * 16000;223v->rangehigh = 108 * 16000;224v->rxsubchans = V4L2_TUNER_SUB_MONO;225v->capability = V4L2_TUNER_CAP_LOW;226v->audmode = V4L2_TUNER_MODE_MONO;227v->signal = 0xFFFF * tt_getsigstr(tt);228return 0;229}230231static int vidioc_s_tuner(struct file *file, void *priv,232struct v4l2_tuner *v)233{234return v->index ? -EINVAL : 0;235}236237static int vidioc_s_frequency(struct file *file, void *priv,238struct v4l2_frequency *f)239{240struct terratec *tt = video_drvdata(file);241242if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)243return -EINVAL;244tt_setfreq(tt, f->frequency);245return 0;246}247248static int vidioc_g_frequency(struct file *file, void *priv,249struct v4l2_frequency *f)250{251struct terratec *tt = video_drvdata(file);252253if (f->tuner != 0)254return -EINVAL;255f->type = V4L2_TUNER_RADIO;256f->frequency = tt->curfreq;257return 0;258}259260static int vidioc_queryctrl(struct file *file, void *priv,261struct v4l2_queryctrl *qc)262{263int i;264265for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {266if (qc->id && qc->id == radio_qctrl[i].id) {267memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));268return 0;269}270}271return -EINVAL;272}273274static int vidioc_g_ctrl(struct file *file, void *priv,275struct v4l2_control *ctrl)276{277struct terratec *tt = video_drvdata(file);278279switch (ctrl->id) {280case V4L2_CID_AUDIO_MUTE:281if (tt->muted)282ctrl->value = 1;283else284ctrl->value = 0;285return 0;286case V4L2_CID_AUDIO_VOLUME:287ctrl->value = tt->curvol * 6554;288return 0;289}290return -EINVAL;291}292293static int vidioc_s_ctrl(struct file *file, void *priv,294struct v4l2_control *ctrl)295{296struct terratec *tt = video_drvdata(file);297298switch (ctrl->id) {299case V4L2_CID_AUDIO_MUTE:300if (ctrl->value)301tt_mute(tt);302else303tt_setvol(tt,tt->curvol);304return 0;305case V4L2_CID_AUDIO_VOLUME:306tt_setvol(tt,ctrl->value);307return 0;308}309return -EINVAL;310}311312static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)313{314*i = 0;315return 0;316}317318static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)319{320return i ? -EINVAL : 0;321}322323static int vidioc_g_audio(struct file *file, void *priv,324struct v4l2_audio *a)325{326a->index = 0;327strlcpy(a->name, "Radio", sizeof(a->name));328a->capability = V4L2_AUDCAP_STEREO;329return 0;330}331332static int vidioc_s_audio(struct file *file, void *priv,333struct v4l2_audio *a)334{335return a->index ? -EINVAL : 0;336}337338static const struct v4l2_file_operations terratec_fops = {339.owner = THIS_MODULE,340.unlocked_ioctl = video_ioctl2,341};342343static const struct v4l2_ioctl_ops terratec_ioctl_ops = {344.vidioc_querycap = vidioc_querycap,345.vidioc_g_tuner = vidioc_g_tuner,346.vidioc_s_tuner = vidioc_s_tuner,347.vidioc_g_frequency = vidioc_g_frequency,348.vidioc_s_frequency = vidioc_s_frequency,349.vidioc_queryctrl = vidioc_queryctrl,350.vidioc_g_ctrl = vidioc_g_ctrl,351.vidioc_s_ctrl = vidioc_s_ctrl,352.vidioc_g_audio = vidioc_g_audio,353.vidioc_s_audio = vidioc_s_audio,354.vidioc_g_input = vidioc_g_input,355.vidioc_s_input = vidioc_s_input,356};357358static int __init terratec_init(void)359{360struct terratec *tt = &terratec_card;361struct v4l2_device *v4l2_dev = &tt->v4l2_dev;362int res;363364strlcpy(v4l2_dev->name, "terratec", sizeof(v4l2_dev->name));365tt->io = io;366if (tt->io == -1) {367v4l2_err(v4l2_dev, "you must set an I/O address with io=0x590 or 0x591\n");368return -EINVAL;369}370if (!request_region(tt->io, 2, "terratec")) {371v4l2_err(v4l2_dev, "port 0x%x already in use\n", io);372return -EBUSY;373}374375res = v4l2_device_register(NULL, v4l2_dev);376if (res < 0) {377release_region(tt->io, 2);378v4l2_err(v4l2_dev, "Could not register v4l2_device\n");379return res;380}381382strlcpy(tt->vdev.name, v4l2_dev->name, sizeof(tt->vdev.name));383tt->vdev.v4l2_dev = v4l2_dev;384tt->vdev.fops = &terratec_fops;385tt->vdev.ioctl_ops = &terratec_ioctl_ops;386tt->vdev.release = video_device_release_empty;387video_set_drvdata(&tt->vdev, tt);388389mutex_init(&tt->lock);390391/* mute card - prevents noisy bootups */392tt_write_vol(tt, 0);393394if (video_register_device(&tt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {395v4l2_device_unregister(&tt->v4l2_dev);396release_region(tt->io, 2);397return -EINVAL;398}399400v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver.\n");401return 0;402}403404static void __exit terratec_exit(void)405{406struct terratec *tt = &terratec_card;407struct v4l2_device *v4l2_dev = &tt->v4l2_dev;408409video_unregister_device(&tt->vdev);410v4l2_device_unregister(&tt->v4l2_dev);411release_region(tt->io, 2);412v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver unloaded.\n");413}414415module_init(terratec_init);416module_exit(terratec_exit);417418419420