diff options
author | Grigoriy Orlov <gluk@cvs.openbsd.org> | 2001-10-04 20:15:43 +0000 |
---|---|---|
committer | Grigoriy Orlov <gluk@cvs.openbsd.org> | 2001-10-04 20:15:43 +0000 |
commit | e9ddebae6a40c779b36b982a9be50addb0eb4aae (patch) | |
tree | c68ebb9cc7be025bede4f97dd69e6797011f26ae /sys/dev/isa/aztech.c | |
parent | a2571ead4129bfe87dd6fd07fe38f9f26bfa6941 (diff) |
Aztech/PackardBell FM Radio Card device driver
Work by Maxim Tsyplakov <tm@oganer.net>, Vladimir Popov <jumbo@narod.ru>
Diffstat (limited to 'sys/dev/isa/aztech.c')
-rw-r--r-- | sys/dev/isa/aztech.c | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/sys/dev/isa/aztech.c b/sys/dev/isa/aztech.c new file mode 100644 index 00000000000..dddc9be9664 --- /dev/null +++ b/sys/dev/isa/aztech.c @@ -0,0 +1,397 @@ +/* $OpenBSD: aztech.c,v 1.1 2001/10/04 20:15:42 gluk Exp $ */ +/* $RuOBSD: aztech.c,v 1.8 2001/10/04 18:51:50 pva Exp $ */ + +/* + * Copyright (c) 2001 Maxim Tsyplakov <tm@oganer.net>, + * Vladimir Popov <jumbo@narod.ru> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Aztech/PackardBell FM Radio Card device driver */ + +/* + * Sanyo LM7001J Direct PLL Frequency Synthesizer: + * ??? See http://www.redsword.com/tjacobs/geeb/fmcard.htm + * + * Philips TEA5712T AM/FM Stereo DTS Radio: + * http://www.semiconductors.philips.com/pip/TEA5712 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/device.h> +#include <sys/radioio.h> + +#include <machine/bus.h> + +#include <dev/isa/isavar.h> +#include <dev/ic/lm700x.h> +#include <dev/radio_if.h> + +#define RF_25K 25 +#define RF_50K 50 +#define RF_100K 100 + +#define MAX_VOL 3 +#define VOLUME_RATIO(x) (255 * x / MAX_VOL) + +#define AZ_BASE_VALID(x) ((x == 0x350) || (x == 0x358)) +#define AZTECH_CAPABILITIES RADIO_CAPS_DETECT_STEREO | \ + RADIO_CAPS_DETECT_SIGNAL | \ + RADIO_CAPS_SET_MONO | \ + RADIO_CAPS_REFERENCE_FREQ + +#define AZ_WREN_ON (1 << 1) +#define AZ_WREN_OFF (0 << 1) + +#define AZ_CLCK_ON (1 << 6) +#define AZ_CLCK_OFF (0 << 6) + +#define AZ_DATA_ON (1 << 7) +#define AZ_DATA_OFF (0 << 7) + +int az_probe(struct device *, void *, void *); +void az_attach(struct device *, struct device * self, void *); +int az_open(dev_t, int, int, struct proc *); +int az_close(dev_t, int, int, struct proc *); +int az_ioctl(dev_t, u_long, caddr_t, int, struct proc *); + +struct radio_hw_if az_hw_if = { + az_open, + az_close, + az_ioctl +}; + +struct az_softc { + struct device sc_dev; + + u_long sc_freq; + u_long sc_rf; + u_char sc_vol; + u_char sc_muted; + u_long sc_stereo; + struct lm700x_t lm; +}; + +struct cfattach az_ca = { + sizeof(struct az_softc), az_probe, az_attach +}; + +struct cfdriver az_cd = { + NULL, "az", DV_DULL +}; + +u_int az_find(bus_space_tag_t, bus_space_handle_t); +void az_set_mute(struct az_softc *); +void az_set_freq(struct az_softc *, u_long); +u_char az_state(bus_space_tag_t, bus_space_handle_t); + +void az_lm700x_init(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_long); +void az_lm700x_rset(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_long); + +u_char az_conv_vol(u_char); +u_char az_unconv_vol(u_char); + +int +az_probe(struct device *parent, void *self, void *aux) +{ + struct isa_attach_args *ia = aux; + bus_space_tag_t iot = ia->ia_iot; + bus_space_handle_t ioh; + + int iosize = 1, iobase = ia->ia_iobase; + + if (!AZ_BASE_VALID(iobase)) { + printf("az: configured iobase 0x%x invalid", iobase); + return 0; + } + + if (bus_space_map(iot, iobase, iosize, 0, &ioh)) + return 0; + + bus_space_unmap(iot, ioh, iosize); + + if (!az_find(iot, ioh)) + return 0; + + ia->ia_iosize = iosize; + return 1; +} + +void +az_attach(struct device *parent, struct device *self, void *aux) +{ + struct az_softc *sc = (void *) self; + struct isa_attach_args *ia = aux; + + sc->lm.iot = ia->ia_iot; + sc->sc_rf = LM700X_REF_050; + sc->sc_stereo = LM700X_STEREO; +#ifdef RADIO_INIT_MUTE + sc->sc_muted = RADIO_INIT_MUTE; +#else + sc->sc_muted = 0; +#endif /* RADIO_INIT_MUTE */ +#ifdef RADIO_INIT_FREQ + sc->sc_freq = RADIO_INIT_FREQ; +#else + sc->sc_freq = MIN_FM_FREQ; +#endif /* RADIO_INIT_FREQ */ +#ifdef RADIO_INIT_VOL + sc->sc_vol = az_conv_vol(RADIO_INIT_VOL); +#else + sc->sc_vol = 0; +#endif /* RADIO_INIT_VOL */ + + /* remap I/O */ + if (bus_space_map(sc->lm.iot, ia->ia_iobase, ia->ia_iosize, + 0, &sc->lm.ioh)) + panic(": bus_space_map() of %s failed", sc->sc_dev.dv_xname); + + printf(": Aztech/PackardBell"); + + /* Configure struct lm700x_t lm */ + sc->lm.offset = 0; + sc->lm.wzcl = AZ_WREN_ON | AZ_CLCK_OFF | AZ_DATA_OFF; + sc->lm.wzch = AZ_WREN_ON | AZ_CLCK_ON | AZ_DATA_OFF; + sc->lm.wocl = AZ_WREN_ON | AZ_CLCK_OFF | AZ_DATA_ON; + sc->lm.woch = AZ_WREN_ON | AZ_CLCK_ON | AZ_DATA_ON; + sc->lm.initdata = 0; + sc->lm.rsetdata = AZ_DATA_ON | AZ_CLCK_ON | AZ_WREN_OFF; + sc->lm.init = az_lm700x_init; + sc->lm.rset = az_lm700x_rset; + + az_set_freq(sc, sc->sc_freq); + + radio_attach_mi(&az_hw_if, sc, &sc->sc_dev); +} + +int +az_open(dev_t dev, int flags, int fmt, struct proc *p) +{ + struct az_softc *sc; + return !(sc = az_cd.cd_devs[0]) ? ENXIO : 0; +} + +int +az_close(dev_t dev, int flags, int fmt, struct proc *p) +{ + return 0; +} + +/* + * Handle the ioctl for the device + */ +int +az_ioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) +{ + int error; + struct az_softc *sc = az_cd.cd_devs[0]; + + error = 0; + switch (cmd) { + case RIOCGMUTE: + *(u_long *)data = sc->sc_muted ? 1 : 0; + break; + case RIOCSMUTE: + sc->sc_muted = *(u_long *)data ? 1 : 0; + az_set_mute(sc); + break; + case RIOCGVOLU: + *(u_long *)data = az_unconv_vol(sc->sc_vol); + break; + case RIOCSVOLU: + sc->sc_vol = az_conv_vol(*(u_int *)data); + az_set_mute(sc); + break; + case RIOCGMONO: + *(u_long *)data = sc->sc_stereo == LM700X_STEREO ? 0 : 1; + break; + case RIOCSMONO: + sc->sc_stereo = *(u_long *)data ? LM700X_MONO : LM700X_STEREO; + az_set_freq(sc, sc->sc_freq); + break; + case RIOCGFREQ: + *(u_long *)data = sc->sc_freq; + break; + case RIOCSFREQ: + az_set_freq(sc, *(u_long *)data); + break; + case RIOCGCAPS: + *(u_long *)data = AZTECH_CAPABILITIES; + break; + case RIOCGINFO: /* Get Info */ + *(u_long *)data = 0x03 & + (3 ^ az_state(sc->lm.iot, sc->lm.ioh)); + break; + case RIOCSREFF: + sc->sc_rf = lm700x_encode_ref(*(u_char *)data); + az_set_freq(sc, sc->sc_freq); + break; + case RIOCGREFF: + *(u_long *)data = lm700x_decode_ref(sc->sc_rf); + break; + case RIOCSSRCH: + /* FALLTHROUGH */ + case RIOCSLOCK: + /* FALLTHROUGH */ + case RIOCGLOCK: + /* NOT SUPPORTED */ + error = ENODEV; + break; + default: + error = EINVAL; + } + return (error); +} + +/* + * Mute the card + */ +void +az_set_mute(struct az_softc *sc) +{ + bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0, + sc->sc_muted ? 0 : sc->sc_vol); + DELAY(6); + bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0, + sc->sc_muted ? 0 : sc->sc_vol); +} + +void +az_set_freq(struct az_softc *sc, u_long nfreq) +{ + u_char vol; + u_long reg; + + vol = sc->sc_muted ? 0 : sc->sc_vol; + + if (nfreq > MAX_FM_FREQ) + nfreq = MAX_FM_FREQ; + if (nfreq < MIN_FM_FREQ) + nfreq = MIN_FM_FREQ; + + sc->sc_freq = nfreq; + + reg = lm700x_encode_freq(nfreq, sc->sc_rf); + reg |= sc->sc_stereo | sc->sc_rf | LM700X_DIVIDER_FM; + + lm700x_hardware_write(&sc->lm, reg, vol); + + az_set_mute(sc); +} + +/* + * Return state of the card - tuned/not tuned, mono/stereo + */ +u_char +az_state(bus_space_tag_t iot, bus_space_handle_t ioh) +{ + return bus_space_read_1(iot, ioh, 0) & 3; +} + +/* + * Convert volume to hardware representation. + * The card uses bits 00000x0x to set volume. + */ +u_char +az_conv_vol(u_char vol) +{ + if (vol < VOLUME_RATIO(1)) + return 0; + else if (vol >= VOLUME_RATIO(1) && vol < VOLUME_RATIO(2)) + return 1; + else if (vol >= VOLUME_RATIO(2) && vol < VOLUME_RATIO(3)) + return 4; + else + return 5; +} + +/* + * Convert volume from hardware representation + */ +u_char +az_unconv_vol(u_char vol) +{ + switch (vol) { + case 0: + return VOLUME_RATIO(0); + case 1: + return VOLUME_RATIO(1); + case 4: + return VOLUME_RATIO(2); + } + return VOLUME_RATIO(3); +} + +u_int +az_find(bus_space_tag_t iot, bus_space_handle_t ioh) +{ + struct az_softc sc; + u_int i, scanres = 0; + + sc.lm.iot = iot; + sc.lm.ioh = ioh; + sc.lm.offset = 0; + sc.lm.wzcl = AZ_WREN_ON | AZ_CLCK_OFF | AZ_DATA_OFF; + sc.lm.wzch = AZ_WREN_ON | AZ_CLCK_ON | AZ_DATA_OFF; + sc.lm.wocl = AZ_WREN_ON | AZ_CLCK_OFF | AZ_DATA_ON; + sc.lm.woch = AZ_WREN_ON | AZ_CLCK_ON | AZ_DATA_ON; + sc.lm.initdata = 0; + sc.lm.rsetdata = AZ_DATA_ON | AZ_CLCK_ON | AZ_WREN_OFF; + sc.lm.init = az_lm700x_init; + sc.lm.rset = az_lm700x_rset; + sc.sc_rf = LM700X_REF_050; + sc.sc_muted = 0; + sc.sc_stereo = LM700X_STEREO; + sc.sc_vol = 0; + + /* + * Scan whole FM range. If there is a card it'll + * respond on some frequency. + */ + for (i = MIN_FM_FREQ; !scanres && i < MAX_FM_FREQ; i += 10) { + az_set_freq(&sc, i); + scanres += 3 - az_state(iot, ioh); + } + + return scanres; +} + +void +az_lm700x_init(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off, + u_long data) +{ + /* Do nothing */ + return; +} + +void +az_lm700x_rset(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off, + u_long data) +{ + bus_space_write_1(iot, ioh, off, data); +} |