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 53 MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net"); 54 MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000."); 55 MODULE_LICENSE("GPL"); 56 MODULE_VERSION("1.0.0"); 57 58 static int radio_nr = -1; 59 module_param(radio_nr, int, 0644); 60 MODULE_PARM_DESC(radio_nr, "Radio device number"); 61 62 /* TEA5757 pin mappings */ 63 static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16; 64 65 static atomic_t maxiradio_instance = ATOMIC_INIT(0); 66 67 #define PCI_VENDOR_ID_GUILLEMOT 0x5046 68 #define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001 69 70 struct 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 79 static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev) 80 { 81 return container_of(v4l2_dev, struct maxiradio, v4l2_dev); 82 } 83 84 static 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. */ 99 static 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 108 static void maxiradio_tea575x_set_direction(struct snd_tea575x *tea, bool output) 109 { 110 } 111 112 static 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 118 static 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 snprintf(dev->tea.bus_info, sizeof(dev->tea.bus_info), 148 "PCI:%s", pci_name(pdev)); 149 150 retval = -ENODEV; 151 152 if (!request_region(pci_resource_start(pdev, 0), 153 pci_resource_len(pdev, 0), v4l2_dev->name)) { 154 dev_err(&pdev->dev, "can't reserve I/O ports\n"); 155 goto err_hdl; 156 } 157 158 if (pci_enable_device(pdev)) 159 goto err_out_free_region; 160 161 dev->io = pci_resource_start(pdev, 0); 162 if (snd_tea575x_init(&dev->tea, THIS_MODULE)) { 163 printk(KERN_ERR "radio-maxiradio: Unable to detect TEA575x tuner\n"); 164 goto err_out_free_region; 165 } 166 return 0; 167 168 err_out_free_region: 169 release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); 170 err_hdl: 171 v4l2_device_unregister(v4l2_dev); 172 errfr: 173 kfree(dev); 174 return retval; 175 } 176 177 static void maxiradio_remove(struct pci_dev *pdev) 178 { 179 struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); 180 struct maxiradio *dev = to_maxiradio(v4l2_dev); 181 182 snd_tea575x_exit(&dev->tea); 183 /* Turn off power */ 184 outb(0, dev->io); 185 v4l2_device_unregister(v4l2_dev); 186 release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); 187 kfree(dev); 188 } 189 190 static const struct pci_device_id maxiradio_pci_tbl[] = { 191 { PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO, 192 PCI_ANY_ID, PCI_ANY_ID, }, 193 { 0 } 194 }; 195 196 MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl); 197 198 static struct pci_driver maxiradio_driver = { 199 .name = "radio-maxiradio", 200 .id_table = maxiradio_pci_tbl, 201 .probe = maxiradio_probe, 202 .remove = maxiradio_remove, 203 }; 204 205 module_pci_driver(maxiradio_driver); 206