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