Path: blob/master/drivers/media/radio/radio-gemtek.c
15111 views
/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <[email protected]>1*2* GemTek hasn't released any specs on the card, so the protocol had to3* be reverse engineered with dosemu.4*5* Besides the protocol changes, this is mostly a copy of:6*7* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff8*9* Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood10* Converted to new API by Alan Cox <[email protected]>11* Various bugfixes and enhancements by Russell Kroll <[email protected]>12*13* TODO: Allow for more than one of these foolish entities :-)14*15* Converted to V4L2 API by Mauro Carvalho Chehab <[email protected]>16*/1718#include <linux/module.h> /* Modules */19#include <linux/init.h> /* Initdata */20#include <linux/ioport.h> /* request_region */21#include <linux/delay.h> /* udelay */22#include <linux/videodev2.h> /* kernel radio structs */23#include <linux/version.h> /* for KERNEL_VERSION MACRO */24#include <linux/mutex.h>25#include <linux/io.h> /* outb, outb_p */26#include <media/v4l2-ioctl.h>27#include <media/v4l2-device.h>2829#define RADIO_VERSION KERNEL_VERSION(0, 0, 3)3031/*32* Module info.33*/3435MODULE_AUTHOR("Jonas Munsin, Pekka Sepp�nen <[email protected]>");36MODULE_DESCRIPTION("A driver for the GemTek Radio card.");37MODULE_LICENSE("GPL");3839/*40* Module params.41*/4243#ifndef CONFIG_RADIO_GEMTEK_PORT44#define CONFIG_RADIO_GEMTEK_PORT -145#endif46#ifndef CONFIG_RADIO_GEMTEK_PROBE47#define CONFIG_RADIO_GEMTEK_PROBE 148#endif4950static int io = CONFIG_RADIO_GEMTEK_PORT;51static int probe = CONFIG_RADIO_GEMTEK_PROBE;52static int hardmute;53static int shutdown = 1;54static int keepmuted = 1;55static int initmute = 1;56static int radio_nr = -1;5758module_param(io, int, 0444);59MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic "60"probing is disabled or fails. The most common I/O ports are: 0x20c "61"0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to "62"work for the combined sound/radiocard).");6364module_param(probe, bool, 0444);65MODULE_PARM_DESC(probe, "Enable automatic device probing. Note: only the most "66"common I/O ports used by the card are probed.");6768module_param(hardmute, bool, 0644);69MODULE_PARM_DESC(hardmute, "Enable `hard muting' by shutting down PLL, may "70"reduce static noise.");7172module_param(shutdown, bool, 0644);73MODULE_PARM_DESC(shutdown, "Enable shutting down PLL and muting line when "74"module is unloaded.");7576module_param(keepmuted, bool, 0644);77MODULE_PARM_DESC(keepmuted, "Keep card muted even when frequency is changed.");7879module_param(initmute, bool, 0444);80MODULE_PARM_DESC(initmute, "Mute card when module is loaded.");8182module_param(radio_nr, int, 0444);8384/*85* Functions for controlling the card.86*/87#define GEMTEK_LOWFREQ (87*16000)88#define GEMTEK_HIGHFREQ (108*16000)8990/*91* Frequency calculation constants. Intermediate frequency 10.52 MHz (nominal92* value 10.7 MHz), reference divisor 6.39 kHz (nominal 6.25 kHz).93*/94#define FSCALE 895#define IF_OFFSET ((unsigned int)(10.52 * 16000 * (1<<FSCALE)))96#define REF_FREQ ((unsigned int)(6.39 * 16 * (1<<FSCALE)))9798#define GEMTEK_CK 0x01 /* Clock signal */99#define GEMTEK_DA 0x02 /* Serial data */100#define GEMTEK_CE 0x04 /* Chip enable */101#define GEMTEK_NS 0x08 /* No signal */102#define GEMTEK_MT 0x10 /* Line mute */103#define GEMTEK_STDF_3_125_KHZ 0x01 /* Standard frequency 3.125 kHz */104#define GEMTEK_PLL_OFF 0x07 /* PLL off */105106#define BU2614_BUS_SIZE 32 /* BU2614 / BU2614FS bus size */107108#define SHORT_DELAY 5 /* usec */109#define LONG_DELAY 75 /* usec */110111struct gemtek {112struct v4l2_device v4l2_dev;113struct video_device vdev;114struct mutex lock;115unsigned long lastfreq;116int muted;117int verified;118int io;119u32 bu2614data;120};121122static struct gemtek gemtek_card;123124#define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */125#define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */126#define BU2614_VOID_BITS 4 /* unused */127#define BU2614_FMES_BITS 1 /* CT, Frequency measurement beginning data */128#define BU2614_STDF_BITS 3 /* R0..R2, Standard frequency data */129#define BU2614_SWIN_BITS 1 /* S, Switch between FMIN / AMIN */130#define BU2614_SWAL_BITS 1 /* PS, Swallow counter division (AMIN only)*/131#define BU2614_VOID2_BITS 1 /* unused */132#define BU2614_FMUN_BITS 1 /* GT, Frequency measurement time & unlock */133#define BU2614_TEST_BITS 1 /* TS, Test data is input */134135#define BU2614_FREQ_SHIFT 0136#define BU2614_PORT_SHIFT (BU2614_FREQ_BITS + BU2614_FREQ_SHIFT)137#define BU2614_VOID_SHIFT (BU2614_PORT_BITS + BU2614_PORT_SHIFT)138#define BU2614_FMES_SHIFT (BU2614_VOID_BITS + BU2614_VOID_SHIFT)139#define BU2614_STDF_SHIFT (BU2614_FMES_BITS + BU2614_FMES_SHIFT)140#define BU2614_SWIN_SHIFT (BU2614_STDF_BITS + BU2614_STDF_SHIFT)141#define BU2614_SWAL_SHIFT (BU2614_SWIN_BITS + BU2614_SWIN_SHIFT)142#define BU2614_VOID2_SHIFT (BU2614_SWAL_BITS + BU2614_SWAL_SHIFT)143#define BU2614_FMUN_SHIFT (BU2614_VOID2_BITS + BU2614_VOID2_SHIFT)144#define BU2614_TEST_SHIFT (BU2614_FMUN_BITS + BU2614_FMUN_SHIFT)145146#define MKMASK(field) (((1<<BU2614_##field##_BITS) - 1) << \147BU2614_##field##_SHIFT)148#define BU2614_PORT_MASK MKMASK(PORT)149#define BU2614_FREQ_MASK MKMASK(FREQ)150#define BU2614_VOID_MASK MKMASK(VOID)151#define BU2614_FMES_MASK MKMASK(FMES)152#define BU2614_STDF_MASK MKMASK(STDF)153#define BU2614_SWIN_MASK MKMASK(SWIN)154#define BU2614_SWAL_MASK MKMASK(SWAL)155#define BU2614_VOID2_MASK MKMASK(VOID2)156#define BU2614_FMUN_MASK MKMASK(FMUN)157#define BU2614_TEST_MASK MKMASK(TEST)158159/*160* Set data which will be sent to BU2614FS.161*/162#define gemtek_bu2614_set(dev, field, data) ((dev)->bu2614data = \163((dev)->bu2614data & ~field##_MASK) | ((data) << field##_SHIFT))164165/*166* Transmit settings to BU2614FS over GemTek IC.167*/168static void gemtek_bu2614_transmit(struct gemtek *gt)169{170int i, bit, q, mute;171172mutex_lock(>->lock);173174mute = gt->muted ? GEMTEK_MT : 0x00;175176outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io);177udelay(SHORT_DELAY);178outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);179udelay(LONG_DELAY);180181for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) {182bit = (q & 1) ? GEMTEK_DA : 0;183outb_p(mute | GEMTEK_CE | bit, gt->io);184udelay(SHORT_DELAY);185outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, gt->io);186udelay(SHORT_DELAY);187}188189outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io);190udelay(SHORT_DELAY);191outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);192udelay(LONG_DELAY);193194mutex_unlock(>->lock);195}196197/*198* Calculate divisor from FM-frequency for BU2614FS (3.125 KHz STDF expected).199*/200static unsigned long gemtek_convfreq(unsigned long freq)201{202return ((freq<<FSCALE) + IF_OFFSET + REF_FREQ/2) / REF_FREQ;203}204205/*206* Set FM-frequency.207*/208static void gemtek_setfreq(struct gemtek *gt, unsigned long freq)209{210if (keepmuted && hardmute && gt->muted)211return;212213freq = clamp_val(freq, GEMTEK_LOWFREQ, GEMTEK_HIGHFREQ);214215gt->lastfreq = freq;216gt->muted = 0;217218gemtek_bu2614_set(gt, BU2614_PORT, 0);219gemtek_bu2614_set(gt, BU2614_FMES, 0);220gemtek_bu2614_set(gt, BU2614_SWIN, 0); /* FM-mode */221gemtek_bu2614_set(gt, BU2614_SWAL, 0);222gemtek_bu2614_set(gt, BU2614_FMUN, 1); /* GT bit set */223gemtek_bu2614_set(gt, BU2614_TEST, 0);224225gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ);226gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq));227228gemtek_bu2614_transmit(gt);229}230231/*232* Set mute flag.233*/234static void gemtek_mute(struct gemtek *gt)235{236int i;237238gt->muted = 1;239240if (hardmute) {241/* Turn off PLL, disable data output */242gemtek_bu2614_set(gt, BU2614_PORT, 0);243gemtek_bu2614_set(gt, BU2614_FMES, 0); /* CT bit off */244gemtek_bu2614_set(gt, BU2614_SWIN, 0); /* FM-mode */245gemtek_bu2614_set(gt, BU2614_SWAL, 0);246gemtek_bu2614_set(gt, BU2614_FMUN, 0); /* GT bit off */247gemtek_bu2614_set(gt, BU2614_TEST, 0);248gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF);249gemtek_bu2614_set(gt, BU2614_FREQ, 0);250gemtek_bu2614_transmit(gt);251return;252}253254mutex_lock(>->lock);255256/* Read bus contents (CE, CK and DA). */257i = inb_p(gt->io);258/* Write it back with mute flag set. */259outb_p((i >> 5) | GEMTEK_MT, gt->io);260udelay(SHORT_DELAY);261262mutex_unlock(>->lock);263}264265/*266* Unset mute flag.267*/268static void gemtek_unmute(struct gemtek *gt)269{270int i;271272gt->muted = 0;273if (hardmute) {274/* Turn PLL back on. */275gemtek_setfreq(gt, gt->lastfreq);276return;277}278mutex_lock(>->lock);279280i = inb_p(gt->io);281outb_p(i >> 5, gt->io);282udelay(SHORT_DELAY);283284mutex_unlock(>->lock);285}286287/*288* Get signal strength (= stereo status).289*/290static inline int gemtek_getsigstr(struct gemtek *gt)291{292int sig;293294mutex_lock(>->lock);295sig = inb_p(gt->io) & GEMTEK_NS ? 0 : 1;296mutex_unlock(>->lock);297return sig;298}299300/*301* Check if requested card acts like GemTek Radio card.302*/303static int gemtek_verify(struct gemtek *gt, int port)304{305int i, q;306307if (gt->verified == port)308return 1;309310mutex_lock(>->lock);311312q = inb_p(port); /* Read bus contents before probing. */313/* Try to turn on CE, CK and DA respectively and check if card responds314properly. */315for (i = 0; i < 3; ++i) {316outb_p(1 << i, port);317udelay(SHORT_DELAY);318319if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) {320mutex_unlock(>->lock);321return 0;322}323}324outb_p(q >> 5, port); /* Write bus contents back. */325udelay(SHORT_DELAY);326327mutex_unlock(>->lock);328gt->verified = port;329330return 1;331}332333/*334* Automatic probing for card.335*/336static int gemtek_probe(struct gemtek *gt)337{338struct v4l2_device *v4l2_dev = >->v4l2_dev;339int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };340int i;341342if (!probe) {343v4l2_info(v4l2_dev, "Automatic device probing disabled.\n");344return -1;345}346347v4l2_info(v4l2_dev, "Automatic device probing enabled.\n");348349for (i = 0; i < ARRAY_SIZE(ioports); ++i) {350v4l2_info(v4l2_dev, "Trying I/O port 0x%x...\n", ioports[i]);351352if (!request_region(ioports[i], 1, "gemtek-probe")) {353v4l2_warn(v4l2_dev, "I/O port 0x%x busy!\n",354ioports[i]);355continue;356}357358if (gemtek_verify(gt, ioports[i])) {359v4l2_info(v4l2_dev, "Card found from I/O port "360"0x%x!\n", ioports[i]);361362release_region(ioports[i], 1);363gt->io = ioports[i];364return gt->io;365}366367release_region(ioports[i], 1);368}369370v4l2_err(v4l2_dev, "Automatic probing failed!\n");371return -1;372}373374/*375* Video 4 Linux stuff.376*/377378static const struct v4l2_file_operations gemtek_fops = {379.owner = THIS_MODULE,380.unlocked_ioctl = video_ioctl2,381};382383static int vidioc_querycap(struct file *file, void *priv,384struct v4l2_capability *v)385{386strlcpy(v->driver, "radio-gemtek", sizeof(v->driver));387strlcpy(v->card, "GemTek", sizeof(v->card));388strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));389v->version = RADIO_VERSION;390v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;391return 0;392}393394static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v)395{396struct gemtek *gt = video_drvdata(file);397398if (v->index > 0)399return -EINVAL;400401strlcpy(v->name, "FM", sizeof(v->name));402v->type = V4L2_TUNER_RADIO;403v->rangelow = GEMTEK_LOWFREQ;404v->rangehigh = GEMTEK_HIGHFREQ;405v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;406v->signal = 0xffff * gemtek_getsigstr(gt);407if (v->signal) {408v->audmode = V4L2_TUNER_MODE_STEREO;409v->rxsubchans = V4L2_TUNER_SUB_STEREO;410} else {411v->audmode = V4L2_TUNER_MODE_MONO;412v->rxsubchans = V4L2_TUNER_SUB_MONO;413}414return 0;415}416417static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v)418{419return (v->index != 0) ? -EINVAL : 0;420}421422static int vidioc_g_frequency(struct file *file, void *priv,423struct v4l2_frequency *f)424{425struct gemtek *gt = video_drvdata(file);426427if (f->tuner != 0)428return -EINVAL;429f->type = V4L2_TUNER_RADIO;430f->frequency = gt->lastfreq;431return 0;432}433434static int vidioc_s_frequency(struct file *file, void *priv,435struct v4l2_frequency *f)436{437struct gemtek *gt = video_drvdata(file);438439if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)440return -EINVAL;441gemtek_setfreq(gt, f->frequency);442return 0;443}444445static int vidioc_queryctrl(struct file *file, void *priv,446struct v4l2_queryctrl *qc)447{448switch (qc->id) {449case V4L2_CID_AUDIO_MUTE:450return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);451default:452return -EINVAL;453}454}455456static int vidioc_g_ctrl(struct file *file, void *priv,457struct v4l2_control *ctrl)458{459struct gemtek *gt = video_drvdata(file);460461switch (ctrl->id) {462case V4L2_CID_AUDIO_MUTE:463ctrl->value = gt->muted;464return 0;465}466return -EINVAL;467}468469static int vidioc_s_ctrl(struct file *file, void *priv,470struct v4l2_control *ctrl)471{472struct gemtek *gt = video_drvdata(file);473474switch (ctrl->id) {475case V4L2_CID_AUDIO_MUTE:476if (ctrl->value)477gemtek_mute(gt);478else479gemtek_unmute(gt);480return 0;481}482return -EINVAL;483}484485static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)486{487*i = 0;488return 0;489}490491static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)492{493return (i != 0) ? -EINVAL : 0;494}495496static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)497{498a->index = 0;499strlcpy(a->name, "Radio", sizeof(a->name));500a->capability = V4L2_AUDCAP_STEREO;501return 0;502}503504static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)505{506return (a->index != 0) ? -EINVAL : 0;507}508509static const struct v4l2_ioctl_ops gemtek_ioctl_ops = {510.vidioc_querycap = vidioc_querycap,511.vidioc_g_tuner = vidioc_g_tuner,512.vidioc_s_tuner = vidioc_s_tuner,513.vidioc_g_audio = vidioc_g_audio,514.vidioc_s_audio = vidioc_s_audio,515.vidioc_g_input = vidioc_g_input,516.vidioc_s_input = vidioc_s_input,517.vidioc_g_frequency = vidioc_g_frequency,518.vidioc_s_frequency = vidioc_s_frequency,519.vidioc_queryctrl = vidioc_queryctrl,520.vidioc_g_ctrl = vidioc_g_ctrl,521.vidioc_s_ctrl = vidioc_s_ctrl522};523524/*525* Initialization / cleanup related stuff.526*/527528static int __init gemtek_init(void)529{530struct gemtek *gt = &gemtek_card;531struct v4l2_device *v4l2_dev = >->v4l2_dev;532int res;533534strlcpy(v4l2_dev->name, "gemtek", sizeof(v4l2_dev->name));535536v4l2_info(v4l2_dev, "GemTek Radio card driver: v0.0.3\n");537538mutex_init(>->lock);539540gt->verified = -1;541gt->io = io;542gemtek_probe(gt);543if (gt->io) {544if (!request_region(gt->io, 1, "gemtek")) {545v4l2_err(v4l2_dev, "I/O port 0x%x already in use.\n", gt->io);546return -EBUSY;547}548549if (!gemtek_verify(gt, gt->io))550v4l2_warn(v4l2_dev, "Card at I/O port 0x%x does not "551"respond properly, check your "552"configuration.\n", gt->io);553else554v4l2_info(v4l2_dev, "Using I/O port 0x%x.\n", gt->io);555} else if (probe) {556v4l2_err(v4l2_dev, "Automatic probing failed and no "557"fixed I/O port defined.\n");558return -ENODEV;559} else {560v4l2_err(v4l2_dev, "Automatic probing disabled but no fixed "561"I/O port defined.");562return -EINVAL;563}564565res = v4l2_device_register(NULL, v4l2_dev);566if (res < 0) {567v4l2_err(v4l2_dev, "Could not register v4l2_device\n");568release_region(gt->io, 1);569return res;570}571572strlcpy(gt->vdev.name, v4l2_dev->name, sizeof(gt->vdev.name));573gt->vdev.v4l2_dev = v4l2_dev;574gt->vdev.fops = &gemtek_fops;575gt->vdev.ioctl_ops = &gemtek_ioctl_ops;576gt->vdev.release = video_device_release_empty;577video_set_drvdata(>->vdev, gt);578579/* Set defaults */580gt->lastfreq = GEMTEK_LOWFREQ;581gt->bu2614data = 0;582583if (initmute)584gemtek_mute(gt);585586if (video_register_device(>->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {587v4l2_device_unregister(v4l2_dev);588release_region(gt->io, 1);589return -EBUSY;590}591592return 0;593}594595/*596* Module cleanup597*/598static void __exit gemtek_exit(void)599{600struct gemtek *gt = &gemtek_card;601struct v4l2_device *v4l2_dev = >->v4l2_dev;602603if (shutdown) {604hardmute = 1; /* Turn off PLL */605gemtek_mute(gt);606} else {607v4l2_info(v4l2_dev, "Module unloaded but card not muted!\n");608}609610video_unregister_device(>->vdev);611v4l2_device_unregister(>->v4l2_dev);612release_region(gt->io, 1);613}614615module_init(gemtek_init);616module_exit(gemtek_exit);617618619