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> 45cfb19b0aSHans Verkuil #include <sound/tea575x-tuner.h> 462710e6aaSHans Verkuil #include <media/v4l2-device.h> 4735ea11ffSHans Verkuil #include <media/v4l2-ioctl.h> 48cfb19b0aSHans Verkuil #include <media/v4l2-fh.h> 49cfb19b0aSHans Verkuil #include <media/v4l2-ctrls.h> 50cfb19b0aSHans Verkuil #include <media/v4l2-event.h> 5129834c1aSMauro Carvalho Chehab 522710e6aaSHans Verkuil MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net"); 53cfb19b0aSHans Verkuil MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000."); 542710e6aaSHans Verkuil MODULE_LICENSE("GPL"); 55cfb19b0aSHans Verkuil MODULE_VERSION("1.0.0"); 562710e6aaSHans Verkuil 572710e6aaSHans Verkuil static int radio_nr = -1; 58cfb19b0aSHans Verkuil module_param(radio_nr, int, 0644); 59cfb19b0aSHans Verkuil MODULE_PARM_DESC(radio_nr, "Radio device number"); 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds /* TEA5757 pin mappings */ 621da177e4SLinus Torvalds static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16; 631da177e4SLinus Torvalds 64cfb19b0aSHans Verkuil static atomic_t maxiradio_instance = ATOMIC_INIT(0); 651da177e4SLinus Torvalds 66cfb19b0aSHans Verkuil #define PCI_VENDOR_ID_GUILLEMOT 0x5046 67cfb19b0aSHans Verkuil #define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001 681da177e4SLinus Torvalds 692710e6aaSHans Verkuil struct maxiradio 703ca685aaSHans Verkuil { 71cfb19b0aSHans Verkuil struct snd_tea575x tea; 722710e6aaSHans Verkuil struct v4l2_device v4l2_dev; 732710e6aaSHans Verkuil struct pci_dev *pdev; 743ca685aaSHans Verkuil 752710e6aaSHans Verkuil u16 io; /* base of radio io */ 76712642b8SMauro Carvalho Chehab }; 771da177e4SLinus Torvalds 782710e6aaSHans Verkuil static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev) 791da177e4SLinus Torvalds { 802710e6aaSHans Verkuil return container_of(v4l2_dev, struct maxiradio, v4l2_dev); 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds 83cfb19b0aSHans Verkuil static void maxiradio_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) 842710e6aaSHans Verkuil { 85cfb19b0aSHans Verkuil struct maxiradio *dev = tea->private_data; 86cfb19b0aSHans Verkuil u8 bits = 0; 872710e6aaSHans Verkuil 88cfb19b0aSHans Verkuil bits |= (pins & TEA575X_DATA) ? data : 0; 89cfb19b0aSHans Verkuil bits |= (pins & TEA575X_CLK) ? clk : 0; 90cfb19b0aSHans Verkuil bits |= (pins & TEA575X_WREN) ? wren : 0; 91cfb19b0aSHans Verkuil bits |= power; 92cfb19b0aSHans Verkuil 93cfb19b0aSHans Verkuil outb(bits, dev->io); 942710e6aaSHans Verkuil } 952710e6aaSHans Verkuil 96cfb19b0aSHans Verkuil /* Note: this card cannot read out the data of the shift registers, 97cfb19b0aSHans Verkuil only the mono/stereo pin works. */ 98cfb19b0aSHans Verkuil static u8 maxiradio_tea575x_get_pins(struct snd_tea575x *tea) 991da177e4SLinus Torvalds { 100cfb19b0aSHans Verkuil struct maxiradio *dev = tea->private_data; 101cfb19b0aSHans Verkuil u8 bits = inb(dev->io); 102cfb19b0aSHans Verkuil 103cfb19b0aSHans Verkuil return ((bits & data) ? TEA575X_DATA : 0) | 104cfb19b0aSHans Verkuil ((bits & mo_st) ? TEA575X_MOST : 0); 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds 107cfb19b0aSHans Verkuil static void maxiradio_tea575x_set_direction(struct snd_tea575x *tea, bool output) 1081da177e4SLinus Torvalds { 109f1557cebSMauro Carvalho Chehab } 1101da177e4SLinus Torvalds 111cfb19b0aSHans Verkuil static struct snd_tea575x_ops maxiradio_tea_ops = { 112cfb19b0aSHans Verkuil .set_pins = maxiradio_tea575x_set_pins, 113cfb19b0aSHans Verkuil .get_pins = maxiradio_tea575x_get_pins, 114cfb19b0aSHans Verkuil .set_direction = maxiradio_tea575x_set_direction, 1152710e6aaSHans Verkuil }; 1162710e6aaSHans Verkuil 117cfb19b0aSHans Verkuil static int __devinit maxiradio_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 1181da177e4SLinus Torvalds { 1192710e6aaSHans Verkuil struct maxiradio *dev; 1202710e6aaSHans Verkuil struct v4l2_device *v4l2_dev; 1212710e6aaSHans Verkuil int retval = -ENOMEM; 1222710e6aaSHans Verkuil 1232710e6aaSHans Verkuil dev = kzalloc(sizeof(*dev), GFP_KERNEL); 1242710e6aaSHans Verkuil if (dev == NULL) { 1252710e6aaSHans Verkuil dev_err(&pdev->dev, "not enough memory\n"); 1262710e6aaSHans Verkuil return -ENOMEM; 1272710e6aaSHans Verkuil } 1282710e6aaSHans Verkuil 1292710e6aaSHans Verkuil v4l2_dev = &dev->v4l2_dev; 130cfb19b0aSHans Verkuil v4l2_device_set_name(v4l2_dev, "maxiradio", &maxiradio_instance); 1312710e6aaSHans Verkuil 1322710e6aaSHans Verkuil retval = v4l2_device_register(&pdev->dev, v4l2_dev); 1332710e6aaSHans Verkuil if (retval < 0) { 1342710e6aaSHans Verkuil v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); 1352710e6aaSHans Verkuil goto errfr; 1362710e6aaSHans Verkuil } 137cfb19b0aSHans Verkuil dev->tea.private_data = dev; 138cfb19b0aSHans Verkuil dev->tea.ops = &maxiradio_tea_ops; 139cfb19b0aSHans Verkuil /* The data pin cannot be read. This may be a hardware limitation, or 140cfb19b0aSHans Verkuil we just don't know how to read it. */ 141cfb19b0aSHans Verkuil dev->tea.cannot_read_data = true; 142cfb19b0aSHans Verkuil dev->tea.v4l2_dev = v4l2_dev; 143cfb19b0aSHans Verkuil dev->tea.radio_nr = radio_nr; 144cfb19b0aSHans Verkuil strlcpy(dev->tea.card, "Maxi Radio FM2000", sizeof(dev->tea.card)); 145cfb19b0aSHans Verkuil snprintf(dev->tea.bus_info, sizeof(dev->tea.bus_info), 146cfb19b0aSHans Verkuil "PCI:%s", pci_name(pdev)); 147cfb19b0aSHans Verkuil 148cfb19b0aSHans Verkuil retval = -ENODEV; 1492710e6aaSHans Verkuil 1501da177e4SLinus Torvalds if (!request_region(pci_resource_start(pdev, 0), 151cfb19b0aSHans Verkuil pci_resource_len(pdev, 0), v4l2_dev->name)) { 152cfb19b0aSHans Verkuil dev_err(&pdev->dev, "can't reserve I/O ports\n"); 153cfb19b0aSHans Verkuil goto err_hdl; 1541da177e4SLinus Torvalds } 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds if (pci_enable_device(pdev)) 1571da177e4SLinus Torvalds goto err_out_free_region; 1581da177e4SLinus Torvalds 1592710e6aaSHans Verkuil dev->io = pci_resource_start(pdev, 0); 160*5daf53a6SHans de Goede if (snd_tea575x_init(&dev->tea, THIS_MODULE)) { 161cfb19b0aSHans Verkuil printk(KERN_ERR "radio-maxiradio: Unable to detect TEA575x tuner\n"); 1621da177e4SLinus Torvalds goto err_out_free_region; 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds return 0; 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds err_out_free_region: 1671da177e4SLinus Torvalds release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); 168cfb19b0aSHans Verkuil err_hdl: 1692710e6aaSHans Verkuil v4l2_device_unregister(v4l2_dev); 1702710e6aaSHans Verkuil errfr: 1712710e6aaSHans Verkuil kfree(dev); 172cfb19b0aSHans Verkuil return retval; 1731da177e4SLinus Torvalds } 1741da177e4SLinus Torvalds 175cfb19b0aSHans Verkuil static void __devexit maxiradio_remove(struct pci_dev *pdev) 1761da177e4SLinus Torvalds { 1772710e6aaSHans Verkuil struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); 1782710e6aaSHans Verkuil struct maxiradio *dev = to_maxiradio(v4l2_dev); 1792710e6aaSHans Verkuil 180cfb19b0aSHans Verkuil snd_tea575x_exit(&dev->tea); 181cfb19b0aSHans Verkuil /* Turn off power */ 182cfb19b0aSHans Verkuil outb(0, dev->io); 183cfb19b0aSHans Verkuil v4l2_device_unregister(v4l2_dev); 1841da177e4SLinus Torvalds release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds static struct pci_device_id maxiradio_pci_tbl[] = { 1881da177e4SLinus Torvalds { PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO, 1891da177e4SLinus Torvalds PCI_ANY_ID, PCI_ANY_ID, }, 1902710e6aaSHans Verkuil { 0 } 1911da177e4SLinus Torvalds }; 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl); 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds static struct pci_driver maxiradio_driver = { 1961da177e4SLinus Torvalds .name = "radio-maxiradio", 1971da177e4SLinus Torvalds .id_table = maxiradio_pci_tbl, 198cfb19b0aSHans Verkuil .probe = maxiradio_probe, 199cfb19b0aSHans Verkuil .remove = __devexit_p(maxiradio_remove), 2001da177e4SLinus Torvalds }; 2011da177e4SLinus Torvalds 202cfb19b0aSHans Verkuil static int __init maxiradio_init(void) 2031da177e4SLinus Torvalds { 2049bfab8ceSRichard Knutsson return pci_register_driver(&maxiradio_driver); 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds 207cfb19b0aSHans Verkuil static void __exit maxiradio_exit(void) 2081da177e4SLinus Torvalds { 2091da177e4SLinus Torvalds pci_unregister_driver(&maxiradio_driver); 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds 212cfb19b0aSHans Verkuil module_init(maxiradio_init); 213cfb19b0aSHans Verkuil module_exit(maxiradio_exit); 214