radio-maxiradio.c (5568B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux 4 * (C) 2001 Dimitromanolakis Apostolos <apdim@grecian.net> 5 * 6 * Based in the radio Maestro PCI driver. Actually it uses the same chip 7 * for radio but different pci controller. 8 * 9 * I didn't have any specs I reversed engineered the protocol from 10 * the windows driver (radio.dll). 11 * 12 * The card uses the TEA5757 chip that includes a search function but it 13 * is useless as I haven't found any way to read back the frequency. If 14 * anybody does please mail me. 15 * 16 * For the pdf file see: 17 * http://www.nxp.com/acrobat_download2/expired_datasheets/TEA5757_5759_3.pdf 18 * 19 * 20 * CHANGES: 21 * 0.75b 22 * - better pci interface thanks to Francois Romieu <romieu@cogenit.fr> 23 * 24 * 0.75 Sun Feb 4 22:51:27 EET 2001 25 * - tiding up 26 * - removed support for multiple devices as it didn't work anyway 27 * 28 * BUGS: 29 * - card unmutes if you change frequency 30 * 31 * (c) 2006, 2007 by Mauro Carvalho Chehab <mchehab@kernel.org>: 32 * - Conversion to V4L2 API 33 * - Uses video_ioctl2 for parsing and to add debug support 34 */ 35 36 37#include <linux/module.h> 38#include <linux/init.h> 39#include <linux/ioport.h> 40#include <linux/delay.h> 41#include <linux/mutex.h> 42#include <linux/pci.h> 43#include <linux/videodev2.h> 44#include <linux/io.h> 45#include <linux/slab.h> 46#include <media/drv-intf/tea575x.h> 47#include <media/v4l2-device.h> 48#include <media/v4l2-ioctl.h> 49#include <media/v4l2-fh.h> 50#include <media/v4l2-ctrls.h> 51#include <media/v4l2-event.h> 52 53MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net"); 54MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000."); 55MODULE_LICENSE("GPL"); 56MODULE_VERSION("1.0.0"); 57 58static int radio_nr = -1; 59module_param(radio_nr, int, 0644); 60MODULE_PARM_DESC(radio_nr, "Radio device number"); 61 62/* TEA5757 pin mappings */ 63static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16; 64 65static atomic_t maxiradio_instance = ATOMIC_INIT(0); 66 67#define PCI_VENDOR_ID_GUILLEMOT 0x5046 68#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001 69 70struct maxiradio 71{ 72 struct snd_tea575x tea; 73 struct v4l2_device v4l2_dev; 74 struct pci_dev *pdev; 75 76 u16 io; /* base of radio io */ 77}; 78 79static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev) 80{ 81 return container_of(v4l2_dev, struct maxiradio, v4l2_dev); 82} 83 84static void maxiradio_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) 85{ 86 struct maxiradio *dev = tea->private_data; 87 u8 bits = 0; 88 89 bits |= (pins & TEA575X_DATA) ? data : 0; 90 bits |= (pins & TEA575X_CLK) ? clk : 0; 91 bits |= (pins & TEA575X_WREN) ? wren : 0; 92 bits |= power; 93 94 outb(bits, dev->io); 95} 96 97/* Note: this card cannot read out the data of the shift registers, 98 only the mono/stereo pin works. */ 99static u8 maxiradio_tea575x_get_pins(struct snd_tea575x *tea) 100{ 101 struct maxiradio *dev = tea->private_data; 102 u8 bits = inb(dev->io); 103 104 return ((bits & data) ? TEA575X_DATA : 0) | 105 ((bits & mo_st) ? TEA575X_MOST : 0); 106} 107 108static void maxiradio_tea575x_set_direction(struct snd_tea575x *tea, bool output) 109{ 110} 111 112static const struct snd_tea575x_ops maxiradio_tea_ops = { 113 .set_pins = maxiradio_tea575x_set_pins, 114 .get_pins = maxiradio_tea575x_get_pins, 115 .set_direction = maxiradio_tea575x_set_direction, 116}; 117 118static int maxiradio_probe(struct pci_dev *pdev, 119 const struct pci_device_id *ent) 120{ 121 struct maxiradio *dev; 122 struct v4l2_device *v4l2_dev; 123 int retval = -ENOMEM; 124 125 dev = kzalloc(sizeof(*dev), GFP_KERNEL); 126 if (dev == NULL) { 127 dev_err(&pdev->dev, "not enough memory\n"); 128 return -ENOMEM; 129 } 130 131 v4l2_dev = &dev->v4l2_dev; 132 v4l2_device_set_name(v4l2_dev, "maxiradio", &maxiradio_instance); 133 134 retval = v4l2_device_register(&pdev->dev, v4l2_dev); 135 if (retval < 0) { 136 v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); 137 goto errfr; 138 } 139 dev->tea.private_data = dev; 140 dev->tea.ops = &maxiradio_tea_ops; 141 /* The data pin cannot be read. This may be a hardware limitation, or 142 we just don't know how to read it. */ 143 dev->tea.cannot_read_data = true; 144 dev->tea.v4l2_dev = v4l2_dev; 145 dev->tea.radio_nr = radio_nr; 146 strscpy(dev->tea.card, "Maxi Radio FM2000", sizeof(dev->tea.card)); 147 148 retval = -ENODEV; 149 150 if (!request_region(pci_resource_start(pdev, 0), 151 pci_resource_len(pdev, 0), v4l2_dev->name)) { 152 dev_err(&pdev->dev, "can't reserve I/O ports\n"); 153 goto err_hdl; 154 } 155 156 if (pci_enable_device(pdev)) 157 goto err_out_free_region; 158 159 dev->io = pci_resource_start(pdev, 0); 160 if (snd_tea575x_init(&dev->tea, THIS_MODULE)) { 161 printk(KERN_ERR "radio-maxiradio: Unable to detect TEA575x tuner\n"); 162 goto err_out_free_region; 163 } 164 return 0; 165 166err_out_free_region: 167 release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); 168err_hdl: 169 v4l2_device_unregister(v4l2_dev); 170errfr: 171 kfree(dev); 172 return retval; 173} 174 175static void maxiradio_remove(struct pci_dev *pdev) 176{ 177 struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); 178 struct maxiradio *dev = to_maxiradio(v4l2_dev); 179 180 snd_tea575x_exit(&dev->tea); 181 /* Turn off power */ 182 outb(0, dev->io); 183 v4l2_device_unregister(v4l2_dev); 184 release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); 185 kfree(dev); 186} 187 188static const struct pci_device_id maxiradio_pci_tbl[] = { 189 { PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO, 190 PCI_ANY_ID, PCI_ANY_ID, }, 191 { 0 } 192}; 193 194MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl); 195 196static struct pci_driver maxiradio_driver = { 197 .name = "radio-maxiradio", 198 .id_table = maxiradio_pci_tbl, 199 .probe = maxiradio_probe, 200 .remove = maxiradio_remove, 201}; 202 203module_pci_driver(maxiradio_driver);