summaryrefslogtreecommitdiff
path: root/sys/dev/pci/fmsradio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci/fmsradio.c')
-rw-r--r--sys/dev/pci/fmsradio.c420
1 files changed, 420 insertions, 0 deletions
diff --git a/sys/dev/pci/fmsradio.c b/sys/dev/pci/fmsradio.c
new file mode 100644
index 00000000000..84b433328b4
--- /dev/null
+++ b/sys/dev/pci/fmsradio.c
@@ -0,0 +1,420 @@
+/* $OpenBSD: fmsradio.c,v 1.1 2002/05/06 16:37:43 mickey Exp $ */
+
+/*
+ * Copyright (c) 2002 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 AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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.
+ */
+
+/* Device Driver for FM Tuners attached to FM801 */
+
+/* Currently supported tuners:
+ * o MediaForte SoundForte SF64-PCR PCI Radio Card
+ * o MediaForte SoundForte SF256-PCP-R PCI Sound Card with FM Radio
+ */
+
+#include "radio.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/radioio.h>
+
+#include <machine/bus.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#include <dev/audio_if.h>
+#include <dev/radio_if.h>
+
+#include <dev/ic/ac97.h>
+#include <dev/pci/fmsradio.h>
+#include <dev/pci/fmsreg.h>
+#include <dev/pci/fmsvar.h>
+
+#include <dev/ic/tea5757.h>
+
+#define TUNER_UNKNOWN 0
+#define TUNER_SF256PCPR 1
+#define TUNER_SF64PCR 2
+#define TUNER_NOT_ATTACHED 0xFFFF
+
+#define SF64PCR_CAPS RADIO_CAPS_DETECT_STEREO | \
+ RADIO_CAPS_DETECT_SIGNAL | \
+ RADIO_CAPS_SET_MONO | \
+ RADIO_CAPS_HW_SEARCH | \
+ RADIO_CAPS_HW_AFC | \
+ RADIO_CAPS_LOCK_SENSITIVITY
+
+#define SF256PCPR_CAPS RADIO_CAPS_SET_MONO | \
+ RADIO_CAPS_HW_SEARCH | \
+ RADIO_CAPS_HW_AFC | \
+ RADIO_CAPS_LOCK_SENSITIVITY
+
+#define PCR_WREN_ON (0 << 1)
+#define PCR_WREN_OFF (1 << 1)
+#define PCR_CLOCK_ON (1 << 0)
+#define PCR_CLOCK_OFF (0 << 0)
+#define PCR_DATA_ON (1 << 2)
+#define PCR_DATA_OFF (0 << 2)
+
+#define PCR_SIGNAL 0x80
+#define PCR_STEREO 0x80
+#define PCR_INFO_SIGNAL (1 << 24)
+#define PCR_INFO_STEREO (1 << 25)
+
+#define PCPR_WREN_ON (0 << 2)
+#define PCPR_WREN_OFF (1 << 2)
+#define PCPR_CLOCK_ON (1 << 0)
+#define PCPR_CLOCK_OFF (0 << 0)
+#define PCPR_DATA_ON (1 << 1)
+#define PCPR_DATA_OFF (0 << 1)
+
+/*
+ * Function prototypes
+ */
+void fmsradio_set_mute(struct fmsradio_if *);
+
+void sf64pcr_init(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t);
+void sf64pcr_rset(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t);
+void sf64pcr_write_bit(bus_space_tag_t, bus_space_handle_t, bus_size_t, int);
+u_int32_t sf64pcr_hw_read(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+int sf64pcr_probe(struct fmsradio_if *);
+
+void sf256pcpr_init(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t);
+void sf256pcpr_rset(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t);
+void sf256pcpr_write_bit(bus_space_tag_t, bus_space_handle_t, bus_size_t, int);
+u_int32_t sf256pcpr_hw_read(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+int sf256pcpr_probe(struct fmsradio_if *);
+
+int
+fmsradio_attach(struct fmsradio_if *sc, char *devname)
+{
+ sc->vol = 0;
+ sc->mute = 0;
+ sc->freq = MIN_FM_FREQ;
+ sc->stereo = TEA5757_STEREO;
+ sc->lock = TEA5757_S030;
+
+ sc->card = TUNER_UNKNOWN;
+ if ((sc->card = sf64pcr_probe(sc)) == TUNER_SF64PCR)
+ printf("%s: SoundForte SF64-PCR FM Radio\n", devname);
+ else if ((sc->card = sf256pcpr_probe(sc)) == TUNER_SF256PCPR)
+ printf("%s: SoundForte SF256-PCP-R FM Radio\n", devname);
+ else {
+ sc->card = TUNER_NOT_ATTACHED;
+ return 0;
+ }
+
+ fmsradio_set_mute(sc);
+
+ return sc->card;
+}
+
+/* SF256-PCP-R specific routines */
+int
+sf256pcpr_probe(struct fmsradio_if *sc)
+{
+ u_int32_t freq;
+
+ sc->tea.init = sf256pcpr_init;
+ sc->tea.rset = sf256pcpr_rset;
+ sc->tea.write_bit = sf256pcpr_write_bit;
+ sc->tea.read = sf256pcpr_hw_read;
+
+ tea5757_set_freq(&sc->tea, sc->stereo, sc->lock, sc->freq);
+ freq = sf256pcpr_hw_read(sc->tea.iot, sc->tea.ioh, sc->tea.offset);
+ if (tea5757_decode_freq(freq, sc->tea.flags & TEA5757_TEA5759)
+ != sc->freq)
+ return TUNER_UNKNOWN;
+
+ return TUNER_SF256PCPR;
+}
+
+u_int32_t
+sf256pcpr_hw_read(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t offset)
+{
+ u_int32_t res = 0ul;
+ u_int16_t i, d;
+
+ d = FM_IO_GPIO;
+ d |= FM_IO_GPIO3_IN | PCPR_WREN_OFF;
+
+ /* Now read data in */
+ d |= FM_IO_GPIO1_IN | PCPR_DATA_ON;
+
+ bus_space_write_2(iot, ioh, offset, d | PCPR_CLOCK_OFF);
+
+ /* Read the register */
+ i = 24;
+ while (i--) {
+ res <<= 1;
+ bus_space_write_2(iot, ioh, offset, d | PCPR_CLOCK_ON);
+ bus_space_write_2(iot, ioh, offset, d | PCPR_CLOCK_OFF);
+ res |= bus_space_read_2(iot, ioh, offset) & PCPR_DATA_ON ? 1 : 0;
+ }
+
+ return (res & (TEA5757_DATA | TEA5757_FREQ));
+}
+
+void
+sf256pcpr_write_bit(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
+ int bit)
+{
+ u_int16_t data, wren;
+
+ wren = FM_IO_GPIO | FM_IO_GPIO3_IN | PCPR_WREN_ON;
+ data = bit ? PCPR_DATA_ON : PCPR_DATA_OFF;
+
+ bus_space_write_2(iot, ioh, off, PCPR_CLOCK_OFF | wren | data);
+ bus_space_write_2(iot, ioh, off, PCPR_CLOCK_ON | wren | data);
+ bus_space_write_2(iot, ioh, off, PCPR_CLOCK_OFF | wren | data);
+}
+
+void
+sf256pcpr_init(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t offset,
+ u_int32_t d)
+{
+ d = FM_IO_GPIO | FM_IO_GPIO3_IN;
+ d |= PCPR_WREN_ON | PCPR_DATA_OFF | PCPR_CLOCK_OFF;
+
+ bus_space_write_2(iot, ioh, offset, d);
+ bus_space_write_2(iot, ioh, offset, d);
+}
+
+void
+sf256pcpr_rset(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t offset,
+ u_int32_t d)
+{
+ d = FM_IO_GPIO | FM_IO_GPIO3_IN;
+ d |= PCPR_WREN_OFF | PCPR_DATA_OFF | PCPR_CLOCK_OFF;
+
+ bus_space_write_2(iot, ioh, offset, d);
+ bus_space_write_2(iot, ioh, offset, d);
+}
+
+/* SF64-PCR specific routines */
+int
+sf64pcr_probe(struct fmsradio_if *sc)
+{
+ u_int32_t freq;
+
+ sc->tea.init = sf64pcr_init;
+ sc->tea.rset = sf64pcr_rset;
+ sc->tea.write_bit = sf64pcr_write_bit;
+ sc->tea.read = sf64pcr_hw_read;
+
+ tea5757_set_freq(&sc->tea, sc->stereo, sc->lock, sc->freq);
+ freq = sf64pcr_hw_read(sc->tea.iot, sc->tea.ioh, sc->tea.offset);
+ if (tea5757_decode_freq(freq, sc->tea.flags & TEA5757_TEA5759)
+ != sc->freq)
+ return TUNER_UNKNOWN;
+
+ return TUNER_SF64PCR;
+}
+
+u_int32_t
+sf64pcr_hw_read(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t offset)
+{
+ u_int32_t res = 0ul;
+ u_int16_t d, i, ind = 0;
+
+ d = FM_IO_GPIO;
+ d |= FM_IO_GPIO3_IN | PCR_WREN_OFF;
+
+ /* Now read data in */
+ d |= FM_IO_GPIO2_IN | PCR_DATA_ON;
+
+ bus_space_write_2(iot, ioh, offset, d | PCR_CLOCK_OFF);
+ DELAY(4);
+
+ /* Read the register */
+ i = 23;
+ while (i--) {
+ bus_space_write_2(iot, ioh, offset, d | PCR_CLOCK_ON);
+ DELAY(4);
+
+ bus_space_write_2(iot, ioh, offset, d | PCR_CLOCK_OFF);
+ DELAY(4);
+
+ res |= bus_space_read_2(iot, ioh, offset) & PCR_DATA_ON ? 1 : 0;
+ res <<= 1;
+ }
+
+ bus_space_write_2(iot, ioh, offset, d | PCR_CLOCK_ON);
+ DELAY(4);
+
+ i = bus_space_read_1(iot, ioh, offset);
+ ind = i & PCR_SIGNAL ? (1 << 1) : (0 << 1); /* Tuning */
+
+ bus_space_write_2(iot, ioh, offset, d | PCR_CLOCK_OFF);
+
+ i = bus_space_read_2(iot, ioh, offset);
+ ind |= i & PCR_STEREO ? (1 << 0) : (0 << 0); /* Mono */
+ res |= i & PCR_DATA_ON ? (1 << 0) : (0 << 0);
+
+ return (res & (TEA5757_DATA | TEA5757_FREQ)) | (ind << 24);
+}
+
+void
+sf64pcr_write_bit(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
+ int bit)
+{
+ u_int16_t data, wren;
+
+ wren = FM_IO_GPIO | FM_IO_GPIO3_IN | PCR_WREN_ON;
+ data = bit ? PCR_DATA_ON : PCR_DATA_OFF;
+
+ bus_space_write_2(iot, ioh, off, PCR_CLOCK_OFF | wren | data);
+ DELAY(4);
+ bus_space_write_2(iot, ioh, off, PCR_CLOCK_ON | wren | data);
+ DELAY(4);
+ bus_space_write_2(iot, ioh, off, PCR_CLOCK_OFF | wren | data);
+ DELAY(4);
+}
+
+void
+sf64pcr_init(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t offset,
+ u_int32_t d)
+{
+ d = FM_IO_GPIO | FM_IO_GPIO3_IN;
+ d |= PCR_WREN_ON | PCR_DATA_ON | PCR_CLOCK_OFF;
+
+ bus_space_write_2(iot, ioh, offset, d);
+ DELAY(4);
+}
+
+void
+sf64pcr_rset(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t offset,
+ u_int32_t d)
+{
+ /* Do nothing */
+ return;
+}
+
+
+/* Common tuner routines */
+/*
+ * Mute/unmute
+ */
+void
+fmsradio_set_mute(struct fmsradio_if *sc)
+{
+ u_int16_t v, mute, unmute;
+
+ mute = FM_IO_GPIO | FM_IO_GPIO3_IN;
+ switch (sc->card) {
+ case TUNER_SF256PCPR:
+ unmute = FM_IO_GPIO | FM_IO_GPIO3_IN | FM_IO_GPIO2;
+ break;
+ case TUNER_SF64PCR:
+ unmute = FM_IO_GPIO | FM_IO_GPIO3_IN | FM_IO_GPIO1;
+ break;
+ default:
+ return;
+ }
+ v = (sc->mute || !sc->vol) ? mute : unmute;
+ bus_space_write_2(sc->tea.iot, sc->tea.ioh, sc->tea.offset, v);
+}
+
+int
+fmsradio_get_info(void *v, struct radio_info *ri)
+{
+ struct fms_softc *fms_sc = v;
+ struct fmsradio_if *sc = &fms_sc->radio;
+ u_int32_t buf;
+
+ if (sc->card == TUNER_NOT_ATTACHED)
+ return (ENXIO);
+
+ ri->mute = sc->mute;
+ ri->volume = sc->vol ? 255 : 0;
+ ri->stereo = sc->stereo == TEA5757_STEREO ? 1 : 0;
+ ri->lock = tea5757_decode_lock(sc->lock);
+
+ switch (sc->card) {
+ case TUNER_SF256PCPR:
+ ri->caps = SF256PCPR_CAPS;
+ buf = sf256pcpr_hw_read(sc->tea.iot, sc->tea.ioh, sc->tea.offset);
+ ri->info = 0; /* UNSUPPORTED */
+ break;
+ case TUNER_SF64PCR:
+ ri->caps = SF64PCR_CAPS;
+ buf = sf64pcr_hw_read(sc->tea.iot, sc->tea.ioh, sc->tea.offset);
+ ri->info = buf & PCR_INFO_SIGNAL ? 0 : RADIO_INFO_SIGNAL;
+ ri->info |= buf & PCR_INFO_STEREO ? 0 : RADIO_INFO_STEREO;
+ break;
+ default:
+ break;
+ }
+
+ ri->freq = sc->freq = tea5757_decode_freq(buf,
+ sc->tea.flags & TEA5757_TEA5759);
+
+ fmsradio_set_mute(sc);
+
+ /* UNSUPPORTED */
+ ri->rfreq = 0;
+
+ return (0);
+}
+
+int
+fmsradio_set_info(void *v, struct radio_info *ri)
+{
+ struct fms_softc *fms_sc = v;
+ struct fmsradio_if *sc = &fms_sc->radio;
+
+ if (sc->card == TUNER_NOT_ATTACHED)
+ return (ENXIO);
+
+ sc->mute = ri->mute ? 1 : 0;
+ sc->vol = ri->volume ? 255 : 0;
+ sc->stereo = ri->stereo ? TEA5757_STEREO: TEA5757_MONO;
+ sc->lock = tea5757_encode_lock(ri->lock);
+ ri->freq = sc->freq = tea5757_set_freq(&sc->tea,
+ sc->lock, sc->stereo, ri->freq);
+ fmsradio_set_mute(sc);
+
+ return (0);
+}
+
+int
+fmsradio_search(void *v, int f)
+{
+ struct fms_softc *fms_sc = v;
+ struct fmsradio_if *sc = &fms_sc->radio;
+
+ if (sc->card == TUNER_NOT_ATTACHED)
+ return (ENXIO);
+
+ tea5757_search(&sc->tea, sc->lock, sc->stereo, f);
+ fmsradio_set_mute(sc);
+
+ return (0);
+}