Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Zoltrix Radio Plus driver
0004  * Copyright 1998 C. van Schaik <carl@leg.uct.ac.za>
0005  *
0006  * BUGS
0007  *  Due to the inconsistency in reading from the signal flags
0008  *  it is difficult to get an accurate tuned signal.
0009  *
0010  *  It seems that the card is not linear to 0 volume. It cuts off
0011  *  at a low volume, and it is not possible (at least I have not found)
0012  *  to get fine volume control over the low volume range.
0013  *
0014  *  Some code derived from code by Romolo Manfredini
0015  *                 romolo@bicnet.it
0016  *
0017  * 1999-05-06 - (C. van Schaik)
0018  *        - Make signal strength and stereo scans
0019  *      kinder to cpu while in delay
0020  * 1999-01-05 - (C. van Schaik)
0021  *        - Changed tuning to 1/160Mhz accuracy
0022  *        - Added stereo support
0023  *      (card defaults to stereo)
0024  *      (can explicitly force mono on the card)
0025  *      (can detect if station is in stereo)
0026  *        - Added unmute function
0027  *        - Reworked ioctl functions
0028  * 2002-07-15 - Fix Stereo typo
0029  *
0030  * 2006-07-24 - Converted to V4L2 API
0031  *      by Mauro Carvalho Chehab <mchehab@kernel.org>
0032  *
0033  * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
0034  *
0035  * Note that this is the driver for the Zoltrix Radio Plus.
0036  * This driver does not work for the Zoltrix Radio Plus 108 or the
0037  * Zoltrix Radio Plus for Windows.
0038  *
0039  * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
0040  */
0041 
0042 #include <linux/module.h>   /* Modules                        */
0043 #include <linux/init.h>     /* Initdata                       */
0044 #include <linux/ioport.h>   /* request_region         */
0045 #include <linux/delay.h>    /* udelay, msleep                 */
0046 #include <linux/videodev2.h>    /* kernel radio structs           */
0047 #include <linux/mutex.h>
0048 #include <linux/io.h>       /* outb, outb_p                   */
0049 #include <linux/slab.h>
0050 #include <media/v4l2-device.h>
0051 #include <media/v4l2-ioctl.h>
0052 #include "radio-isa.h"
0053 
0054 MODULE_AUTHOR("C. van Schaik");
0055 MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
0056 MODULE_LICENSE("GPL");
0057 MODULE_VERSION("0.1.99");
0058 
0059 #ifndef CONFIG_RADIO_ZOLTRIX_PORT
0060 #define CONFIG_RADIO_ZOLTRIX_PORT -1
0061 #endif
0062 
0063 #define ZOLTRIX_MAX 2
0064 
0065 static int io[ZOLTRIX_MAX] = { [0] = CONFIG_RADIO_ZOLTRIX_PORT,
0066                    [1 ... (ZOLTRIX_MAX - 1)] = -1 };
0067 static int radio_nr[ZOLTRIX_MAX] = { [0 ... (ZOLTRIX_MAX - 1)] = -1 };
0068 
0069 module_param_array(io, int, NULL, 0444);
0070 MODULE_PARM_DESC(io, "I/O addresses of the Zoltrix Radio Plus card (0x20c or 0x30c)");
0071 module_param_array(radio_nr, int, NULL, 0444);
0072 MODULE_PARM_DESC(radio_nr, "Radio device numbers");
0073 
0074 struct zoltrix {
0075     struct radio_isa_card isa;
0076     int curvol;
0077     bool muted;
0078 };
0079 
0080 static struct radio_isa_card *zoltrix_alloc(void)
0081 {
0082     struct zoltrix *zol = kzalloc(sizeof(*zol), GFP_KERNEL);
0083 
0084     return zol ? &zol->isa : NULL;
0085 }
0086 
0087 static int zoltrix_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
0088 {
0089     struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
0090 
0091     zol->curvol = vol;
0092     zol->muted = mute;
0093     if (mute || vol == 0) {
0094         outb(0, isa->io);
0095         outb(0, isa->io);
0096         inb(isa->io + 3);            /* Zoltrix needs to be read to confirm */
0097         return 0;
0098     }
0099 
0100     outb(vol - 1, isa->io);
0101     msleep(10);
0102     inb(isa->io + 2);
0103     return 0;
0104 }
0105 
0106 /* tunes the radio to the desired frequency */
0107 static int zoltrix_s_frequency(struct radio_isa_card *isa, u32 freq)
0108 {
0109     struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
0110     struct v4l2_device *v4l2_dev = &isa->v4l2_dev;
0111     unsigned long long bitmask, f, m;
0112     bool stereo = isa->stereo;
0113     int i;
0114 
0115     if (freq == 0) {
0116         v4l2_warn(v4l2_dev, "cannot set a frequency of 0.\n");
0117         return -EINVAL;
0118     }
0119 
0120     m = (freq / 160 - 8800) * 2;
0121     f = (unsigned long long)m + 0x4d1c;
0122 
0123     bitmask = 0xc480402c10080000ull;
0124     i = 45;
0125 
0126     outb(0, isa->io);
0127     outb(0, isa->io);
0128     inb(isa->io + 3);            /* Zoltrix needs to be read to confirm */
0129 
0130     outb(0x40, isa->io);
0131     outb(0xc0, isa->io);
0132 
0133     bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ (stereo << 31));
0134     while (i--) {
0135         if ((bitmask & 0x8000000000000000ull) != 0) {
0136             outb(0x80, isa->io);
0137             udelay(50);
0138             outb(0x00, isa->io);
0139             udelay(50);
0140             outb(0x80, isa->io);
0141             udelay(50);
0142         } else {
0143             outb(0xc0, isa->io);
0144             udelay(50);
0145             outb(0x40, isa->io);
0146             udelay(50);
0147             outb(0xc0, isa->io);
0148             udelay(50);
0149         }
0150         bitmask *= 2;
0151     }
0152     /* termination sequence */
0153     outb(0x80, isa->io);
0154     outb(0xc0, isa->io);
0155     outb(0x40, isa->io);
0156     udelay(1000);
0157     inb(isa->io + 2);
0158     udelay(1000);
0159 
0160     return zoltrix_s_mute_volume(isa, zol->muted, zol->curvol);
0161 }
0162 
0163 /* Get signal strength */
0164 static u32 zoltrix_g_rxsubchans(struct radio_isa_card *isa)
0165 {
0166     struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
0167     int a, b;
0168 
0169     outb(0x00, isa->io);         /* This stuff I found to do nothing */
0170     outb(zol->curvol, isa->io);
0171     msleep(20);
0172 
0173     a = inb(isa->io);
0174     msleep(10);
0175     b = inb(isa->io);
0176 
0177     return (a == b && a == 0xcf) ?
0178         V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
0179 }
0180 
0181 static u32 zoltrix_g_signal(struct radio_isa_card *isa)
0182 {
0183     struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
0184     int a, b;
0185 
0186     outb(0x00, isa->io);         /* This stuff I found to do nothing */
0187     outb(zol->curvol, isa->io);
0188     msleep(20);
0189 
0190     a = inb(isa->io);
0191     msleep(10);
0192     b = inb(isa->io);
0193 
0194     if (a != b)
0195         return 0;
0196 
0197     /* I found this out by playing with a binary scanner on the card io */
0198     return (a == 0xcf || a == 0xdf || a == 0xef) ? 0xffff : 0;
0199 }
0200 
0201 static int zoltrix_s_stereo(struct radio_isa_card *isa, bool stereo)
0202 {
0203     return zoltrix_s_frequency(isa, isa->freq);
0204 }
0205 
0206 static const struct radio_isa_ops zoltrix_ops = {
0207     .alloc = zoltrix_alloc,
0208     .s_mute_volume = zoltrix_s_mute_volume,
0209     .s_frequency = zoltrix_s_frequency,
0210     .s_stereo = zoltrix_s_stereo,
0211     .g_rxsubchans = zoltrix_g_rxsubchans,
0212     .g_signal = zoltrix_g_signal,
0213 };
0214 
0215 static const int zoltrix_ioports[] = { 0x20c, 0x30c };
0216 
0217 static struct radio_isa_driver zoltrix_driver = {
0218     .driver = {
0219         .match      = radio_isa_match,
0220         .probe      = radio_isa_probe,
0221         .remove     = radio_isa_remove,
0222         .driver     = {
0223             .name   = "radio-zoltrix",
0224         },
0225     },
0226     .io_params = io,
0227     .radio_nr_params = radio_nr,
0228     .io_ports = zoltrix_ioports,
0229     .num_of_io_ports = ARRAY_SIZE(zoltrix_ioports),
0230     .region_size = 2,
0231     .card = "Zoltrix Radio Plus",
0232     .ops = &zoltrix_ops,
0233     .has_stereo = true,
0234     .max_volume = 15,
0235 };
0236 
0237 static int __init zoltrix_init(void)
0238 {
0239     return isa_register_driver(&zoltrix_driver.driver, ZOLTRIX_MAX);
0240 }
0241 
0242 static void __exit zoltrix_exit(void)
0243 {
0244     isa_unregister_driver(&zoltrix_driver.driver);
0245 }
0246 
0247 module_init(zoltrix_init);
0248 module_exit(zoltrix_exit);
0249