11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux 31da177e4SLinus Torvalds * (C) 2001 Dimitromanolakis Apostolos <apdim@grecian.net> 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Based in the radio Maestro PCI driver. Actually it uses the same chip 61da177e4SLinus Torvalds * for radio but different pci controller. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * I didn't have any specs I reversed engineered the protocol from 91da177e4SLinus Torvalds * the windows driver (radio.dll). 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * The card uses the TEA5757 chip that includes a search function but it 121da177e4SLinus Torvalds * is useless as I haven't found any way to read back the frequency. If 131da177e4SLinus Torvalds * anybody does please mail me. 141da177e4SLinus Torvalds * 151da177e4SLinus Torvalds * For the pdf file see: 16631dd1a8SJustin P. Mattock * http://www.nxp.com/acrobat_download2/expired_datasheets/TEA5757_5759_3.pdf 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * 191da177e4SLinus Torvalds * CHANGES: 201da177e4SLinus Torvalds * 0.75b 211da177e4SLinus Torvalds * - better pci interface thanks to Francois Romieu <romieu@cogenit.fr> 221da177e4SLinus Torvalds * 23e84fef6bSMauro Carvalho Chehab * 0.75 Sun Feb 4 22:51:27 EET 2001 241da177e4SLinus Torvalds * - tiding up 251da177e4SLinus Torvalds * - removed support for multiple devices as it didn't work anyway 261da177e4SLinus Torvalds * 271da177e4SLinus Torvalds * BUGS: 281da177e4SLinus Torvalds * - card unmutes if you change frequency 291da177e4SLinus Torvalds * 3006470ed6SMauro Carvalho Chehab * (c) 2006, 2007 by Mauro Carvalho Chehab <mchehab@infradead.org>: 3106470ed6SMauro Carvalho Chehab * - Conversion to V4L2 API 3206470ed6SMauro Carvalho Chehab * - Uses video_ioctl2 for parsing and to add debug support 331da177e4SLinus Torvalds */ 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds #include <linux/module.h> 371da177e4SLinus Torvalds #include <linux/init.h> 381da177e4SLinus Torvalds #include <linux/ioport.h> 391da177e4SLinus Torvalds #include <linux/delay.h> 403593cab5SIngo Molnar #include <linux/mutex.h> 411da177e4SLinus Torvalds #include <linux/pci.h> 42e84fef6bSMauro Carvalho Chehab #include <linux/videodev2.h> 432710e6aaSHans Verkuil #include <linux/io.h> 445a0e3ad6STejun Heo #include <linux/slab.h> 452710e6aaSHans Verkuil #include <media/v4l2-device.h> 4635ea11ffSHans Verkuil #include <media/v4l2-ioctl.h> 471da177e4SLinus Torvalds 48*29834c1aSMauro Carvalho Chehab #define DRIVER_VERSION "0.7.8" 49*29834c1aSMauro Carvalho Chehab 50*29834c1aSMauro Carvalho Chehab 512710e6aaSHans Verkuil MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net"); 522710e6aaSHans Verkuil MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio."); 532710e6aaSHans Verkuil MODULE_LICENSE("GPL"); 54*29834c1aSMauro Carvalho Chehab MODULE_VERSION(DRIVER_VERSION); 552710e6aaSHans Verkuil 562710e6aaSHans Verkuil static int radio_nr = -1; 572710e6aaSHans Verkuil module_param(radio_nr, int, 0); 582710e6aaSHans Verkuil 592710e6aaSHans Verkuil static int debug; 602710e6aaSHans Verkuil 612710e6aaSHans Verkuil module_param(debug, int, 0644); 622710e6aaSHans Verkuil MODULE_PARM_DESC(debug, "activates debug info"); 632710e6aaSHans Verkuil 642710e6aaSHans Verkuil #define dprintk(dev, num, fmt, arg...) \ 652710e6aaSHans Verkuil v4l2_dbg(num, debug, &dev->v4l2_dev, fmt, ## arg) 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds #ifndef PCI_VENDOR_ID_GUILLEMOT 681da177e4SLinus Torvalds #define PCI_VENDOR_ID_GUILLEMOT 0x5046 691da177e4SLinus Torvalds #endif 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds #ifndef PCI_DEVICE_ID_GUILLEMOT 721da177e4SLinus Torvalds #define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001 731da177e4SLinus Torvalds #endif 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds /* TEA5757 pin mappings */ 771da177e4SLinus Torvalds static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16; 781da177e4SLinus Torvalds 7990d873a7SHans Verkuil #define FREQ_LO (87 * 16000) 8090d873a7SHans Verkuil #define FREQ_HI (108 * 16000) 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds #define FREQ_IF 171200 /* 10.7*16000 */ 831da177e4SLinus Torvalds #define FREQ_STEP 200 /* 12.5*16 */ 841da177e4SLinus Torvalds 85f1557cebSMauro Carvalho Chehab /* (x==fmhz*16*1000) -> bits */ 862710e6aaSHans Verkuil #define FREQ2BITS(x) \ 872710e6aaSHans Verkuil ((((unsigned int)(x) + FREQ_IF + (FREQ_STEP << 1)) / (FREQ_STEP << 2)) << 2) 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds #define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF) 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds 922710e6aaSHans Verkuil struct maxiradio 933ca685aaSHans Verkuil { 942710e6aaSHans Verkuil struct v4l2_device v4l2_dev; 952710e6aaSHans Verkuil struct video_device vdev; 962710e6aaSHans Verkuil struct pci_dev *pdev; 973ca685aaSHans Verkuil 982710e6aaSHans Verkuil u16 io; /* base of radio io */ 992710e6aaSHans Verkuil u16 muted; /* VIDEO_AUDIO_MUTE */ 1002710e6aaSHans Verkuil u16 stereo; /* VIDEO_TUNER_STEREO_ON */ 1012710e6aaSHans Verkuil u16 tuned; /* signal strength (0 or 0xffff) */ 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds unsigned long freq; 1041da177e4SLinus Torvalds 1053593cab5SIngo Molnar struct mutex lock; 106712642b8SMauro Carvalho Chehab }; 1071da177e4SLinus Torvalds 1082710e6aaSHans Verkuil static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev) 1091da177e4SLinus Torvalds { 1102710e6aaSHans Verkuil return container_of(v4l2_dev, struct maxiradio, v4l2_dev); 1111da177e4SLinus Torvalds } 1121da177e4SLinus Torvalds 1132710e6aaSHans Verkuil static void outbit(unsigned long bit, u16 io) 1142710e6aaSHans Verkuil { 1152710e6aaSHans Verkuil int val = power | wren | (bit ? data : 0); 1162710e6aaSHans Verkuil 1172710e6aaSHans Verkuil outb(val, io); 1182710e6aaSHans Verkuil udelay(4); 1192710e6aaSHans Verkuil outb(val | clk, io); 1202710e6aaSHans Verkuil udelay(4); 1212710e6aaSHans Verkuil outb(val, io); 1222710e6aaSHans Verkuil udelay(4); 1232710e6aaSHans Verkuil } 1242710e6aaSHans Verkuil 1252710e6aaSHans Verkuil static void turn_power(struct maxiradio *dev, int p) 1261da177e4SLinus Torvalds { 127f1557cebSMauro Carvalho Chehab if (p != 0) { 1282710e6aaSHans Verkuil dprintk(dev, 1, "Radio powered on\n"); 1292710e6aaSHans Verkuil outb(power, dev->io); 130f1557cebSMauro Carvalho Chehab } else { 1312710e6aaSHans Verkuil dprintk(dev, 1, "Radio powered off\n"); 1322710e6aaSHans Verkuil outb(0, dev->io); 133f1557cebSMauro Carvalho Chehab } 1341da177e4SLinus Torvalds } 1351da177e4SLinus Torvalds 1362710e6aaSHans Verkuil static void set_freq(struct maxiradio *dev, u32 freq) 1371da177e4SLinus Torvalds { 1381da177e4SLinus Torvalds unsigned long int si; 1391da177e4SLinus Torvalds int bl; 1402710e6aaSHans Verkuil int io = dev->io; 141c6eb8eafSHans Verkuil int val = FREQ2BITS(freq); 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds /* TEA5757 shift register bits (see pdf) */ 1441da177e4SLinus Torvalds 145c6eb8eafSHans Verkuil outbit(0, io); /* 24 search */ 146c6eb8eafSHans Verkuil outbit(1, io); /* 23 search up/down */ 1471da177e4SLinus Torvalds 148c6eb8eafSHans Verkuil outbit(0, io); /* 22 stereo/mono */ 1491da177e4SLinus Torvalds 150c6eb8eafSHans Verkuil outbit(0, io); /* 21 band */ 151c6eb8eafSHans Verkuil outbit(0, io); /* 20 band (only 00=FM works I think) */ 1521da177e4SLinus Torvalds 153c6eb8eafSHans Verkuil outbit(0, io); /* 19 port ? */ 154c6eb8eafSHans Verkuil outbit(0, io); /* 18 port ? */ 1551da177e4SLinus Torvalds 156c6eb8eafSHans Verkuil outbit(0, io); /* 17 search level */ 157c6eb8eafSHans Verkuil outbit(0, io); /* 16 search level */ 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds si = 0x8000; 160f1557cebSMauro Carvalho Chehab for (bl = 1; bl <= 16; bl++) { 161c6eb8eafSHans Verkuil outbit(val & si, io); 162f1557cebSMauro Carvalho Chehab si >>= 1; 163f1557cebSMauro Carvalho Chehab } 1641da177e4SLinus Torvalds 1652710e6aaSHans Verkuil dprintk(dev, 1, "Radio freq set to %d.%02d MHz\n", 166f1557cebSMauro Carvalho Chehab freq / 16000, 167f1557cebSMauro Carvalho Chehab freq % 16000 * 100 / 16000); 168f1557cebSMauro Carvalho Chehab 1692710e6aaSHans Verkuil turn_power(dev, 1); 1701da177e4SLinus Torvalds } 1711da177e4SLinus Torvalds 1722710e6aaSHans Verkuil static int get_stereo(u16 io) 1731da177e4SLinus Torvalds { 174f1557cebSMauro Carvalho Chehab outb(power,io); 175f1557cebSMauro Carvalho Chehab udelay(4); 176f1557cebSMauro Carvalho Chehab 1771da177e4SLinus Torvalds return !(inb(io) & mo_st); 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds 1802710e6aaSHans Verkuil static int get_tune(u16 io) 1811da177e4SLinus Torvalds { 182f1557cebSMauro Carvalho Chehab outb(power+clk,io); 183f1557cebSMauro Carvalho Chehab udelay(4); 184f1557cebSMauro Carvalho Chehab 1851da177e4SLinus Torvalds return !(inb(io) & mo_st); 1861da177e4SLinus Torvalds } 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds 18906470ed6SMauro Carvalho Chehab static int vidioc_querycap(struct file *file, void *priv, 19006470ed6SMauro Carvalho Chehab struct v4l2_capability *v) 1911da177e4SLinus Torvalds { 1922710e6aaSHans Verkuil struct maxiradio *dev = video_drvdata(file); 1932710e6aaSHans Verkuil 194e84fef6bSMauro Carvalho Chehab strlcpy(v->driver, "radio-maxiradio", sizeof(v->driver)); 195e84fef6bSMauro Carvalho Chehab strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof(v->card)); 1962710e6aaSHans Verkuil snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev)); 1972710e6aaSHans Verkuil v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; 1981da177e4SLinus Torvalds return 0; 1991da177e4SLinus Torvalds } 20006470ed6SMauro Carvalho Chehab 20106470ed6SMauro Carvalho Chehab static int vidioc_g_tuner(struct file *file, void *priv, 20206470ed6SMauro Carvalho Chehab struct v4l2_tuner *v) 203e84fef6bSMauro Carvalho Chehab { 2042710e6aaSHans Verkuil struct maxiradio *dev = video_drvdata(file); 2051da177e4SLinus Torvalds 206e84fef6bSMauro Carvalho Chehab if (v->index > 0) 2071da177e4SLinus Torvalds return -EINVAL; 2081da177e4SLinus Torvalds 2092710e6aaSHans Verkuil mutex_lock(&dev->lock); 2102710e6aaSHans Verkuil strlcpy(v->name, "FM", sizeof(v->name)); 211e84fef6bSMauro Carvalho Chehab v->type = V4L2_TUNER_RADIO; 2121da177e4SLinus Torvalds v->rangelow = FREQ_LO; 2131da177e4SLinus Torvalds v->rangehigh = FREQ_HI; 214e84fef6bSMauro Carvalho Chehab v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; 215e84fef6bSMauro Carvalho Chehab v->capability = V4L2_TUNER_CAP_LOW; 2162710e6aaSHans Verkuil if (get_stereo(dev->io)) 217e84fef6bSMauro Carvalho Chehab v->audmode = V4L2_TUNER_MODE_STEREO; 218e84fef6bSMauro Carvalho Chehab else 219e84fef6bSMauro Carvalho Chehab v->audmode = V4L2_TUNER_MODE_MONO; 2202710e6aaSHans Verkuil v->signal = 0xffff * get_tune(dev->io); 2212710e6aaSHans Verkuil mutex_unlock(&dev->lock); 2221da177e4SLinus Torvalds 2231da177e4SLinus Torvalds return 0; 2241da177e4SLinus Torvalds } 225e84fef6bSMauro Carvalho Chehab 22606470ed6SMauro Carvalho Chehab static int vidioc_s_tuner(struct file *file, void *priv, 22706470ed6SMauro Carvalho Chehab struct v4l2_tuner *v) 22806470ed6SMauro Carvalho Chehab { 2292710e6aaSHans Verkuil return v->index ? -EINVAL : 0; 230140dcc46SMauro Carvalho Chehab } 231140dcc46SMauro Carvalho Chehab 232a0c05ab9SMauro Carvalho Chehab static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 233a0c05ab9SMauro Carvalho Chehab { 234a0c05ab9SMauro Carvalho Chehab *i = 0; 235a0c05ab9SMauro Carvalho Chehab return 0; 236a0c05ab9SMauro Carvalho Chehab } 237a0c05ab9SMauro Carvalho Chehab 238a0c05ab9SMauro Carvalho Chehab static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 239a0c05ab9SMauro Carvalho Chehab { 2402710e6aaSHans Verkuil return i ? -EINVAL : 0; 2412710e6aaSHans Verkuil } 242f1557cebSMauro Carvalho Chehab 2432710e6aaSHans Verkuil static int vidioc_g_audio(struct file *file, void *priv, 2442710e6aaSHans Verkuil struct v4l2_audio *a) 2452710e6aaSHans Verkuil { 2462710e6aaSHans Verkuil a->index = 0; 2472710e6aaSHans Verkuil strlcpy(a->name, "Radio", sizeof(a->name)); 2482710e6aaSHans Verkuil a->capability = V4L2_AUDCAP_STEREO; 249a0c05ab9SMauro Carvalho Chehab return 0; 250a0c05ab9SMauro Carvalho Chehab } 251a0c05ab9SMauro Carvalho Chehab 252a0c05ab9SMauro Carvalho Chehab 253140dcc46SMauro Carvalho Chehab static int vidioc_s_audio(struct file *file, void *priv, 254140dcc46SMauro Carvalho Chehab struct v4l2_audio *a) 255140dcc46SMauro Carvalho Chehab { 2562710e6aaSHans Verkuil return a->index ? -EINVAL : 0; 257140dcc46SMauro Carvalho Chehab } 258140dcc46SMauro Carvalho Chehab 25906470ed6SMauro Carvalho Chehab static int vidioc_s_frequency(struct file *file, void *priv, 26006470ed6SMauro Carvalho Chehab struct v4l2_frequency *f) 261e84fef6bSMauro Carvalho Chehab { 2622710e6aaSHans Verkuil struct maxiradio *dev = video_drvdata(file); 2631da177e4SLinus Torvalds 264a3a9e287SHans Verkuil if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) 265a3a9e287SHans Verkuil return -EINVAL; 266f1557cebSMauro Carvalho Chehab if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) { 2672710e6aaSHans Verkuil dprintk(dev, 1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n", 268f1557cebSMauro Carvalho Chehab f->frequency / 16000, 269f1557cebSMauro Carvalho Chehab f->frequency % 16000 * 100 / 16000, 270f1557cebSMauro Carvalho Chehab FREQ_LO / 16000, FREQ_HI / 16000); 271f1557cebSMauro Carvalho Chehab 2721da177e4SLinus Torvalds return -EINVAL; 273f1557cebSMauro Carvalho Chehab } 274e84fef6bSMauro Carvalho Chehab 2752710e6aaSHans Verkuil mutex_lock(&dev->lock); 2762710e6aaSHans Verkuil dev->freq = f->frequency; 2772710e6aaSHans Verkuil set_freq(dev, dev->freq); 2781da177e4SLinus Torvalds msleep(125); 2792710e6aaSHans Verkuil mutex_unlock(&dev->lock); 28006470ed6SMauro Carvalho Chehab 2811da177e4SLinus Torvalds return 0; 2821da177e4SLinus Torvalds } 28306470ed6SMauro Carvalho Chehab 28406470ed6SMauro Carvalho Chehab static int vidioc_g_frequency(struct file *file, void *priv, 28506470ed6SMauro Carvalho Chehab struct v4l2_frequency *f) 286e84fef6bSMauro Carvalho Chehab { 2872710e6aaSHans Verkuil struct maxiradio *dev = video_drvdata(file); 288e84fef6bSMauro Carvalho Chehab 289a3a9e287SHans Verkuil if (f->tuner != 0) 290a3a9e287SHans Verkuil return -EINVAL; 291e84fef6bSMauro Carvalho Chehab f->type = V4L2_TUNER_RADIO; 2922710e6aaSHans Verkuil f->frequency = dev->freq; 293e84fef6bSMauro Carvalho Chehab 2942710e6aaSHans Verkuil dprintk(dev, 4, "radio freq is %d.%02d MHz", 295f1557cebSMauro Carvalho Chehab f->frequency / 16000, 296f1557cebSMauro Carvalho Chehab f->frequency % 16000 * 100 / 16000); 297f1557cebSMauro Carvalho Chehab 2981da177e4SLinus Torvalds return 0; 2991da177e4SLinus Torvalds } 30006470ed6SMauro Carvalho Chehab 30106470ed6SMauro Carvalho Chehab static int vidioc_queryctrl(struct file *file, void *priv, 30206470ed6SMauro Carvalho Chehab struct v4l2_queryctrl *qc) 303e84fef6bSMauro Carvalho Chehab { 3042710e6aaSHans Verkuil switch (qc->id) { 3052710e6aaSHans Verkuil case V4L2_CID_AUDIO_MUTE: 3062710e6aaSHans Verkuil return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); 307e84fef6bSMauro Carvalho Chehab } 3081da177e4SLinus Torvalds return -EINVAL; 309e84fef6bSMauro Carvalho Chehab } 31006470ed6SMauro Carvalho Chehab 31106470ed6SMauro Carvalho Chehab static int vidioc_g_ctrl(struct file *file, void *priv, 31206470ed6SMauro Carvalho Chehab struct v4l2_control *ctrl) 313e84fef6bSMauro Carvalho Chehab { 3142710e6aaSHans Verkuil struct maxiradio *dev = video_drvdata(file); 315e84fef6bSMauro Carvalho Chehab 316e84fef6bSMauro Carvalho Chehab switch (ctrl->id) { 317e84fef6bSMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 3182710e6aaSHans Verkuil ctrl->value = dev->muted; 3192710e6aaSHans Verkuil return 0; 320e84fef6bSMauro Carvalho Chehab } 321f1557cebSMauro Carvalho Chehab 322e84fef6bSMauro Carvalho Chehab return -EINVAL; 323e84fef6bSMauro Carvalho Chehab } 32406470ed6SMauro Carvalho Chehab 32506470ed6SMauro Carvalho Chehab static int vidioc_s_ctrl(struct file *file, void *priv, 32606470ed6SMauro Carvalho Chehab struct v4l2_control *ctrl) 327e84fef6bSMauro Carvalho Chehab { 3282710e6aaSHans Verkuil struct maxiradio *dev = video_drvdata(file); 329e84fef6bSMauro Carvalho Chehab 330e84fef6bSMauro Carvalho Chehab switch (ctrl->id) { 331e84fef6bSMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 3322710e6aaSHans Verkuil mutex_lock(&dev->lock); 3332710e6aaSHans Verkuil dev->muted = ctrl->value; 3342710e6aaSHans Verkuil if (dev->muted) 3352710e6aaSHans Verkuil turn_power(dev, 0); 3361da177e4SLinus Torvalds else 3372710e6aaSHans Verkuil set_freq(dev, dev->freq); 3382710e6aaSHans Verkuil mutex_unlock(&dev->lock); 3391da177e4SLinus Torvalds return 0; 3401da177e4SLinus Torvalds } 341f1557cebSMauro Carvalho Chehab 342e84fef6bSMauro Carvalho Chehab return -EINVAL; 3431da177e4SLinus Torvalds } 344e84fef6bSMauro Carvalho Chehab 3452710e6aaSHans Verkuil static const struct v4l2_file_operations maxiradio_fops = { 3462710e6aaSHans Verkuil .owner = THIS_MODULE, 34732958fddSHans Verkuil .unlocked_ioctl = video_ioctl2, 3482710e6aaSHans Verkuil }; 3492710e6aaSHans Verkuil 350a399810cSHans Verkuil static const struct v4l2_ioctl_ops maxiradio_ioctl_ops = { 35106470ed6SMauro Carvalho Chehab .vidioc_querycap = vidioc_querycap, 35206470ed6SMauro Carvalho Chehab .vidioc_g_tuner = vidioc_g_tuner, 35306470ed6SMauro Carvalho Chehab .vidioc_s_tuner = vidioc_s_tuner, 354140dcc46SMauro Carvalho Chehab .vidioc_g_audio = vidioc_g_audio, 355140dcc46SMauro Carvalho Chehab .vidioc_s_audio = vidioc_s_audio, 356a0c05ab9SMauro Carvalho Chehab .vidioc_g_input = vidioc_g_input, 357a0c05ab9SMauro Carvalho Chehab .vidioc_s_input = vidioc_s_input, 35806470ed6SMauro Carvalho Chehab .vidioc_g_frequency = vidioc_g_frequency, 35906470ed6SMauro Carvalho Chehab .vidioc_s_frequency = vidioc_s_frequency, 36006470ed6SMauro Carvalho Chehab .vidioc_queryctrl = vidioc_queryctrl, 36106470ed6SMauro Carvalho Chehab .vidioc_g_ctrl = vidioc_g_ctrl, 36206470ed6SMauro Carvalho Chehab .vidioc_s_ctrl = vidioc_s_ctrl, 36306470ed6SMauro Carvalho Chehab }; 3641da177e4SLinus Torvalds 3651da177e4SLinus Torvalds static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 3661da177e4SLinus Torvalds { 3672710e6aaSHans Verkuil struct maxiradio *dev; 3682710e6aaSHans Verkuil struct v4l2_device *v4l2_dev; 3692710e6aaSHans Verkuil int retval = -ENOMEM; 3702710e6aaSHans Verkuil 3712710e6aaSHans Verkuil dev = kzalloc(sizeof(*dev), GFP_KERNEL); 3722710e6aaSHans Verkuil if (dev == NULL) { 3732710e6aaSHans Verkuil dev_err(&pdev->dev, "not enough memory\n"); 3742710e6aaSHans Verkuil return -ENOMEM; 3752710e6aaSHans Verkuil } 3762710e6aaSHans Verkuil 3772710e6aaSHans Verkuil v4l2_dev = &dev->v4l2_dev; 3782710e6aaSHans Verkuil mutex_init(&dev->lock); 3792710e6aaSHans Verkuil dev->pdev = pdev; 3802710e6aaSHans Verkuil dev->muted = 1; 3812710e6aaSHans Verkuil dev->freq = FREQ_LO; 3822710e6aaSHans Verkuil 3832710e6aaSHans Verkuil strlcpy(v4l2_dev->name, "maxiradio", sizeof(v4l2_dev->name)); 3842710e6aaSHans Verkuil 3852710e6aaSHans Verkuil retval = v4l2_device_register(&pdev->dev, v4l2_dev); 3862710e6aaSHans Verkuil if (retval < 0) { 3872710e6aaSHans Verkuil v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); 3882710e6aaSHans Verkuil goto errfr; 3892710e6aaSHans Verkuil } 3902710e6aaSHans Verkuil 3911da177e4SLinus Torvalds if (!request_region(pci_resource_start(pdev, 0), 3921da177e4SLinus Torvalds pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) { 3932710e6aaSHans Verkuil v4l2_err(v4l2_dev, "can't reserve I/O ports\n"); 3941da177e4SLinus Torvalds goto err_out; 3951da177e4SLinus Torvalds } 3961da177e4SLinus Torvalds 3971da177e4SLinus Torvalds if (pci_enable_device(pdev)) 3981da177e4SLinus Torvalds goto err_out_free_region; 3991da177e4SLinus Torvalds 4002710e6aaSHans Verkuil dev->io = pci_resource_start(pdev, 0); 4012710e6aaSHans Verkuil strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); 4022710e6aaSHans Verkuil dev->vdev.v4l2_dev = v4l2_dev; 4032710e6aaSHans Verkuil dev->vdev.fops = &maxiradio_fops; 4042710e6aaSHans Verkuil dev->vdev.ioctl_ops = &maxiradio_ioctl_ops; 4052710e6aaSHans Verkuil dev->vdev.release = video_device_release_empty; 4062710e6aaSHans Verkuil video_set_drvdata(&dev->vdev, dev); 4071da177e4SLinus Torvalds 4082710e6aaSHans Verkuil if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { 4092710e6aaSHans Verkuil v4l2_err(v4l2_dev, "can't register device!"); 4101da177e4SLinus Torvalds goto err_out_free_region; 4111da177e4SLinus Torvalds } 4121da177e4SLinus Torvalds 413372c05c4SMichal Marek v4l2_info(v4l2_dev, "version " DRIVER_VERSION "\n"); 4141da177e4SLinus Torvalds 4152710e6aaSHans Verkuil v4l2_info(v4l2_dev, "found Guillemot MAXI Radio device (io = 0x%x)\n", 4162710e6aaSHans Verkuil dev->io); 4171da177e4SLinus Torvalds return 0; 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds err_out_free_region: 4201da177e4SLinus Torvalds release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); 4211da177e4SLinus Torvalds err_out: 4222710e6aaSHans Verkuil v4l2_device_unregister(v4l2_dev); 4232710e6aaSHans Verkuil errfr: 4242710e6aaSHans Verkuil kfree(dev); 4251da177e4SLinus Torvalds return -ENODEV; 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds static void __devexit maxiradio_remove_one(struct pci_dev *pdev) 4291da177e4SLinus Torvalds { 4302710e6aaSHans Verkuil struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); 4312710e6aaSHans Verkuil struct maxiradio *dev = to_maxiradio(v4l2_dev); 4322710e6aaSHans Verkuil 4332710e6aaSHans Verkuil video_unregister_device(&dev->vdev); 4342710e6aaSHans Verkuil v4l2_device_unregister(&dev->v4l2_dev); 4351da177e4SLinus Torvalds release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); 4361da177e4SLinus Torvalds } 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds static struct pci_device_id maxiradio_pci_tbl[] = { 4391da177e4SLinus Torvalds { PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO, 4401da177e4SLinus Torvalds PCI_ANY_ID, PCI_ANY_ID, }, 4412710e6aaSHans Verkuil { 0 } 4421da177e4SLinus Torvalds }; 4431da177e4SLinus Torvalds 4441da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl); 4451da177e4SLinus Torvalds 4461da177e4SLinus Torvalds static struct pci_driver maxiradio_driver = { 4471da177e4SLinus Torvalds .name = "radio-maxiradio", 4481da177e4SLinus Torvalds .id_table = maxiradio_pci_tbl, 4491da177e4SLinus Torvalds .probe = maxiradio_init_one, 4501da177e4SLinus Torvalds .remove = __devexit_p(maxiradio_remove_one), 4511da177e4SLinus Torvalds }; 4521da177e4SLinus Torvalds 4531da177e4SLinus Torvalds static int __init maxiradio_radio_init(void) 4541da177e4SLinus Torvalds { 4559bfab8ceSRichard Knutsson return pci_register_driver(&maxiradio_driver); 4561da177e4SLinus Torvalds } 4571da177e4SLinus Torvalds 4581da177e4SLinus Torvalds static void __exit maxiradio_radio_exit(void) 4591da177e4SLinus Torvalds { 4601da177e4SLinus Torvalds pci_unregister_driver(&maxiradio_driver); 4611da177e4SLinus Torvalds } 4621da177e4SLinus Torvalds 4631da177e4SLinus Torvalds module_init(maxiradio_radio_init); 4641da177e4SLinus Torvalds module_exit(maxiradio_radio_exit); 465