Path: blob/master/drivers/media/radio/radio-sf16fmr2.c
15111 views
/* SF16FMR2 radio driver for Linux radio support1* heavily based on fmi driver...2* (c) 2000-2002 Ziglio Frediano, [email protected]3*4* Notes on the hardware5*6* Frequency control is done digitally -- ie out(port,encodefreq(95.8));7* No volume control - only mute/unmute - you have to use line volume8*9* For read stereo/mono you must wait 0.1 sec after set frequency and10* card unmuted so I set frequency on unmute11* Signal handling seem to work only on autoscanning (not implemented)12*13* Converted to V4L2 API by Mauro Carvalho Chehab <[email protected]>14*/1516#include <linux/module.h> /* Modules */17#include <linux/init.h> /* Initdata */18#include <linux/ioport.h> /* request_region */19#include <linux/delay.h> /* udelay */20#include <linux/videodev2.h> /* kernel radio structs */21#include <linux/mutex.h>22#include <linux/version.h> /* for KERNEL_VERSION MACRO */23#include <linux/io.h> /* outb, outb_p */24#include <media/v4l2-device.h>25#include <media/v4l2-ioctl.h>2627MODULE_AUTHOR("Ziglio Frediano, [email protected]");28MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");29MODULE_LICENSE("GPL");3031static int io = 0x384;32static int radio_nr = -1;3334module_param(io, int, 0);35MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");36module_param(radio_nr, int, 0);3738#define RADIO_VERSION KERNEL_VERSION(0,0,2)3940#define AUD_VOL_INDEX 14142#undef DEBUG43//#define DEBUG 14445#ifdef DEBUG46# define debug_print(s) printk s47#else48# define debug_print(s)49#endif5051/* this should be static vars for module size */52struct fmr253{54struct v4l2_device v4l2_dev;55struct video_device vdev;56struct mutex lock;57int io;58int curvol; /* 0-15 */59int mute;60int stereo; /* card is producing stereo audio */61unsigned long curfreq; /* freq in kHz */62int card_type;63};6465static struct fmr2 fmr2_card;6667/* hw precision is 12.5 kHz68* It is only useful to give freq in interval of 200 (=0.0125Mhz),69* other bits will be truncated70*/71#define RSF16_ENCODE(x) ((x) / 200 + 856)72#define RSF16_MINFREQ (87 * 16000)73#define RSF16_MAXFREQ (108 * 16000)7475static inline void wait(int n, int io)76{77for (; n; --n)78inb(io);79}8081static void outbits(int bits, unsigned int data, int nWait, int io)82{83int bit;8485for (; --bits >= 0;) {86bit = (data >> bits) & 1;87outb(bit, io);88wait(nWait, io);89outb(bit | 2, io);90wait(nWait, io);91outb(bit, io);92wait(nWait, io);93}94}9596static inline void fmr2_mute(int io)97{98outb(0x00, io);99wait(4, io);100}101102static inline void fmr2_unmute(int io)103{104outb(0x04, io);105wait(4, io);106}107108static inline int fmr2_stereo_mode(int io)109{110int n = inb(io);111112outb(6, io);113inb(io);114n = ((n >> 3) & 1) ^ 1;115debug_print((KERN_DEBUG "stereo: %d\n", n));116return n;117}118119static int fmr2_product_info(struct fmr2 *dev)120{121int n = inb(dev->io);122123n &= 0xC1;124if (n == 0) {125/* this should support volume set */126dev->card_type = 12;127return 0;128}129/* not volume (mine is 11) */130dev->card_type = (n == 128) ? 11 : 0;131return n;132}133134static inline int fmr2_getsigstr(struct fmr2 *dev)135{136/* !!! works only if scanning freq */137int res = 0xffff;138139outb(5, dev->io);140wait(4, dev->io);141if (!(inb(dev->io) & 1))142res = 0;143debug_print((KERN_DEBUG "signal: %d\n", res));144return res;145}146147/* set frequency and unmute card */148static int fmr2_setfreq(struct fmr2 *dev)149{150unsigned long freq = dev->curfreq;151152fmr2_mute(dev->io);153154/* 0x42 for mono output155* 0x102 forward scanning156* 0x182 scansione avanti157*/158outbits(9, 0x2, 3, dev->io);159outbits(16, RSF16_ENCODE(freq), 2, dev->io);160161fmr2_unmute(dev->io);162163/* wait 0.11 sec */164msleep(110);165166/* NOTE if mute this stop radio167you must set freq on unmute */168dev->stereo = fmr2_stereo_mode(dev->io);169return 0;170}171172/* !!! not tested, in my card this doesn't work !!! */173static int fmr2_setvolume(struct fmr2 *dev)174{175int vol[16] = { 0x021, 0x084, 0x090, 0x104,1760x110, 0x204, 0x210, 0x402,1770x404, 0x408, 0x410, 0x801,1780x802, 0x804, 0x808, 0x810 };179int i, a;180int n = vol[dev->curvol & 0x0f];181182if (dev->card_type != 11)183return 1;184185for (i = 12; --i >= 0; ) {186a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */187outb(a | 4, dev->io);188wait(4, dev->io);189outb(a | 0x24, dev->io);190wait(4, dev->io);191outb(a | 4, dev->io);192wait(4, dev->io);193}194for (i = 6; --i >= 0; ) {195a = ((0x18 >> i) & 1) << 6;196outb(a | 4, dev->io);197wait(4, dev->io);198outb(a | 0x24, dev->io);199wait(4, dev->io);200outb(a | 4, dev->io);201wait(4, dev->io);202}203wait(4, dev->io);204outb(0x14, dev->io);205return 0;206}207208static int vidioc_querycap(struct file *file, void *priv,209struct v4l2_capability *v)210{211strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));212strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));213strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));214v->version = RADIO_VERSION;215v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;216return 0;217}218219static int vidioc_g_tuner(struct file *file, void *priv,220struct v4l2_tuner *v)221{222struct fmr2 *fmr2 = video_drvdata(file);223224if (v->index > 0)225return -EINVAL;226227strlcpy(v->name, "FM", sizeof(v->name));228v->type = V4L2_TUNER_RADIO;229230v->rangelow = RSF16_MINFREQ;231v->rangehigh = RSF16_MAXFREQ;232v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :233V4L2_TUNER_SUB_MONO;234v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;235v->audmode = V4L2_TUNER_MODE_STEREO;236mutex_lock(&fmr2->lock);237v->signal = fmr2_getsigstr(fmr2);238mutex_unlock(&fmr2->lock);239return 0;240}241242static int vidioc_s_tuner(struct file *file, void *priv,243struct v4l2_tuner *v)244{245return v->index ? -EINVAL : 0;246}247248static int vidioc_s_frequency(struct file *file, void *priv,249struct v4l2_frequency *f)250{251struct fmr2 *fmr2 = video_drvdata(file);252253if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)254return -EINVAL;255if (f->frequency < RSF16_MINFREQ ||256f->frequency > RSF16_MAXFREQ)257return -EINVAL;258/* rounding in steps of 200 to match the freq259that will be used */260fmr2->curfreq = (f->frequency / 200) * 200;261262/* set card freq (if not muted) */263if (fmr2->curvol && !fmr2->mute) {264mutex_lock(&fmr2->lock);265fmr2_setfreq(fmr2);266mutex_unlock(&fmr2->lock);267}268return 0;269}270271static int vidioc_g_frequency(struct file *file, void *priv,272struct v4l2_frequency *f)273{274struct fmr2 *fmr2 = video_drvdata(file);275276if (f->tuner != 0)277return -EINVAL;278f->type = V4L2_TUNER_RADIO;279f->frequency = fmr2->curfreq;280return 0;281}282283static int vidioc_queryctrl(struct file *file, void *priv,284struct v4l2_queryctrl *qc)285{286struct fmr2 *fmr2 = video_drvdata(file);287288switch (qc->id) {289case V4L2_CID_AUDIO_MUTE:290return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);291case V4L2_CID_AUDIO_VOLUME:292/* Only card_type == 11 implements volume */293if (fmr2->card_type == 11)294return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);295return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);296}297return -EINVAL;298}299300static int vidioc_g_ctrl(struct file *file, void *priv,301struct v4l2_control *ctrl)302{303struct fmr2 *fmr2 = video_drvdata(file);304305switch (ctrl->id) {306case V4L2_CID_AUDIO_MUTE:307ctrl->value = fmr2->mute;308return 0;309case V4L2_CID_AUDIO_VOLUME:310ctrl->value = fmr2->curvol;311return 0;312}313return -EINVAL;314}315316static int vidioc_s_ctrl(struct file *file, void *priv,317struct v4l2_control *ctrl)318{319struct fmr2 *fmr2 = video_drvdata(file);320321switch (ctrl->id) {322case V4L2_CID_AUDIO_MUTE:323fmr2->mute = ctrl->value;324break;325case V4L2_CID_AUDIO_VOLUME:326fmr2->curvol = ctrl->value;327break;328default:329return -EINVAL;330}331332#ifdef DEBUG333if (fmr2->curvol && !fmr2->mute)334printk(KERN_DEBUG "unmute\n");335else336printk(KERN_DEBUG "mute\n");337#endif338339mutex_lock(&fmr2->lock);340if (fmr2->curvol && !fmr2->mute) {341fmr2_setvolume(fmr2);342/* Set frequency and unmute card */343fmr2_setfreq(fmr2);344} else345fmr2_mute(fmr2->io);346mutex_unlock(&fmr2->lock);347return 0;348}349350static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)351{352*i = 0;353return 0;354}355356static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)357{358return i ? -EINVAL : 0;359}360361static int vidioc_g_audio(struct file *file, void *priv,362struct v4l2_audio *a)363{364a->index = 0;365strlcpy(a->name, "Radio", sizeof(a->name));366a->capability = V4L2_AUDCAP_STEREO;367return 0;368}369370static int vidioc_s_audio(struct file *file, void *priv,371struct v4l2_audio *a)372{373return a->index ? -EINVAL : 0;374}375376static const struct v4l2_file_operations fmr2_fops = {377.owner = THIS_MODULE,378.unlocked_ioctl = video_ioctl2,379};380381static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {382.vidioc_querycap = vidioc_querycap,383.vidioc_g_tuner = vidioc_g_tuner,384.vidioc_s_tuner = vidioc_s_tuner,385.vidioc_g_audio = vidioc_g_audio,386.vidioc_s_audio = vidioc_s_audio,387.vidioc_g_input = vidioc_g_input,388.vidioc_s_input = vidioc_s_input,389.vidioc_g_frequency = vidioc_g_frequency,390.vidioc_s_frequency = vidioc_s_frequency,391.vidioc_queryctrl = vidioc_queryctrl,392.vidioc_g_ctrl = vidioc_g_ctrl,393.vidioc_s_ctrl = vidioc_s_ctrl,394};395396static int __init fmr2_init(void)397{398struct fmr2 *fmr2 = &fmr2_card;399struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;400int res;401402strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));403fmr2->io = io;404fmr2->stereo = 1;405mutex_init(&fmr2->lock);406407if (!request_region(fmr2->io, 2, "sf16fmr2")) {408v4l2_err(v4l2_dev, "request_region failed!\n");409return -EBUSY;410}411412res = v4l2_device_register(NULL, v4l2_dev);413if (res < 0) {414release_region(fmr2->io, 2);415v4l2_err(v4l2_dev, "Could not register v4l2_device\n");416return res;417}418419strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));420fmr2->vdev.v4l2_dev = v4l2_dev;421fmr2->vdev.fops = &fmr2_fops;422fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;423fmr2->vdev.release = video_device_release_empty;424video_set_drvdata(&fmr2->vdev, fmr2);425426/* mute card - prevents noisy bootups */427fmr2_mute(fmr2->io);428fmr2_product_info(fmr2);429430if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {431v4l2_device_unregister(v4l2_dev);432release_region(fmr2->io, 2);433return -EINVAL;434}435436v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);437debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));438return 0;439}440441static void __exit fmr2_exit(void)442{443struct fmr2 *fmr2 = &fmr2_card;444445video_unregister_device(&fmr2->vdev);446v4l2_device_unregister(&fmr2->v4l2_dev);447release_region(fmr2->io, 2);448}449450module_init(fmr2_init);451module_exit(fmr2_exit);452453#ifndef MODULE454455static int __init fmr2_setup_io(char *str)456{457get_option(&str, &io);458return 1;459}460461__setup("sf16fmr2=", fmr2_setup_io);462463#endif464465466