1 /* 2 * Zoltrix Radio Plus driver 3 * Copyright 1998 C. van Schaik <carl@leg.uct.ac.za> 4 * 5 * BUGS 6 * Due to the inconsistency in reading from the signal flags 7 * it is difficult to get an accurate tuned signal. 8 * 9 * It seems that the card is not linear to 0 volume. It cuts off 10 * at a low volume, and it is not possible (at least I have not found) 11 * to get fine volume control over the low volume range. 12 * 13 * Some code derived from code by Romolo Manfredini 14 * romolo@bicnet.it 15 * 16 * 1999-05-06 - (C. van Schaik) 17 * - Make signal strength and stereo scans 18 * kinder to cpu while in delay 19 * 1999-01-05 - (C. van Schaik) 20 * - Changed tuning to 1/160Mhz accuracy 21 * - Added stereo support 22 * (card defaults to stereo) 23 * (can explicitly force mono on the card) 24 * (can detect if station is in stereo) 25 * - Added unmute function 26 * - Reworked ioctl functions 27 * 2002-07-15 - Fix Stereo typo 28 * 29 * 2006-07-24 - Converted to V4L2 API 30 * by Mauro Carvalho Chehab <mchehab@kernel.org> 31 * 32 * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> 33 * 34 * Note that this is the driver for the Zoltrix Radio Plus. 35 * This driver does not work for the Zoltrix Radio Plus 108 or the 36 * Zoltrix Radio Plus for Windows. 37 * 38 * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. 39 */ 40 41 #include <linux/module.h> /* Modules */ 42 #include <linux/init.h> /* Initdata */ 43 #include <linux/ioport.h> /* request_region */ 44 #include <linux/delay.h> /* udelay, msleep */ 45 #include <linux/videodev2.h> /* kernel radio structs */ 46 #include <linux/mutex.h> 47 #include <linux/io.h> /* outb, outb_p */ 48 #include <linux/slab.h> 49 #include <media/v4l2-device.h> 50 #include <media/v4l2-ioctl.h> 51 #include "radio-isa.h" 52 53 MODULE_AUTHOR("C. van Schaik"); 54 MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus."); 55 MODULE_LICENSE("GPL"); 56 MODULE_VERSION("0.1.99"); 57 58 #ifndef CONFIG_RADIO_ZOLTRIX_PORT 59 #define CONFIG_RADIO_ZOLTRIX_PORT -1 60 #endif 61 62 #define ZOLTRIX_MAX 2 63 64 static int io[ZOLTRIX_MAX] = { [0] = CONFIG_RADIO_ZOLTRIX_PORT, 65 [1 ... (ZOLTRIX_MAX - 1)] = -1 }; 66 static int radio_nr[ZOLTRIX_MAX] = { [0 ... (ZOLTRIX_MAX - 1)] = -1 }; 67 68 module_param_array(io, int, NULL, 0444); 69 MODULE_PARM_DESC(io, "I/O addresses of the Zoltrix Radio Plus card (0x20c or 0x30c)"); 70 module_param_array(radio_nr, int, NULL, 0444); 71 MODULE_PARM_DESC(radio_nr, "Radio device numbers"); 72 73 struct zoltrix { 74 struct radio_isa_card isa; 75 int curvol; 76 bool muted; 77 }; 78 79 static struct radio_isa_card *zoltrix_alloc(void) 80 { 81 struct zoltrix *zol = kzalloc(sizeof(*zol), GFP_KERNEL); 82 83 return zol ? &zol->isa : NULL; 84 } 85 86 static int zoltrix_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) 87 { 88 struct zoltrix *zol = container_of(isa, struct zoltrix, isa); 89 90 zol->curvol = vol; 91 zol->muted = mute; 92 if (mute || vol == 0) { 93 outb(0, isa->io); 94 outb(0, isa->io); 95 inb(isa->io + 3); /* Zoltrix needs to be read to confirm */ 96 return 0; 97 } 98 99 outb(vol - 1, isa->io); 100 msleep(10); 101 inb(isa->io + 2); 102 return 0; 103 } 104 105 /* tunes the radio to the desired frequency */ 106 static int zoltrix_s_frequency(struct radio_isa_card *isa, u32 freq) 107 { 108 struct zoltrix *zol = container_of(isa, struct zoltrix, isa); 109 struct v4l2_device *v4l2_dev = &isa->v4l2_dev; 110 unsigned long long bitmask, f, m; 111 bool stereo = isa->stereo; 112 int i; 113 114 if (freq == 0) { 115 v4l2_warn(v4l2_dev, "cannot set a frequency of 0.\n"); 116 return -EINVAL; 117 } 118 119 m = (freq / 160 - 8800) * 2; 120 f = (unsigned long long)m + 0x4d1c; 121 122 bitmask = 0xc480402c10080000ull; 123 i = 45; 124 125 outb(0, isa->io); 126 outb(0, isa->io); 127 inb(isa->io + 3); /* Zoltrix needs to be read to confirm */ 128 129 outb(0x40, isa->io); 130 outb(0xc0, isa->io); 131 132 bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ (stereo << 31)); 133 while (i--) { 134 if ((bitmask & 0x8000000000000000ull) != 0) { 135 outb(0x80, isa->io); 136 udelay(50); 137 outb(0x00, isa->io); 138 udelay(50); 139 outb(0x80, isa->io); 140 udelay(50); 141 } else { 142 outb(0xc0, isa->io); 143 udelay(50); 144 outb(0x40, isa->io); 145 udelay(50); 146 outb(0xc0, isa->io); 147 udelay(50); 148 } 149 bitmask *= 2; 150 } 151 /* termination sequence */ 152 outb(0x80, isa->io); 153 outb(0xc0, isa->io); 154 outb(0x40, isa->io); 155 udelay(1000); 156 inb(isa->io + 2); 157 udelay(1000); 158 159 return zoltrix_s_mute_volume(isa, zol->muted, zol->curvol); 160 } 161 162 /* Get signal strength */ 163 static u32 zoltrix_g_rxsubchans(struct radio_isa_card *isa) 164 { 165 struct zoltrix *zol = container_of(isa, struct zoltrix, isa); 166 int a, b; 167 168 outb(0x00, isa->io); /* This stuff I found to do nothing */ 169 outb(zol->curvol, isa->io); 170 msleep(20); 171 172 a = inb(isa->io); 173 msleep(10); 174 b = inb(isa->io); 175 176 return (a == b && a == 0xcf) ? 177 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; 178 } 179 180 static u32 zoltrix_g_signal(struct radio_isa_card *isa) 181 { 182 struct zoltrix *zol = container_of(isa, struct zoltrix, isa); 183 int a, b; 184 185 outb(0x00, isa->io); /* This stuff I found to do nothing */ 186 outb(zol->curvol, isa->io); 187 msleep(20); 188 189 a = inb(isa->io); 190 msleep(10); 191 b = inb(isa->io); 192 193 if (a != b) 194 return 0; 195 196 /* I found this out by playing with a binary scanner on the card io */ 197 return (a == 0xcf || a == 0xdf || a == 0xef) ? 0xffff : 0; 198 } 199 200 static int zoltrix_s_stereo(struct radio_isa_card *isa, bool stereo) 201 { 202 return zoltrix_s_frequency(isa, isa->freq); 203 } 204 205 static const struct radio_isa_ops zoltrix_ops = { 206 .alloc = zoltrix_alloc, 207 .s_mute_volume = zoltrix_s_mute_volume, 208 .s_frequency = zoltrix_s_frequency, 209 .s_stereo = zoltrix_s_stereo, 210 .g_rxsubchans = zoltrix_g_rxsubchans, 211 .g_signal = zoltrix_g_signal, 212 }; 213 214 static const int zoltrix_ioports[] = { 0x20c, 0x30c }; 215 216 static struct radio_isa_driver zoltrix_driver = { 217 .driver = { 218 .match = radio_isa_match, 219 .probe = radio_isa_probe, 220 .remove = radio_isa_remove, 221 .driver = { 222 .name = "radio-zoltrix", 223 }, 224 }, 225 .io_params = io, 226 .radio_nr_params = radio_nr, 227 .io_ports = zoltrix_ioports, 228 .num_of_io_ports = ARRAY_SIZE(zoltrix_ioports), 229 .region_size = 2, 230 .card = "Zoltrix Radio Plus", 231 .ops = &zoltrix_ops, 232 .has_stereo = true, 233 .max_volume = 15, 234 }; 235 236 static int __init zoltrix_init(void) 237 { 238 return isa_register_driver(&zoltrix_driver.driver, ZOLTRIX_MAX); 239 } 240 241 static void __exit zoltrix_exit(void) 242 { 243 isa_unregister_driver(&zoltrix_driver.driver); 244 } 245 246 module_init(zoltrix_init); 247 module_exit(zoltrix_exit); 248 249