Annotation of sys/dev/isa/aztech.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: aztech.c,v 1.4 2002/01/23 20:30:38 mickey Exp $ */
! 2: /* $RuOBSD: aztech.c,v 1.11 2001/10/20 13:23:47 pva Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 2001 Maxim Tsyplakov <tm@oganer.net>,
! 6: * Vladimir Popov <jumbo@narod.ru>
! 7: * All rights reserved.
! 8: *
! 9: * Redistribution and use in source and binary forms, with or without
! 10: * modification, are permitted provided that the following conditions
! 11: * are met:
! 12: * 1. Redistributions of source code must retain the above copyright
! 13: * notice, this list of conditions and the following disclaimer.
! 14: * 2. Redistributions in binary form must reproduce the above copyright
! 15: * notice, this list of conditions and the following disclaimer in the
! 16: * documentation and/or other materials provided with the distribution.
! 17: *
! 18: * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
! 19: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 20: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 21: * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
! 22: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 23: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 24: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 25: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 26: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 27: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 28: */
! 29:
! 30: /* Aztech/PackardBell FM Radio Card device driver */
! 31:
! 32: /*
! 33: * Sanyo LM7001J Direct PLL Frequency Synthesizer:
! 34: * ??? See http://www.redsword.com/tjacobs/geeb/fmcard.htm
! 35: *
! 36: * Philips TEA5712T AM/FM Stereo DTS Radio:
! 37: * http://www.semiconductors.philips.com/pip/TEA5712
! 38: */
! 39:
! 40: #include <sys/param.h>
! 41: #include <sys/systm.h>
! 42: #include <sys/proc.h>
! 43: #include <sys/errno.h>
! 44: #include <sys/ioctl.h>
! 45: #include <sys/device.h>
! 46: #include <sys/radioio.h>
! 47:
! 48: #include <machine/bus.h>
! 49:
! 50: #include <dev/isa/isavar.h>
! 51: #include <dev/ic/lm700x.h>
! 52: #include <dev/radio_if.h>
! 53:
! 54: #define RF_25K 25
! 55: #define RF_50K 50
! 56: #define RF_100K 100
! 57:
! 58: #define MAX_VOL 3
! 59: #define VOLUME_RATIO(x) (255 * x / MAX_VOL)
! 60:
! 61: #define AZ_BASE_VALID(x) ((x == 0x350) || (x == 0x358))
! 62: #define AZTECH_CAPABILITIES RADIO_CAPS_DETECT_STEREO | \
! 63: RADIO_CAPS_DETECT_SIGNAL | \
! 64: RADIO_CAPS_SET_MONO | \
! 65: RADIO_CAPS_REFERENCE_FREQ
! 66:
! 67:
! 68: #define AZTECH_STEREO (1 << 0)
! 69: #define AZTECH_SIGNAL (1 << 1)
! 70:
! 71: #define AZ_WREN_ON (1 << 1)
! 72: #define AZ_WREN_OFF (0 << 1)
! 73:
! 74: #define AZ_CLCK_ON (1 << 6)
! 75: #define AZ_CLCK_OFF (0 << 6)
! 76:
! 77: #define AZ_DATA_ON (1 << 7)
! 78: #define AZ_DATA_OFF (0 << 7)
! 79:
! 80: int az_probe(struct device *, void *, void *);
! 81: void az_attach(struct device *, struct device * self, void *);
! 82:
! 83: int az_get_info(void *, struct radio_info *);
! 84: int az_set_info(void *, struct radio_info *);
! 85:
! 86: struct radio_hw_if az_hw_if = {
! 87: NULL, /* open */
! 88: NULL, /* close */
! 89: az_get_info,
! 90: az_set_info,
! 91: NULL
! 92: };
! 93:
! 94: struct az_softc {
! 95: struct device sc_dev;
! 96:
! 97: int mute;
! 98: u_int8_t vol;
! 99: u_int32_t freq;
! 100: u_int32_t rf;
! 101: u_int32_t stereo;
! 102:
! 103: struct lm700x_t lm;
! 104: };
! 105:
! 106: struct cfattach az_ca = {
! 107: sizeof(struct az_softc), az_probe, az_attach
! 108: };
! 109:
! 110: struct cfdriver az_cd = {
! 111: NULL, "az", DV_DULL
! 112: };
! 113:
! 114: u_int az_find(bus_space_tag_t, bus_space_handle_t);
! 115: void az_set_mute(struct az_softc *);
! 116: void az_set_freq(struct az_softc *, u_int32_t);
! 117: u_int8_t az_state(bus_space_tag_t, bus_space_handle_t);
! 118:
! 119: void az_lm700x_init(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t);
! 120: void az_lm700x_rset(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t);
! 121:
! 122: u_int8_t az_conv_vol(u_int8_t);
! 123: u_int8_t az_unconv_vol(u_int8_t);
! 124:
! 125: int
! 126: az_probe(struct device *parent, void *self, void *aux)
! 127: {
! 128: struct isa_attach_args *ia = aux;
! 129: bus_space_tag_t iot = ia->ia_iot;
! 130: bus_space_handle_t ioh;
! 131:
! 132: int iosize = 1, iobase = ia->ia_iobase;
! 133:
! 134: if (!AZ_BASE_VALID(iobase)) {
! 135: printf("az: configured iobase 0x%x invalid\n", iobase);
! 136: return (0);
! 137: }
! 138:
! 139: if (bus_space_map(iot, iobase, iosize, 0, &ioh))
! 140: return (0);
! 141:
! 142: if (!az_find(iot, ioh)) {
! 143: bus_space_unmap(iot, ioh, iosize);
! 144: return (0);
! 145: }
! 146:
! 147: bus_space_unmap(iot, ioh, iosize);
! 148: ia->ia_iosize = iosize;
! 149: return (1);
! 150: }
! 151:
! 152: void
! 153: az_attach(struct device *parent, struct device *self, void *aux)
! 154: {
! 155: struct az_softc *sc = (void *) self;
! 156: struct isa_attach_args *ia = aux;
! 157:
! 158: sc->lm.iot = ia->ia_iot;
! 159: sc->rf = LM700X_REF_050;
! 160: sc->stereo = LM700X_STEREO;
! 161: sc->mute = 0;
! 162: sc->freq = MIN_FM_FREQ;
! 163: sc->vol = 0;
! 164:
! 165: /* remap I/O */
! 166: if (bus_space_map(sc->lm.iot, ia->ia_iobase, ia->ia_iosize,
! 167: 0, &sc->lm.ioh)) {
! 168: printf(": bus_space_map() failed\n");
! 169: return;
! 170: }
! 171:
! 172: printf(": Aztech/PackardBell\n");
! 173:
! 174: /* Configure struct lm700x_t lm */
! 175: sc->lm.offset = 0;
! 176: sc->lm.wzcl = AZ_WREN_ON | AZ_CLCK_OFF | AZ_DATA_OFF;
! 177: sc->lm.wzch = AZ_WREN_ON | AZ_CLCK_ON | AZ_DATA_OFF;
! 178: sc->lm.wocl = AZ_WREN_ON | AZ_CLCK_OFF | AZ_DATA_ON;
! 179: sc->lm.woch = AZ_WREN_ON | AZ_CLCK_ON | AZ_DATA_ON;
! 180: sc->lm.initdata = 0;
! 181: sc->lm.rsetdata = AZ_DATA_ON | AZ_CLCK_ON | AZ_WREN_OFF;
! 182: sc->lm.init = az_lm700x_init;
! 183: sc->lm.rset = az_lm700x_rset;
! 184:
! 185: az_set_freq(sc, sc->freq);
! 186:
! 187: radio_attach_mi(&az_hw_if, sc, &sc->sc_dev);
! 188: }
! 189:
! 190: /*
! 191: * Mute the card
! 192: */
! 193: void
! 194: az_set_mute(struct az_softc *sc)
! 195: {
! 196: bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0,
! 197: sc->mute ? 0 : sc->vol);
! 198: DELAY(6);
! 199: bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0,
! 200: sc->mute ? 0 : sc->vol);
! 201: }
! 202:
! 203: void
! 204: az_set_freq(struct az_softc *sc, u_int32_t nfreq)
! 205: {
! 206: u_int8_t vol;
! 207: u_int32_t reg;
! 208:
! 209: vol = sc->mute ? 0 : sc->vol;
! 210:
! 211: if (nfreq > MAX_FM_FREQ)
! 212: nfreq = MAX_FM_FREQ;
! 213: if (nfreq < MIN_FM_FREQ)
! 214: nfreq = MIN_FM_FREQ;
! 215:
! 216: sc->freq = nfreq;
! 217:
! 218: reg = lm700x_encode_freq(nfreq, sc->rf);
! 219: reg |= sc->stereo | sc->rf | LM700X_DIVIDER_FM;
! 220:
! 221: lm700x_hardware_write(&sc->lm, reg, vol);
! 222:
! 223: az_set_mute(sc);
! 224: }
! 225:
! 226: /*
! 227: * Return state of the card - tuned/not tuned, mono/stereo
! 228: */
! 229: u_int8_t
! 230: az_state(bus_space_tag_t iot, bus_space_handle_t ioh)
! 231: {
! 232: u_int8_t info = 0, portdata;
! 233:
! 234: portdata = bus_space_read_1(iot, ioh, 0);
! 235:
! 236: info |= portdata & AZTECH_STEREO ? 0 : RADIO_INFO_STEREO;
! 237: info |= portdata & AZTECH_SIGNAL ? 0 : RADIO_INFO_SIGNAL;
! 238:
! 239: return info;
! 240: }
! 241:
! 242: /*
! 243: * Convert volume to hardware representation.
! 244: * The card uses bits 00000x0x to set volume.
! 245: */
! 246: u_int8_t
! 247: az_conv_vol(u_int8_t vol)
! 248: {
! 249: if (vol < VOLUME_RATIO(1))
! 250: return 0;
! 251: else if (vol >= VOLUME_RATIO(1) && vol < VOLUME_RATIO(2))
! 252: return 1;
! 253: else if (vol >= VOLUME_RATIO(2) && vol < VOLUME_RATIO(3))
! 254: return 4;
! 255: else
! 256: return 5;
! 257: }
! 258:
! 259: /*
! 260: * Convert volume from hardware representation
! 261: */
! 262: u_int8_t
! 263: az_unconv_vol(u_int8_t vol)
! 264: {
! 265: switch (vol) {
! 266: case 0:
! 267: return VOLUME_RATIO(0);
! 268: case 1:
! 269: return VOLUME_RATIO(1);
! 270: case 4:
! 271: return VOLUME_RATIO(2);
! 272: }
! 273: return VOLUME_RATIO(3);
! 274: }
! 275:
! 276: u_int
! 277: az_find(bus_space_tag_t iot, bus_space_handle_t ioh)
! 278: {
! 279: struct az_softc sc;
! 280: u_int i;
! 281:
! 282: sc.lm.iot = iot;
! 283: sc.lm.ioh = ioh;
! 284: sc.lm.offset = 0;
! 285: sc.lm.wzcl = AZ_WREN_ON | AZ_CLCK_OFF | AZ_DATA_OFF;
! 286: sc.lm.wzch = AZ_WREN_ON | AZ_CLCK_ON | AZ_DATA_OFF;
! 287: sc.lm.wocl = AZ_WREN_ON | AZ_CLCK_OFF | AZ_DATA_ON;
! 288: sc.lm.woch = AZ_WREN_ON | AZ_CLCK_ON | AZ_DATA_ON;
! 289: sc.lm.initdata = 0;
! 290: sc.lm.rsetdata = AZ_DATA_ON | AZ_CLCK_ON | AZ_WREN_OFF;
! 291: sc.lm.init = az_lm700x_init;
! 292: sc.lm.rset = az_lm700x_rset;
! 293: sc.rf = LM700X_REF_050;
! 294: sc.mute = 0;
! 295: sc.stereo = LM700X_STEREO;
! 296: sc.vol = 0;
! 297:
! 298: /*
! 299: * Scan whole FM range. If there is a card it'll
! 300: * respond on some frequency.
! 301: */
! 302: for (i = MIN_FM_FREQ; i < MAX_FM_FREQ; i += 10) {
! 303: az_set_freq(&sc, i);
! 304: if (az_state(iot, ioh))
! 305: return 1;
! 306: }
! 307:
! 308: return 0;
! 309: }
! 310:
! 311: void
! 312: az_lm700x_init(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
! 313: u_int32_t data)
! 314: {
! 315: /* Do nothing */
! 316: return;
! 317: }
! 318:
! 319: void
! 320: az_lm700x_rset(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
! 321: u_int32_t data)
! 322: {
! 323: bus_space_write_1(iot, ioh, off, data);
! 324: }
! 325:
! 326: int
! 327: az_get_info(void *v, struct radio_info *ri)
! 328: {
! 329: struct az_softc *sc = v;
! 330:
! 331: ri->mute = sc->mute;
! 332: ri->volume = az_unconv_vol(sc->vol);
! 333: ri->stereo = sc->stereo == LM700X_STEREO ? 1 : 0;
! 334: ri->caps = AZTECH_CAPABILITIES;
! 335: ri->rfreq = lm700x_decode_ref(sc->rf);
! 336: ri->info = az_state(sc->lm.iot, sc->lm.ioh);
! 337: ri->freq = sc->freq;
! 338:
! 339: /* UNSUPPORTED */
! 340: ri->lock = 0;
! 341:
! 342: return (0);
! 343: }
! 344:
! 345: int
! 346: az_set_info(void *v, struct radio_info *ri)
! 347: {
! 348: struct az_softc *sc = v;
! 349:
! 350: sc->mute = ri->mute ? 1 : 0;
! 351: sc->vol = az_conv_vol(ri->volume);
! 352: sc->stereo = ri->stereo ? LM700X_STEREO : LM700X_MONO;
! 353: sc->rf = lm700x_encode_ref(ri->rfreq);
! 354:
! 355: az_set_freq(sc, ri->freq);
! 356: az_set_mute(sc);
! 357:
! 358: return (0);
! 359: }
CVSweb