1 1.20 christos /* $NetBSD: radiotrack.c,v 1.20 2014/03/23 02:59:19 christos Exp $ */ 2 1.1 augustss /* $OpenBSD: radiotrack.c,v 1.1 2001/12/05 10:27:06 mickey Exp $ */ 3 1.1 augustss /* $RuOBSD: radiotrack.c,v 1.3 2001/10/18 16:51:36 pva Exp $ */ 4 1.1 augustss 5 1.1 augustss /* 6 1.1 augustss * Copyright (c) 2001 Maxim Tsyplakov <tm (at) oganer.net>, 7 1.1 augustss * Vladimir Popov <jumbo (at) narod.ru> 8 1.1 augustss * All rights reserved. 9 1.1 augustss * 10 1.1 augustss * Redistribution and use in source and binary forms, with or without 11 1.1 augustss * modification, are permitted provided that the following conditions 12 1.1 augustss * are met: 13 1.1 augustss * 1. Redistributions of source code must retain the above copyright 14 1.1 augustss * notice, this list of conditions and the following disclaimer. 15 1.1 augustss * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 augustss * notice, this list of conditions and the following disclaimer in the 17 1.1 augustss * documentation and/or other materials provided with the distribution. 18 1.1 augustss * 19 1.1 augustss * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 20 1.1 augustss * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 1.1 augustss * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 1.1 augustss * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 1.1 augustss * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 1.1 augustss * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 1.1 augustss * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 1.1 augustss * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 1.1 augustss * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 1.1 augustss * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 1.1 augustss */ 30 1.1 augustss 31 1.1 augustss /* AIMS Lab Radiotrack FM Radio Card device driver */ 32 1.1 augustss 33 1.1 augustss /* 34 1.1 augustss * Sanyo LM7000 Direct PLL Frequency Synthesizer 35 1.1 augustss */ 36 1.9 lukem 37 1.9 lukem #include <sys/cdefs.h> 38 1.20 christos __KERNEL_RCSID(0, "$NetBSD: radiotrack.c,v 1.20 2014/03/23 02:59:19 christos Exp $"); 39 1.1 augustss 40 1.1 augustss #include <sys/param.h> 41 1.1 augustss #include <sys/systm.h> 42 1.1 augustss #include <sys/proc.h> 43 1.1 augustss #include <sys/errno.h> 44 1.1 augustss #include <sys/ioctl.h> 45 1.1 augustss #include <sys/device.h> 46 1.1 augustss #include <sys/radioio.h> 47 1.1 augustss 48 1.15 ad #include <sys/bus.h> 49 1.1 augustss 50 1.1 augustss #include <dev/isa/isavar.h> 51 1.1 augustss #include <dev/ic/lm700x.h> 52 1.1 augustss #include <dev/radio_if.h> 53 1.1 augustss 54 1.1 augustss #define RF_25K 25 55 1.1 augustss #define RF_50K 50 56 1.1 augustss #define RF_100K 100 57 1.1 augustss 58 1.1 augustss #define MAX_VOL 5 /* XXX Find real value */ 59 1.1 augustss #define VOLUME_RATIO(x) (255 * x / MAX_VOL) 60 1.1 augustss 61 1.1 augustss #define RT_BASE_VALID(x) \ 62 1.1 augustss ((x == 0x30C) || (x == 0x20C) || (x == 0x284) || (x == 0x384)) 63 1.1 augustss 64 1.1 augustss #define CARD_RADIOTRACK 0x01 65 1.1 augustss #define CARD_SF16FMI 0x02 66 1.1 augustss #define CARD_UNKNOWN 0xFF 67 1.1 augustss 68 1.1 augustss #define RTRACK_CAPABILITIES RADIO_CAPS_DETECT_STEREO | \ 69 1.1 augustss RADIO_CAPS_DETECT_SIGNAL | \ 70 1.1 augustss RADIO_CAPS_SET_MONO | \ 71 1.1 augustss RADIO_CAPS_REFERENCE_FREQ 72 1.1 augustss 73 1.1 augustss #define RT_WREN_ON (1 << 0) 74 1.1 augustss #define RT_WREN_OFF (0 << 0) 75 1.1 augustss 76 1.1 augustss #define RT_CLCK_ON (1 << 1) 77 1.1 augustss #define RT_CLCK_OFF (0 << 1) 78 1.1 augustss 79 1.1 augustss #define RT_DATA_ON (1 << 2) 80 1.1 augustss #define RT_DATA_OFF (0 << 2) 81 1.1 augustss 82 1.1 augustss #define RT_CARD_ON (1 << 3) 83 1.1 augustss #define RT_CARD_OFF (0 << 3) 84 1.1 augustss 85 1.1 augustss #define RT_SIGNAL_METER (1 << 4) 86 1.1 augustss #define RT_SIGNAL_METER_DELAY 150000 87 1.1 augustss 88 1.1 augustss #define RT_VOLUME_DOWN (1 << 6) 89 1.1 augustss #define RT_VOLUME_UP (2 << 6) 90 1.1 augustss #define RT_VOLUME_STEADY (3 << 6) 91 1.1 augustss #define RT_VOLUME_DELAY 100000 92 1.1 augustss 93 1.18 cegger int rt_probe(device_t, cfdata_t, void *); 94 1.18 cegger void rt_attach(device_t, device_t self, void *); 95 1.1 augustss int rt_get_info(void *, struct radio_info *); 96 1.1 augustss int rt_set_info(void *, struct radio_info *); 97 1.1 augustss 98 1.10 yamt const struct radio_hw_if rt_hw_if = { 99 1.1 augustss NULL, /* open */ 100 1.1 augustss NULL, /* close */ 101 1.1 augustss rt_get_info, 102 1.1 augustss rt_set_info, 103 1.1 augustss NULL 104 1.1 augustss }; 105 1.1 augustss 106 1.1 augustss struct rt_softc { 107 1.1 augustss int mute; 108 1.1 augustss u_int8_t vol; 109 1.1 augustss u_int8_t cardtype; 110 1.1 augustss u_int32_t freq; 111 1.1 augustss u_int32_t rf; 112 1.1 augustss u_int32_t stereo; 113 1.1 augustss 114 1.1 augustss struct lm700x_t lm; 115 1.1 augustss }; 116 1.1 augustss 117 1.19 chs CFATTACH_DECL_NEW(rt, sizeof(struct rt_softc), 118 1.8 thorpej rt_probe, rt_attach, NULL, NULL); 119 1.1 augustss 120 1.1 augustss int rt_find(bus_space_tag_t, bus_space_handle_t); 121 1.1 augustss void rt_set_mute(struct rt_softc *, int); 122 1.1 augustss void rt_set_freq(struct rt_softc *, u_int32_t); 123 1.1 augustss u_int8_t rt_state(bus_space_tag_t, bus_space_handle_t); 124 1.1 augustss 125 1.1 augustss void rt_lm700x_init(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t); 126 1.1 augustss void rt_lm700x_rset(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t); 127 1.1 augustss 128 1.1 augustss u_int8_t rt_conv_vol(u_int8_t); 129 1.1 augustss u_int8_t rt_unconv_vol(u_int8_t); 130 1.1 augustss 131 1.1 augustss int 132 1.18 cegger rt_probe(device_t parent, cfdata_t cf, void *aux) 133 1.1 augustss { 134 1.1 augustss struct isa_attach_args *ia = aux; 135 1.1 augustss bus_space_tag_t iot = ia->ia_iot; 136 1.1 augustss bus_space_handle_t ioh; 137 1.3 augustss u_int r; 138 1.5 thorpej int iosize = 1, iobase; 139 1.5 thorpej 140 1.5 thorpej if (ISA_DIRECT_CONFIG(ia)) 141 1.5 thorpej return 0; 142 1.5 thorpej 143 1.5 thorpej if (ia->ia_nio < 1) 144 1.5 thorpej return (0); 145 1.5 thorpej 146 1.5 thorpej iobase = ia->ia_io[0].ir_addr; 147 1.1 augustss 148 1.1 augustss if (!RT_BASE_VALID(iobase)) { 149 1.2 augustss printf("rt: configured iobase 0x%x invalid\n", iobase); 150 1.1 augustss return 0; 151 1.1 augustss } 152 1.1 augustss 153 1.1 augustss if (bus_space_map(iot, iobase, iosize, 0, &ioh)) 154 1.1 augustss return 0; 155 1.1 augustss 156 1.3 augustss r = rt_find(iot, ioh); 157 1.3 augustss 158 1.1 augustss bus_space_unmap(iot, ioh, iosize); 159 1.1 augustss 160 1.5 thorpej if (r != 0) { 161 1.5 thorpej ia->ia_nio = 1; 162 1.5 thorpej ia->ia_io[0].ir_size = iosize; 163 1.5 thorpej 164 1.5 thorpej ia->ia_niomem = 0; 165 1.5 thorpej ia->ia_nirq = 0; 166 1.5 thorpej ia->ia_ndrq = 0; 167 1.5 thorpej 168 1.5 thorpej return (1); 169 1.5 thorpej } 170 1.1 augustss 171 1.5 thorpej return (0); 172 1.1 augustss } 173 1.1 augustss 174 1.1 augustss void 175 1.18 cegger rt_attach(device_t parent, device_t self, void *aux) 176 1.1 augustss { 177 1.19 chs struct rt_softc *sc = device_private(self); 178 1.1 augustss struct isa_attach_args *ia = aux; 179 1.1 augustss 180 1.1 augustss sc->lm.iot = ia->ia_iot; 181 1.1 augustss sc->rf = LM700X_REF_050; 182 1.1 augustss sc->stereo = LM700X_STEREO; 183 1.1 augustss sc->mute = 0; 184 1.1 augustss sc->freq = MIN_FM_FREQ; 185 1.1 augustss sc->vol = 0; 186 1.1 augustss 187 1.1 augustss /* remap I/O */ 188 1.5 thorpej if (bus_space_map(sc->lm.iot, ia->ia_io[0].ir_addr, 189 1.5 thorpej ia->ia_io[0].ir_size, 0, &sc->lm.ioh)) 190 1.19 chs panic(": bus_space_map() of %s failed", device_xname(self)); 191 1.1 augustss 192 1.5 thorpej switch (ia->ia_io[0].ir_addr) { 193 1.1 augustss case 0x20C: 194 1.1 augustss /* FALLTHROUGH */ 195 1.1 augustss case 0x30C: 196 1.1 augustss sc->cardtype = CARD_RADIOTRACK; 197 1.4 augustss printf(": AIMS Lab Radiotrack or compatible\n"); 198 1.1 augustss break; 199 1.1 augustss case 0x284: 200 1.1 augustss /* FALLTHROUGH */ 201 1.1 augustss case 0x384: 202 1.1 augustss sc->cardtype = CARD_SF16FMI; 203 1.4 augustss printf(": SoundForte RadioX SF16-FMI\n"); 204 1.1 augustss break; 205 1.1 augustss default: 206 1.1 augustss sc->cardtype = CARD_UNKNOWN; 207 1.4 augustss printf(": Unknown card\n"); 208 1.1 augustss break; 209 1.1 augustss } 210 1.1 augustss 211 1.1 augustss /* Configure struct lm700x_t lm */ 212 1.1 augustss sc->lm.offset = 0; 213 1.1 augustss sc->lm.wzcl = RT_WREN_ON | RT_CLCK_OFF | RT_DATA_OFF; 214 1.1 augustss sc->lm.wzch = RT_WREN_ON | RT_CLCK_ON | RT_DATA_OFF; 215 1.1 augustss sc->lm.wocl = RT_WREN_ON | RT_CLCK_OFF | RT_DATA_ON; 216 1.1 augustss sc->lm.woch = RT_WREN_ON | RT_CLCK_ON | RT_DATA_ON; 217 1.1 augustss sc->lm.initdata = 0; 218 1.1 augustss sc->lm.rsetdata = RT_DATA_ON | RT_CLCK_ON | RT_WREN_OFF; 219 1.1 augustss sc->lm.init = rt_lm700x_init; 220 1.1 augustss sc->lm.rset = rt_lm700x_rset; 221 1.1 augustss 222 1.1 augustss rt_set_freq(sc, sc->freq); 223 1.1 augustss 224 1.19 chs radio_attach_mi(&rt_hw_if, sc, self); 225 1.1 augustss } 226 1.1 augustss 227 1.1 augustss /* 228 1.1 augustss * Mute the card 229 1.1 augustss */ 230 1.1 augustss void 231 1.1 augustss rt_set_mute(struct rt_softc *sc, int vol) 232 1.1 augustss { 233 1.1 augustss int val; 234 1.1 augustss 235 1.1 augustss if (sc->mute) { 236 1.1 augustss bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0, 237 1.1 augustss RT_VOLUME_DOWN | RT_CARD_ON); 238 1.1 augustss DELAY(MAX_VOL * RT_VOLUME_DELAY); 239 1.1 augustss bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0, 240 1.1 augustss RT_VOLUME_STEADY | RT_CARD_ON); 241 1.1 augustss bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0, RT_CARD_OFF); 242 1.1 augustss bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0, RT_CARD_OFF); 243 1.1 augustss } else { 244 1.1 augustss val = sc->vol - vol; 245 1.1 augustss if (val < 0) { 246 1.1 augustss val *= -1; 247 1.1 augustss bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0, 248 1.1 augustss RT_VOLUME_DOWN | RT_CARD_ON); 249 1.1 augustss } else { 250 1.1 augustss bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0, 251 1.1 augustss RT_VOLUME_UP | RT_CARD_ON); 252 1.1 augustss } 253 1.1 augustss DELAY(val * RT_VOLUME_DELAY); 254 1.1 augustss bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0, 255 1.1 augustss RT_VOLUME_STEADY | RT_CARD_ON); 256 1.1 augustss } 257 1.1 augustss } 258 1.1 augustss 259 1.1 augustss void 260 1.1 augustss rt_set_freq(struct rt_softc *sc, u_int32_t nfreq) 261 1.1 augustss { 262 1.1 augustss u_int32_t reg; 263 1.1 augustss 264 1.1 augustss if (nfreq > MAX_FM_FREQ) 265 1.1 augustss nfreq = MAX_FM_FREQ; 266 1.1 augustss if (nfreq < MIN_FM_FREQ) 267 1.1 augustss nfreq = MIN_FM_FREQ; 268 1.1 augustss 269 1.1 augustss sc->freq = nfreq; 270 1.1 augustss 271 1.1 augustss reg = lm700x_encode_freq(nfreq, sc->rf); 272 1.1 augustss reg |= sc->stereo | sc->rf | LM700X_DIVIDER_FM; 273 1.1 augustss 274 1.1 augustss lm700x_hardware_write(&sc->lm, reg, RT_VOLUME_STEADY); 275 1.1 augustss 276 1.1 augustss rt_set_mute(sc, sc->vol); 277 1.1 augustss } 278 1.1 augustss 279 1.1 augustss /* 280 1.1 augustss * Return state of the card - tuned/not tuned, mono/stereo 281 1.1 augustss */ 282 1.1 augustss u_int8_t 283 1.1 augustss rt_state(bus_space_tag_t iot, bus_space_handle_t ioh) 284 1.1 augustss { 285 1.1 augustss u_int8_t ret; 286 1.1 augustss 287 1.1 augustss bus_space_write_1(iot, ioh, 0, 288 1.1 augustss RT_VOLUME_STEADY | RT_SIGNAL_METER | RT_CARD_ON); 289 1.1 augustss DELAY(RT_SIGNAL_METER_DELAY); 290 1.1 augustss ret = bus_space_read_1(iot, ioh, 0); 291 1.1 augustss 292 1.1 augustss switch (ret) { 293 1.1 augustss case 0xFD: 294 1.1 augustss ret = RADIO_INFO_SIGNAL | RADIO_INFO_STEREO; 295 1.1 augustss break; 296 1.1 augustss case 0xFF: 297 1.1 augustss ret = 0; 298 1.1 augustss break; 299 1.1 augustss default: 300 1.1 augustss ret = RADIO_INFO_SIGNAL; 301 1.1 augustss break; 302 1.1 augustss } 303 1.11 perry 304 1.1 augustss return ret; 305 1.1 augustss } 306 1.1 augustss 307 1.1 augustss /* 308 1.1 augustss * Convert volume to hardware representation. 309 1.1 augustss */ 310 1.1 augustss u_int8_t 311 1.1 augustss rt_conv_vol(u_int8_t vol) 312 1.1 augustss { 313 1.1 augustss if (vol < VOLUME_RATIO(1)) 314 1.1 augustss return 0; 315 1.1 augustss else if (vol >= VOLUME_RATIO(1) && vol < VOLUME_RATIO(2)) 316 1.1 augustss return 1; 317 1.1 augustss else if (vol >= VOLUME_RATIO(2) && vol < VOLUME_RATIO(3)) 318 1.1 augustss return 2; 319 1.1 augustss else if (vol >= VOLUME_RATIO(3) && vol < VOLUME_RATIO(4)) 320 1.1 augustss return 3; 321 1.1 augustss else 322 1.1 augustss return 4; 323 1.1 augustss } 324 1.1 augustss 325 1.1 augustss /* 326 1.1 augustss * Convert volume from hardware representation 327 1.1 augustss */ 328 1.1 augustss u_int8_t 329 1.1 augustss rt_unconv_vol(u_int8_t vol) 330 1.1 augustss { 331 1.1 augustss return VOLUME_RATIO(vol); 332 1.1 augustss } 333 1.1 augustss 334 1.1 augustss int 335 1.1 augustss rt_find(bus_space_tag_t iot, bus_space_handle_t ioh) 336 1.1 augustss { 337 1.20 christos #ifdef notdef 338 1.1 augustss struct rt_softc sc; 339 1.1 augustss u_int i, scanres = 0; 340 1.1 augustss 341 1.1 augustss sc.lm.iot = iot; 342 1.1 augustss sc.lm.ioh = ioh; 343 1.1 augustss sc.lm.offset = 0; 344 1.1 augustss sc.lm.wzcl = RT_WREN_ON | RT_CLCK_OFF | RT_DATA_OFF; 345 1.1 augustss sc.lm.wzch = RT_WREN_ON | RT_CLCK_ON | RT_DATA_OFF; 346 1.1 augustss sc.lm.wocl = RT_WREN_ON | RT_CLCK_OFF | RT_DATA_ON; 347 1.1 augustss sc.lm.woch = RT_WREN_ON | RT_CLCK_ON | RT_DATA_ON; 348 1.1 augustss sc.lm.initdata = 0; 349 1.1 augustss sc.lm.rsetdata = RT_SIGNAL_METER; 350 1.1 augustss sc.lm.init = rt_lm700x_init; 351 1.1 augustss sc.lm.rset = rt_lm700x_rset; 352 1.1 augustss sc.rf = LM700X_REF_050; 353 1.1 augustss sc.mute = 0; 354 1.1 augustss sc.stereo = LM700X_STEREO; 355 1.1 augustss sc.vol = 0; 356 1.1 augustss 357 1.1 augustss /* 358 1.1 augustss * Scan whole FM range. If there is a card it'll 359 1.1 augustss * respond on some frequency. 360 1.1 augustss */ 361 1.1 augustss for (i = MIN_FM_FREQ; !scanres && i < MAX_FM_FREQ; i += 10) { 362 1.1 augustss rt_set_freq(&sc, i); 363 1.1 augustss scanres += rt_state(iot, ioh); 364 1.1 augustss } 365 1.1 augustss 366 1.1 augustss return scanres; 367 1.20 christos #else 368 1.20 christos return 0; 369 1.1 augustss #endif 370 1.1 augustss } 371 1.1 augustss 372 1.1 augustss void 373 1.14 christos rt_lm700x_init(bus_space_tag_t iot, bus_space_handle_t ioh, 374 1.14 christos bus_size_t off, u_int32_t data) 375 1.1 augustss { 376 1.1 augustss /* Do nothing */ 377 1.1 augustss return; 378 1.1 augustss } 379 1.1 augustss 380 1.1 augustss void 381 1.1 augustss rt_lm700x_rset(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off, 382 1.1 augustss u_int32_t data) 383 1.1 augustss { 384 1.1 augustss DELAY(1000); 385 1.1 augustss bus_space_write_1(iot, ioh, off, RT_CARD_OFF | data); 386 1.1 augustss DELAY(50000); 387 1.1 augustss bus_space_write_1(iot, ioh, off, RT_VOLUME_STEADY | RT_CARD_ON | data); 388 1.1 augustss } 389 1.1 augustss 390 1.1 augustss int 391 1.1 augustss rt_set_info(void *v, struct radio_info *ri) 392 1.1 augustss { 393 1.1 augustss struct rt_softc *sc = v; 394 1.1 augustss 395 1.1 augustss sc->mute = ri->mute ? 1 : 0; 396 1.1 augustss sc->vol = rt_conv_vol(ri->volume); 397 1.1 augustss sc->stereo = ri->stereo ? LM700X_STEREO : LM700X_MONO; 398 1.1 augustss sc->rf = lm700x_encode_ref(ri->rfreq); 399 1.1 augustss 400 1.1 augustss rt_set_freq(sc, ri->freq); 401 1.1 augustss rt_set_mute(sc, sc->vol); 402 1.1 augustss 403 1.1 augustss return (0); 404 1.1 augustss } 405 1.1 augustss 406 1.1 augustss int 407 1.1 augustss rt_get_info(void *v, struct radio_info *ri) 408 1.1 augustss { 409 1.1 augustss struct rt_softc *sc = v; 410 1.1 augustss 411 1.1 augustss ri->mute = sc->mute; 412 1.1 augustss ri->volume = rt_unconv_vol(sc->vol); 413 1.1 augustss ri->stereo = sc->stereo == LM700X_STEREO ? 0 : 1; 414 1.1 augustss ri->caps = RTRACK_CAPABILITIES; 415 1.1 augustss ri->rfreq = lm700x_decode_ref(sc->rf); 416 1.1 augustss ri->info = 3 & rt_state(sc->lm.iot, sc->lm.ioh); 417 1.1 augustss ri->freq = sc->freq; 418 1.1 augustss 419 1.1 augustss /* UNSUPPORTED */ 420 1.1 augustss ri->lock = 0; 421 1.1 augustss 422 1.1 augustss return (0); 423 1.1 augustss } 424