1*09c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux 41da177e4SLinus Torvalds * (C) 2001 Dimitromanolakis Apostolos <apdim@grecian.net> 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * Based in the radio Maestro PCI driver. Actually it uses the same chip 71da177e4SLinus Torvalds * for radio but different pci controller. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * I didn't have any specs I reversed engineered the protocol from 101da177e4SLinus Torvalds * the windows driver (radio.dll). 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * The card uses the TEA5757 chip that includes a search function but it 131da177e4SLinus Torvalds * is useless as I haven't found any way to read back the frequency. If 141da177e4SLinus Torvalds * anybody does please mail me. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * For the pdf file see: 17631dd1a8SJustin P. Mattock * http://www.nxp.com/acrobat_download2/expired_datasheets/TEA5757_5759_3.pdf 181da177e4SLinus Torvalds * 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds * CHANGES: 211da177e4SLinus Torvalds * 0.75b 221da177e4SLinus Torvalds * - better pci interface thanks to Francois Romieu <romieu@cogenit.fr> 231da177e4SLinus Torvalds * 24e84fef6bSMauro Carvalho Chehab * 0.75 Sun Feb 4 22:51:27 EET 2001 251da177e4SLinus Torvalds * - tiding up 261da177e4SLinus Torvalds * - removed support for multiple devices as it didn't work anyway 271da177e4SLinus Torvalds * 281da177e4SLinus Torvalds * BUGS: 291da177e4SLinus Torvalds * - card unmutes if you change frequency 301da177e4SLinus Torvalds * 3132590819SMauro Carvalho Chehab * (c) 2006, 2007 by Mauro Carvalho Chehab <mchehab@kernel.org>: 3206470ed6SMauro Carvalho Chehab * - Conversion to V4L2 API 3306470ed6SMauro Carvalho Chehab * - Uses video_ioctl2 for parsing and to add debug support 341da177e4SLinus Torvalds */ 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds #include <linux/module.h> 381da177e4SLinus Torvalds #include <linux/init.h> 391da177e4SLinus Torvalds #include <linux/ioport.h> 401da177e4SLinus Torvalds #include <linux/delay.h> 413593cab5SIngo Molnar #include <linux/mutex.h> 421da177e4SLinus Torvalds #include <linux/pci.h> 43e84fef6bSMauro Carvalho Chehab #include <linux/videodev2.h> 442710e6aaSHans Verkuil #include <linux/io.h> 455a0e3ad6STejun Heo #include <linux/slab.h> 46d647f0b7SMauro Carvalho Chehab #include <media/drv-intf/tea575x.h> 472710e6aaSHans Verkuil #include <media/v4l2-device.h> 4835ea11ffSHans Verkuil #include <media/v4l2-ioctl.h> 49cfb19b0aSHans Verkuil #include <media/v4l2-fh.h> 50cfb19b0aSHans Verkuil #include <media/v4l2-ctrls.h> 51cfb19b0aSHans Verkuil #include <media/v4l2-event.h> 5229834c1aSMauro Carvalho Chehab 532710e6aaSHans Verkuil MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net"); 54cfb19b0aSHans Verkuil MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000."); 552710e6aaSHans Verkuil MODULE_LICENSE("GPL"); 56cfb19b0aSHans Verkuil MODULE_VERSION("1.0.0"); 572710e6aaSHans Verkuil 582710e6aaSHans Verkuil static int radio_nr = -1; 59cfb19b0aSHans Verkuil module_param(radio_nr, int, 0644); 60cfb19b0aSHans Verkuil MODULE_PARM_DESC(radio_nr, "Radio device number"); 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds /* TEA5757 pin mappings */ 631da177e4SLinus Torvalds static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16; 641da177e4SLinus Torvalds 65cfb19b0aSHans Verkuil static atomic_t maxiradio_instance = ATOMIC_INIT(0); 661da177e4SLinus Torvalds 67cfb19b0aSHans Verkuil #define PCI_VENDOR_ID_GUILLEMOT 0x5046 68cfb19b0aSHans Verkuil #define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001 691da177e4SLinus Torvalds 702710e6aaSHans Verkuil struct maxiradio 713ca685aaSHans Verkuil { 72cfb19b0aSHans Verkuil struct snd_tea575x tea; 732710e6aaSHans Verkuil struct v4l2_device v4l2_dev; 742710e6aaSHans Verkuil struct pci_dev *pdev; 753ca685aaSHans Verkuil 762710e6aaSHans Verkuil u16 io; /* base of radio io */ 77712642b8SMauro Carvalho Chehab }; 781da177e4SLinus Torvalds 792710e6aaSHans Verkuil static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev) 801da177e4SLinus Torvalds { 812710e6aaSHans Verkuil return container_of(v4l2_dev, struct maxiradio, v4l2_dev); 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds 84cfb19b0aSHans Verkuil static void maxiradio_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) 852710e6aaSHans Verkuil { 86cfb19b0aSHans Verkuil struct maxiradio *dev = tea->private_data; 87cfb19b0aSHans Verkuil u8 bits = 0; 882710e6aaSHans Verkuil 89cfb19b0aSHans Verkuil bits |= (pins & TEA575X_DATA) ? data : 0; 90cfb19b0aSHans Verkuil bits |= (pins & TEA575X_CLK) ? clk : 0; 91cfb19b0aSHans Verkuil bits |= (pins & TEA575X_WREN) ? wren : 0; 92cfb19b0aSHans Verkuil bits |= power; 93cfb19b0aSHans Verkuil 94cfb19b0aSHans Verkuil outb(bits, dev->io); 952710e6aaSHans Verkuil } 962710e6aaSHans Verkuil 97cfb19b0aSHans Verkuil /* Note: this card cannot read out the data of the shift registers, 98cfb19b0aSHans Verkuil only the mono/stereo pin works. */ 99cfb19b0aSHans Verkuil static u8 maxiradio_tea575x_get_pins(struct snd_tea575x *tea) 1001da177e4SLinus Torvalds { 101cfb19b0aSHans Verkuil struct maxiradio *dev = tea->private_data; 102cfb19b0aSHans Verkuil u8 bits = inb(dev->io); 103cfb19b0aSHans Verkuil 104cfb19b0aSHans Verkuil return ((bits & data) ? TEA575X_DATA : 0) | 105cfb19b0aSHans Verkuil ((bits & mo_st) ? TEA575X_MOST : 0); 1061da177e4SLinus Torvalds } 1071da177e4SLinus Torvalds 108cfb19b0aSHans Verkuil static void maxiradio_tea575x_set_direction(struct snd_tea575x *tea, bool output) 1091da177e4SLinus Torvalds { 110f1557cebSMauro Carvalho Chehab } 1111da177e4SLinus Torvalds 11222dbec26SJulia Lawall static const struct snd_tea575x_ops maxiradio_tea_ops = { 113cfb19b0aSHans Verkuil .set_pins = maxiradio_tea575x_set_pins, 114cfb19b0aSHans Verkuil .get_pins = maxiradio_tea575x_get_pins, 115cfb19b0aSHans Verkuil .set_direction = maxiradio_tea575x_set_direction, 1162710e6aaSHans Verkuil }; 1172710e6aaSHans Verkuil 1184c62e976SGreg Kroah-Hartman static int maxiradio_probe(struct pci_dev *pdev, 1194c62e976SGreg Kroah-Hartman const struct pci_device_id *ent) 1201da177e4SLinus Torvalds { 1212710e6aaSHans Verkuil struct maxiradio *dev; 1222710e6aaSHans Verkuil struct v4l2_device *v4l2_dev; 1232710e6aaSHans Verkuil int retval = -ENOMEM; 1242710e6aaSHans Verkuil 1252710e6aaSHans Verkuil dev = kzalloc(sizeof(*dev), GFP_KERNEL); 1262710e6aaSHans Verkuil if (dev == NULL) { 1272710e6aaSHans Verkuil dev_err(&pdev->dev, "not enough memory\n"); 1282710e6aaSHans Verkuil return -ENOMEM; 1292710e6aaSHans Verkuil } 1302710e6aaSHans Verkuil 1312710e6aaSHans Verkuil v4l2_dev = &dev->v4l2_dev; 132cfb19b0aSHans Verkuil v4l2_device_set_name(v4l2_dev, "maxiradio", &maxiradio_instance); 1332710e6aaSHans Verkuil 1342710e6aaSHans Verkuil retval = v4l2_device_register(&pdev->dev, v4l2_dev); 1352710e6aaSHans Verkuil if (retval < 0) { 1362710e6aaSHans Verkuil v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); 1372710e6aaSHans Verkuil goto errfr; 1382710e6aaSHans Verkuil } 139cfb19b0aSHans Verkuil dev->tea.private_data = dev; 140cfb19b0aSHans Verkuil dev->tea.ops = &maxiradio_tea_ops; 141cfb19b0aSHans Verkuil /* The data pin cannot be read. This may be a hardware limitation, or 142cfb19b0aSHans Verkuil we just don't know how to read it. */ 143cfb19b0aSHans Verkuil dev->tea.cannot_read_data = true; 144cfb19b0aSHans Verkuil dev->tea.v4l2_dev = v4l2_dev; 145cfb19b0aSHans Verkuil dev->tea.radio_nr = radio_nr; 146c0decac1SMauro Carvalho Chehab strscpy(dev->tea.card, "Maxi Radio FM2000", sizeof(dev->tea.card)); 147cfb19b0aSHans Verkuil snprintf(dev->tea.bus_info, sizeof(dev->tea.bus_info), 148cfb19b0aSHans Verkuil "PCI:%s", pci_name(pdev)); 149cfb19b0aSHans Verkuil 150cfb19b0aSHans Verkuil retval = -ENODEV; 1512710e6aaSHans Verkuil 1521da177e4SLinus Torvalds if (!request_region(pci_resource_start(pdev, 0), 153cfb19b0aSHans Verkuil pci_resource_len(pdev, 0), v4l2_dev->name)) { 154cfb19b0aSHans Verkuil dev_err(&pdev->dev, "can't reserve I/O ports\n"); 155cfb19b0aSHans Verkuil goto err_hdl; 1561da177e4SLinus Torvalds } 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds if (pci_enable_device(pdev)) 1591da177e4SLinus Torvalds goto err_out_free_region; 1601da177e4SLinus Torvalds 1612710e6aaSHans Verkuil dev->io = pci_resource_start(pdev, 0); 1625daf53a6SHans de Goede if (snd_tea575x_init(&dev->tea, THIS_MODULE)) { 163cfb19b0aSHans Verkuil printk(KERN_ERR "radio-maxiradio: Unable to detect TEA575x tuner\n"); 1641da177e4SLinus Torvalds goto err_out_free_region; 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds return 0; 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds err_out_free_region: 1691da177e4SLinus Torvalds release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); 170cfb19b0aSHans Verkuil err_hdl: 1712710e6aaSHans Verkuil v4l2_device_unregister(v4l2_dev); 1722710e6aaSHans Verkuil errfr: 1732710e6aaSHans Verkuil kfree(dev); 174cfb19b0aSHans Verkuil return retval; 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds 1774c62e976SGreg Kroah-Hartman static void maxiradio_remove(struct pci_dev *pdev) 1781da177e4SLinus Torvalds { 1792710e6aaSHans Verkuil struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); 1802710e6aaSHans Verkuil struct maxiradio *dev = to_maxiradio(v4l2_dev); 1812710e6aaSHans Verkuil 182cfb19b0aSHans Verkuil snd_tea575x_exit(&dev->tea); 183cfb19b0aSHans Verkuil /* Turn off power */ 184cfb19b0aSHans Verkuil outb(0, dev->io); 185cfb19b0aSHans Verkuil v4l2_device_unregister(v4l2_dev); 1861da177e4SLinus Torvalds release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); 18704bc9365SAlexey Khoroshilov kfree(dev); 1881da177e4SLinus Torvalds } 1891da177e4SLinus Torvalds 190c181efd2SArvind Yadav static const struct pci_device_id maxiradio_pci_tbl[] = { 1911da177e4SLinus Torvalds { PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO, 1921da177e4SLinus Torvalds PCI_ANY_ID, PCI_ANY_ID, }, 1932710e6aaSHans Verkuil { 0 } 1941da177e4SLinus Torvalds }; 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl); 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds static struct pci_driver maxiradio_driver = { 1991da177e4SLinus Torvalds .name = "radio-maxiradio", 2001da177e4SLinus Torvalds .id_table = maxiradio_pci_tbl, 201cfb19b0aSHans Verkuil .probe = maxiradio_probe, 2024c62e976SGreg Kroah-Hartman .remove = maxiradio_remove, 2031da177e4SLinus Torvalds }; 2041da177e4SLinus Torvalds 205725d7b71SLibo Chen module_pci_driver(maxiradio_driver); 206