summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Shalayeff <mickey@cvs.openbsd.org>2002-04-25 04:57:00 +0000
committerMichael Shalayeff <mickey@cvs.openbsd.org>2002-04-25 04:57:00 +0000
commit9e8f5723f85ca68057fd064e73f8500b88c65582 (patch)
tree7e0c3995dc5b8f02fca59e85e2e15a2a1060d0a7
parent111a4e9f838d2bedb71d72db200520ccf3360f7c (diff)
soundforte radio driver, from Vladimir Popov <jumbo@narod.ru>
-rw-r--r--sys/arch/i386/conf/GENERIC3
-rw-r--r--sys/conf/files6
-rw-r--r--sys/dev/ic/pt2254a.c93
-rw-r--r--sys/dev/ic/pt2254a.h74
-rw-r--r--sys/dev/ic/tc921x.c173
-rw-r--r--sys/dev/ic/tc921x.h112
-rw-r--r--sys/dev/isa/files.isa7
-rw-r--r--sys/dev/isa/sf16fmr.c283
8 files changed, 748 insertions, 3 deletions
diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC
index 3510ce61054..3d9d3477d75 100644
--- a/sys/arch/i386/conf/GENERIC
+++ b/sys/arch/i386/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.294 2002/04/08 01:52:34 frantzen Exp $
+# $OpenBSD: GENERIC,v 1.295 2002/04/25 04:56:59 mickey Exp $
# $NetBSD: GENERIC,v 1.48 1996/05/20 18:17:23 mrg Exp $
#
# GENERIC -- everything that's currently supported
@@ -451,6 +451,7 @@ bktr0 at pci? dev ? function ?
# FM-Radio devices
#mr* at pci? dev ? function ? # Guillemot Maxi Radio FM2000 PCI Radio Card
#sf4r* at pci? dev ? function ? # SoundForte RadioLink SF64-PCR FM Radio Card
+#sfr0 at isa? port 0x384 # SoundForte RadioLink SF16-FMR FM Radio Card
#sf2r0 at isa? port 0x384 # SoundForte RadioLink SF16-FMR2 FM Radio Card
#az0 at isa? port 0x350 # Aztech/PackardBell FM Radio Card
#rt0 at isa? port 0x30c # AIMS Lab Radiotrack FM Radio Card
diff --git a/sys/conf/files b/sys/conf/files
index 44ee0d9404e..7701ee3eacc 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.245 2002/03/28 18:23:07 mickey Exp $
+# $OpenBSD: files,v 1.246 2002/04/25 04:56:59 mickey Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -24,6 +24,8 @@ define auconv
# radio device attributes
define tea5757
define lm700x
+define tc921x
+define pt2254a
# audio and midi devices, attaches to audio hardware driver
device audio
@@ -383,6 +385,8 @@ file dev/ic/dp8390.c dp8390nic
file dev/ic/rtl80x9.c rtl80x9
file dev/ic/tea5757.c tea5757
file dev/ic/lm700x.c lm700x
+file dev/ic/tc921x.c tc921x
+file dev/ic/pt2254a.c pt2254a
file dev/midi.c midi | midibus needs-flag
file dev/midisyn.c midisyn
file dev/mulaw.c mulaw
diff --git a/sys/dev/ic/pt2254a.c b/sys/dev/ic/pt2254a.c
new file mode 100644
index 00000000000..7428c43197d
--- /dev/null
+++ b/sys/dev/ic/pt2254a.c
@@ -0,0 +1,93 @@
+/* $OpenBSD: pt2254a.c,v 1.1 2002/04/25 04:56:59 mickey Exp $ */
+/*
+ * Copyright (c) 2002 Vladimir Popov <jumbo@narod.ru>.
+ *
+ * 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 ``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 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.
+ */
+/*
+ * Princeton Technology Corp.'s Electronic Volume Controller IC PT2254A
+ * http://www.princeton.com.tw
+ *
+ * PT2254A is an electronic volume controller IC utilizing CMOS Technology
+ * specially designed for use in audio equipment. It has two built-in
+ * channels making it highly suitable for mono and stereo sound applications.
+ * Through the specially designated signals that are applied externally to
+ * the data, clock and strobe input pins, PT2254A can control attenuation
+ * and channel balance.
+ */
+
+#include <sys/param.h>
+
+#include <dev/ic/pt2254a.h>
+
+u_int32_t
+pt2254a_encode_volume(u_int8_t *current, u_int8_t max) {
+ u_int32_t ret = 0ul;
+ u_int8_t vol, tens, ones;
+ int steps;
+
+ /*
+ * Volume management is done nonlinear.
+ *
+ * Approximate the curve with three lines:
+ * low volume: y = - (48 /(max/3)) * x + 68
+ * middle volume: y = - (14 /(max/3)) * x + 34
+ * high volume: y = - (6 /(max/3)) * x + 18
+ */
+ if (*current > 0 && *current <= max / 3) {
+ vol = PT2254A_MAX_ATTENUATION - (144 * (int)(*current) / max);
+ } else if (*current > max / 3 && *current <= 2 * max / 3) {
+ vol = 34 - (42 * (int)(*current) / max);
+ } else if (*current > 2 * max / 3) {
+ vol = 18 - (18 * (int)(*current) / max);
+ } else vol = PT2254A_MAX_ATTENUATION;
+
+ /* Report adjusted volume */
+ /* *current = max - (vol * max / PT2254A_MAX_ATTENUATION); */
+ steps = *current * PT2254A_MAX_ATTENUATION / max;
+ *current = steps * max / PT2254A_MAX_ATTENUATION;
+
+ tens = vol / 10;
+ ones = vol - tens * 10;
+
+ ret = PT2254A_ATTENUATION_MAJOR(tens) | PT2254A_ATTENUATION_MINOR(ones);
+
+ return ret;
+}
+
+u_int32_t
+pt2254a_compose_register(u_int32_t lvol, u_int32_t rvol, int left, int right) {
+ u_int32_t ret = 0ul;
+
+ if (left == USE_CHANNEL) {
+ ret |= PT2254A_LEFT_CHANNEL;
+ ret |= lvol;
+ }
+ if (right == USE_CHANNEL) {
+ ret |= PT2254A_RIGHT_CHANNEL;
+ ret |= rvol;
+ }
+
+ ret |= PT2254A_ZERO_PADDING;
+ ret |= PT2254A_EMPTY_BIT;
+
+ return ret;
+}
diff --git a/sys/dev/ic/pt2254a.h b/sys/dev/ic/pt2254a.h
new file mode 100644
index 00000000000..7c1bb6247df
--- /dev/null
+++ b/sys/dev/ic/pt2254a.h
@@ -0,0 +1,74 @@
+/* $OpenBSD: pt2254a.h,v 1.1 2002/04/25 04:56:59 mickey Exp $ */
+/*
+ * Copyright (c) 2002 Vladimir Popov <jumbo@narod.ru>.
+ *
+ * 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 ``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 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.
+ */
+/*
+ * Princeton Technology Corp.'s Electronic Volume Controller IC PT2254A
+ * http://www.princeton.com.tw
+ *
+ * PT2254A is an electronic volume controller IC utilizing CMOS Technology
+ * specially designed for use in audio equipment. It has two built-in
+ * channels making it highly suitable for mono and stereo sound applications.
+ * Through the specially designated signals that are applied externally to
+ * the data, clock and strobe input pins, PT2254A can control attenuation
+ * and channel balance.
+ */
+
+#ifndef _PT2254A_H_
+#define _PT2254A_H_
+
+#define PT2254A_MAX_ATTENUATION 68
+#define PT2254A_ATTENUATION_STEPS 35
+
+#define PT2254A_REGISTER_LENGTH 18
+
+#define PT2254A_ATTENUATION_MAJOR_0dB (1 << 0)
+#define PT2254A_ATTENUATION_MAJOR_10dB (1 << 1)
+#define PT2254A_ATTENUATION_MAJOR_20dB (1 << 2)
+#define PT2254A_ATTENUATION_MAJOR_30dB (1 << 3)
+#define PT2254A_ATTENUATION_MAJOR_40dB (1 << 4)
+#define PT2254A_ATTENUATION_MAJOR_50dB (1 << 5)
+#define PT2254A_ATTENUATION_MAJOR_60dB (1 << 6)
+#define PT2254A_ATTENUATION_MAJOR(x) (1 << x)
+
+#define PT2254A_ATTENUATION_MINOR_0dB (1 << 7)
+#define PT2254A_ATTENUATION_MINOR_2dB (1 << 8
+#define PT2254A_ATTENUATION_MINOR_4dB (1 << 9)
+#define PT2254A_ATTENUATION_MINOR_6dB (1 << 10)
+#define PT2254A_ATTENUATION_MINOR_8dB (1 << 11)
+#define PT2254A_ATTENUATION_MINOR(x) (1 << (7 + x / 2))
+
+#define PT2254A_EMPTY_BIT (0 << 12)
+
+#define PT2254A_BOTH_CHANNELS (3 << 13)
+#define PT2254A_LEFT_CHANNEL (1 << 13)
+#define PT2254A_RIGHT_CHANNEL (1 << 14)
+
+#define PT2254A_ZERO_PADDING (0 << 15)
+
+#define USE_CHANNEL 1
+
+u_int32_t pt2254a_encode_volume(u_int8_t *, u_int8_t);
+u_int32_t pt2254a_compose_register(u_int32_t, u_int32_t, int, int);
+
+#endif /* _PT2254A_H_ */
diff --git a/sys/dev/ic/tc921x.c b/sys/dev/ic/tc921x.c
new file mode 100644
index 00000000000..62e0519e9e0
--- /dev/null
+++ b/sys/dev/ic/tc921x.c
@@ -0,0 +1,173 @@
+/* $OpenBSD: tc921x.c,v 1.1 2002/04/25 04:56:59 mickey Exp $ */
+
+/*
+ * Copyright (c) 2001, 2002 Vladimir Popov <jumbo@narod.ru>.
+ *
+ * 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 ``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 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.
+ */
+/*
+ * Toshiba's High Speed PLL for DTS
+ * http://www.chipbook.co.kr/pdf/ic/toshiba/TC9216.pdf
+ *
+ * TC9216P, TC9217P, TC9217F are a high speed PLL-LSI with built-in 2 modulus
+ * prescaler. Each function is controlled through 3 serial bus lines and high
+ * performance digital tuning system can be constitued.
+ *
+ * Each function is controlled by the data setting to a pair of 24-bit
+ * registers. Each data of these registers is exchanged with controller side
+ * by 3 serial lines of DATA, CLOCK and PERIOD.
+ *
+ * 8 address bits and 24 data bits, total 32 bits, are transferred thru
+ * serial port.
+ *
+ * Input data is latched to the first and second input registers at the fall
+ * of PERIOD signal and each function is activated.
+ *
+ * Each output data is latched to output register in parallel at the fall
+ * timing of the 9th of CLOCK signal and can be received serially over the
+ * DATA line. Serial data of DATA, CLOCK and PERIOD is synchronized with
+ * crystal oscillation clock and tacken into the internal circuit of LSI.
+ * Thus, if crystal oscillator is stopped, serial data can not be input.
+ */
+
+#include <sys/param.h>
+#include <sys/radioio.h>
+
+#include <dev/ic/tc921x.h>
+
+#define PL_CL_DL(c) ((0 << c->period) | (0 << c->clock) | (0 << c->data))
+#define PL_CL_DH(c) ((0 << c->period) | (0 << c->clock) | (1 << c->data))
+#define PL_CH_DL(c) ((0 << c->period) | (1 << c->clock) | (0 << c->data))
+#define PL_CH_DH(c) ((0 << c->period) | (1 << c->clock) | (1 << c->data))
+
+#define PH_CL_DL(c) ((1 << c->period) | (0 << c->clock) | (0 << c->data))
+#define PH_CL_DH(c) ((1 << c->period) | (0 << c->clock) | (1 << c->data))
+#define PH_CH_DL(c) ((1 << c->period) | (1 << c->clock) | (0 << c->data))
+#define PH_CH_DH(c) ((1 << c->period) | (1 << c->clock) | (1 << c->data))
+
+#define PERIOD_LOW 0
+#define PERIOD_HIGH 1
+
+static void __tc921x_write_burst(unsigned int, u_int32_t, struct tc921x_t *, int);
+static u_int32_t __tc921x_read_burst(unsigned int, struct tc921x_t *);
+
+u_int32_t
+tc921x_encode_freq(u_int32_t freq) {
+ /* Normalize incoming frequency */
+ if (freq < MIN_FM_FREQ)
+ freq = MIN_FM_FREQ;
+ if (freq > MAX_FM_FREQ)
+ freq = MAX_FM_FREQ;
+
+ return (freq + IF_FREQ)/10;
+}
+
+u_int32_t
+tc921x_decode_freq(u_int32_t reg) {
+ return (reg & TC921X_D0_FREQ_DIVIDER) * 10 - IF_FREQ;
+}
+
+u_int32_t
+tc921x_read_addr(struct tc921x_t *c, u_int8_t addr) {
+ u_int32_t ret;
+
+ /* Finish previous transmission - PERIOD HIGH, CLOCK HIGH, DATA HIGH */
+ bus_space_write_1(c->iot, c->ioh, c->offset, PH_CH_DH(c));
+ /* Start transmission - PERIOD LOW, CLOCK HIGH, DATA HIGH */
+ bus_space_write_1(c->iot, c->ioh, c->offset, PL_CH_DH(c));
+
+ /*
+ * Period must be low when the register address transmission starts.
+ * Period must be high when the register data transmission starts.
+ * Do the switch in the middle of the address transmission.
+ */
+ __tc921x_write_burst(4, addr, c, PERIOD_LOW);
+ __tc921x_write_burst(4, addr >> 4, c, PERIOD_HIGH);
+
+ /* Reading data from the register */
+ ret = __tc921x_read_burst(TC921X_REGISTER_LENGTH, c);
+
+ /* End of transmission - PERIOD goes LOW then HIGH */
+ bus_space_write_1(c->iot, c->ioh, c->offset, PL_CH_DH(c));
+ bus_space_write_1(c->iot, c->ioh, c->offset, PH_CH_DH(c));
+
+ return ret;
+}
+
+void
+tc921x_write_addr(struct tc921x_t *c, u_int8_t addr, u_int32_t reg) {
+ /* Finish previous transmission - PERIOD HIGH, CLOCK HIGH, DATA HIGH */
+ bus_space_write_1(c->iot, c->ioh, c->offset, PH_CH_DH(c));
+ /* Start transmission - PERIOD LOW, CLOCK HIGH, DATA HIGH */
+ bus_space_write_1(c->iot, c->ioh, c->offset, PL_CH_DH(c));
+
+ /*
+ * Period must be low when the register address transmission starts.
+ * Period must be high when the register data transmission starts.
+ * Do the switch in the middle of the address transmission.
+ */
+ __tc921x_write_burst(4, addr, c, PERIOD_LOW);
+ __tc921x_write_burst(4, addr >> 4, c, PERIOD_HIGH);
+
+ /* Writing data to the register */
+ __tc921x_write_burst(TC921X_REGISTER_LENGTH, reg, c, 1);
+
+ /* End of transmission - PERIOD goes LOW then HIGH */
+ bus_space_write_1(c->iot, c->ioh, c->offset, PL_CH_DH(c));
+ bus_space_write_1(c->iot, c->ioh, c->offset, PH_CH_DH(c));
+}
+
+static void
+__tc921x_write_burst(unsigned int length, u_int32_t data, struct tc921x_t *c, int p) {
+ int i;
+ u_int8_t cldh, chdh, cldl, chdl;
+
+ cldh = p == PERIOD_LOW ? PL_CL_DH(c) : PH_CL_DH(c);
+ chdh = p == PERIOD_LOW ? PL_CH_DH(c) : PH_CH_DH(c);
+ cldl = p == PERIOD_LOW ? PL_CL_DL(c) : PH_CL_DL(c);
+ chdl = p == PERIOD_LOW ? PL_CH_DL(c) : PH_CH_DL(c);
+
+ for (i = 0; i < length; i++)
+ if (data & (1 << i)) {
+ bus_space_write_1(c->iot, c->ioh, c->offset, cldh);
+ bus_space_write_1(c->iot, c->ioh, c->offset, chdh);
+ } else {
+ bus_space_write_1(c->iot, c->ioh, c->offset, cldl);
+ bus_space_write_1(c->iot, c->ioh, c->offset, chdl);
+ }
+}
+
+static u_int32_t
+__tc921x_read_burst(unsigned int length, struct tc921x_t *c) {
+ unsigned int i;
+ u_int32_t ret = 0ul;
+
+#define DATA_ON (1 << c->data)
+
+ for (i = 0; i < length; i++) {
+ bus_space_write_1(c->iot, c->ioh, c->offset, PH_CL_DH(c));
+ bus_space_write_1(c->iot, c->ioh, c->offset, PH_CH_DH(c));
+ ret |= bus_space_read_1(c->iot, c->ioh, c->offset) & DATA_ON ?
+ (1 << i) : (0 << i);
+ }
+
+ return ret;
+}
diff --git a/sys/dev/ic/tc921x.h b/sys/dev/ic/tc921x.h
new file mode 100644
index 00000000000..21980e6c132
--- /dev/null
+++ b/sys/dev/ic/tc921x.h
@@ -0,0 +1,112 @@
+/* $OpenBSD: tc921x.h,v 1.1 2002/04/25 04:56:59 mickey Exp $ */
+
+/*
+ * Copyright (c) 2001, 2002 Vladimir Popov <jumbo@narod.ru>.
+ *
+ * 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 ``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 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.
+ */
+/*
+ * Toshiba's High Speed PLL for DTS
+ * http://www.chipbook.co.kr/pdf/ic/toshiba/TC9216.pdf
+ *
+ * TC9216P, TC9217P, TC9217F are a high speed PLL-LSI with built-in 2 modulus
+ * prescaler. Each function is controlled through 3 serial bus lines and high
+ * performance digital tuning system can be constitued.
+ *
+ */
+
+#ifndef _TC921X_H_
+#define _TC921X_H_
+
+#include <sys/types.h>
+
+#include <machine/bus.h>
+
+#define TC921X_REGISTER_LENGTH 24
+
+/* Input Register at 0xD0 */
+#define TC921X_D0_FREQ_DIVIDER 0xFFFF
+
+/* (*) are only available at 4.5 MHz crystal resonator used */
+#define TC921X_D0_REF_FREQ_500_HZ (0x0 << 16)
+#define TC921X_D0_REF_FREQ_1_KHZ (0x1 << 16)
+#define TC921X_D0_REF_FREQ_2P5_KHZ (0x2 << 16)
+#define TC921X_D0_REF_FREQ_3_KHZ (0x3 << 16)
+#define TC921X_D0_REF_FREQ_3P125_KHZ (0x4 << 16)
+#define TC921X_D0_REF_FREQ_3PXXX_KHZ (0x5 << 16) /* (*) */
+#define TC921X_D0_REF_FREQ_5_KHZ (0x6 << 16)
+#define TC921X_D0_REF_FREQ_6P25_KHZ (0x7 << 16)
+#define TC921X_D0_REF_FREQ_7PXXX_KHZ (0x8 << 16) /* (*) */
+#define TC921X_D0_REF_FREQ_9_KHZ (0x9 << 16)
+#define TC921X_D0_REF_FREQ_10_KHZ (0xA << 16)
+#define TC921X_D0_REF_FREQ_12P5_KHZ (0xB << 16)
+#define TC921X_D0_REF_FREQ_25_KHZ (0xC << 16)
+#define TC921X_D0_REF_FREQ_50_KHZ (0xD << 16)
+#define TC921X_D0_REF_FREQ_100_KHZ (0xE << 16)
+#define TC921X_D0_REF_FREQ_NOT_USED (0xF << 16)
+
+#define TC921X_D0_DIRECT_DIVIDING_MODE (0 << 20)
+#define TC921X_D0_PULSE_SWALLOW_HF_MODE (2 << 20)
+#define TC921X_D0_PULSE_SWALLOW_FM_MODE (1 << 20)
+#define TC921X_D0_HALF_PULSE_SWALLOW_MODE (3 << 20)
+
+#define TC921X_D0_OSC_7POINT2_MHZ (1 << 22)
+#define TC921X_D0_OSC_4POINT5_MHZ (0 << 22)
+
+#define TC921X_D0_OUT_CONTROL_ON (1 << 23)
+#define TC921X_D0_OUT_CONTROL_OFF (0 << 23)
+
+/* Input Register at 0xD2 */
+#define TC921X_D2_GATE_TIME(x) (x << 0)
+#define TC921X_D2_GATE_TIME_1MS TC921X_D2_GATE_TIME(0)
+#define TC921X_D2_GATE_TIME_4MS TC921X_D2_GATE_TIME(1)
+#define TC921X_D2_GATE_TIME_16MS TC921X_D2_GATE_TIME(2)
+#define TC921X_D2_GATE_TIME_MANUAL TC921X_D2_GATE_TIME(3)
+
+#define TC921X_D2_COUNTER_MODE(x) (x << 2)
+
+#define TC921X_D2_COUNTER_INPUT_SC (1 << 5)
+#define TC921X_D2_COUNTER_INPUT_HFC (1 << 6)
+#define TC921X_D2_COUNTER_INPUT_LFC (1 << 7)
+
+#define TC921X_D2_START_BIT (1 << 8)
+#define TC921X_D2_TEST_BIT (1 << 9)
+
+#define TC921X_D2_IO_PORT(x) (x << 10)
+#define TC921X_D2_IO_PORT_OUTPUT(x) (x << 15)
+#define TC921X_D2_IO_PORT_INPUT(x) (x << 19)
+
+struct tc921x_t {
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_size_t offset;
+
+ u_int8_t period;
+ u_int8_t clock;
+ u_int8_t data;
+};
+
+void tc921x_write_addr(struct tc921x_t *, u_int8_t, u_int32_t);
+u_int32_t tc921x_read_addr(struct tc921x_t *, u_int8_t);
+u_int32_t tc921x_encode_freq(u_int32_t);
+u_int32_t tc921x_decode_freq(u_int32_t);
+
+#endif /* _TC921X_H_ */
diff --git a/sys/dev/isa/files.isa b/sys/dev/isa/files.isa
index 08dc5625cb0..9def8d83545 100644
--- a/sys/dev/isa/files.isa
+++ b/sys/dev/isa/files.isa
@@ -1,4 +1,4 @@
-# $OpenBSD: files.isa,v 1.75 2002/02/12 19:02:22 mickey Exp $
+# $OpenBSD: files.isa,v 1.76 2002/04/25 04:56:59 mickey Exp $
# $NetBSD: files.isa,v 1.21 1996/05/16 03:45:55 mycroft Exp $
#
# Config file and device description for machine-independent ISA code.
@@ -338,6 +338,11 @@ file dev/isa/gus_isa.c gus & (gus_isa | gus_isapnp) needs-flag
attach opl at isa with opl_isa
file dev/isa/opl_isa.c opl_isa
+# Sound Forte RadioLink SF16-FMR FM Radio Card
+device sfr: radio, tc921x, pt2254a
+attach sfr at isa
+file dev/isa/sf16fmr.c sfr
+
# Sound Forte RadioLink SF16-FMR2 FM Radio Card
device sf2r: radio, tea5757
attach sf2r at isa
diff --git a/sys/dev/isa/sf16fmr.c b/sys/dev/isa/sf16fmr.c
new file mode 100644
index 00000000000..79f8e40a683
--- /dev/null
+++ b/sys/dev/isa/sf16fmr.c
@@ -0,0 +1,283 @@
+/* $OpenBSD: sf16fmr.c,v 1.1 2002/04/25 04:56:59 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 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.
+ */
+
+/* SoundForte RadioLink SF16-FMR FM Radio Card device driver */
+
+#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 <dev/isa/isavar.h>
+#include <dev/radio_if.h>
+
+#include <dev/ic/tc921x.h>
+#include <dev/ic/pt2254a.h>
+
+#define SF16FMR_BASE_VALID(x) (x == 0x384 || x == 0x284)
+
+#define SF16FMR_CAPABILITIES 0
+
+#define SF16FMR_FREQ_DATA 0
+#define SF16FMR_FREQ_CLOCK 1
+#define SF16FMR_FREQ_PERIOD 2
+
+#define SF16FMR_FREQ_STEADY (1 << SF16FMR_FREQ_DATA) | \
+ (1 << SF16FMR_FREQ_CLOCK) | \
+ (1 << SF16FMR_FREQ_PERIOD)
+
+#define SF16FMR_VOLU_STROBE_ON (1 << 3)
+#define SF16FMR_VOLU_STROBE_OFF (0 << 3)
+#define SF16FMR_VOLU_CLOCK_ON (1 << 4)
+#define SF16FMR_VOLU_CLOCK_OFF (0 << 4)
+#define SF16FMR_VOLU_DATA_ON (1 << 5)
+#define SF16FMR_VOLU_DATA_OFF (0 << 5)
+
+int sfr_probe(struct device *, void *, void *);
+void sfr_attach(struct device *, struct device * self, void *);
+
+int sfr_get_info(void *, struct radio_info *);
+int sfr_set_info(void *, struct radio_info *);
+
+/* define our interface to the higher level radio driver */
+struct radio_hw_if sfr_hw_if = {
+ NULL, /* open */
+ NULL, /* close */
+ sfr_get_info,
+ sfr_set_info,
+ NULL
+};
+
+struct sfr_softc {
+ struct device sc_dev;
+
+ u_int32_t freq;
+ u_int8_t vol;
+ int mute;
+
+ struct tc921x_t c;
+};
+
+struct cfattach sfr_ca = {
+ sizeof(struct sfr_softc), sfr_probe, sfr_attach
+};
+
+struct cfdriver sfr_cd = {
+ NULL, "sfr", DV_DULL
+};
+
+int sfr_find(bus_space_tag_t, bus_space_handle_t);
+u_int32_t sfr_set_freq(struct tc921x_t *, u_int32_t);
+u_int32_t sfr_get_freq(struct tc921x_t *);
+u_int8_t sfr_set_vol(bus_space_tag_t, bus_space_handle_t, u_int8_t, int);
+void sfr_send_volume(bus_space_tag_t, bus_space_handle_t, u_int32_t);
+
+int
+sfr_probe(struct device *parent, void *match, 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 (!SF16FMR_BASE_VALID(iobase)) {
+ printf("sfr: configured iobase 0x%x invalid\n", iobase);
+ return (0);
+ }
+
+ if (bus_space_map(iot, iobase, iosize, 0, &ioh))
+ return (0);
+
+ if (!sfr_find(iot, ioh)) {
+ bus_space_unmap(iot, ioh, iosize);
+ return (0);
+ }
+
+ bus_space_unmap(iot, ioh, iosize);
+ ia->ia_iosize = iosize;
+ return (1);
+}
+
+void
+sfr_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct sfr_softc *sc = (void *) self;
+ struct isa_attach_args *ia = aux;
+
+ sc->c.iot = ia->ia_iot;
+ sc->mute = 0;
+ sc->vol = 0;
+ sc->freq = MIN_FM_FREQ;
+ sc->c.period = SF16FMR_FREQ_PERIOD;
+ sc->c.clock = SF16FMR_FREQ_CLOCK;
+ sc->c.data = SF16FMR_FREQ_DATA;
+
+ /* remap I/O */
+ if (bus_space_map(sc->c.iot, ia->ia_iobase, ia->ia_iosize,
+ 0, &sc->c.ioh)) {
+ printf(": bus_space_map() failed\n");
+ return;
+ }
+
+ printf(": SoundForte RadioLink SF16-FMR\n");
+ sfr_set_freq(&sc->c, sc->freq);
+ sfr_set_vol(sc->c.iot, sc->c.ioh, sc->vol, sc->mute);
+
+ radio_attach_mi(&sfr_hw_if, sc, &sc->sc_dev);
+}
+
+int
+sfr_find(bus_space_tag_t iot, bus_space_handle_t ioh)
+{
+ struct sfr_softc sc;
+ u_int32_t freq;
+
+ sc.c.iot = iot;
+ sc.c.ioh = ioh;
+ sc.c.offset = 0;
+ sc.c.period = SF16FMR_FREQ_PERIOD;
+ sc.c.clock = SF16FMR_FREQ_CLOCK;
+ sc.c.data = SF16FMR_FREQ_DATA;
+
+ /*
+ * Let's try to write and read a frequency.
+ * If the written and read frequencies are
+ * the same then success.
+ */
+ sc.freq = MIN_FM_FREQ;
+ /* Initialize the tc921x chip */
+ sfr_set_freq(&sc.c, sc.freq);
+ /* Do actual frequency setting */
+ freq = sfr_set_freq(&sc.c, sc.freq);
+ if (sc.freq == freq)
+ return 1;
+
+ return 0;
+}
+
+int
+sfr_get_info(void *v, struct radio_info *ri)
+{
+ struct sfr_softc *sc = v;
+
+ ri->mute = sc->mute;
+ ri->volume = sc->vol;
+ ri->caps = SF16FMR_CAPABILITIES;
+ ri->freq = sc->freq = sfr_get_freq(&sc->c);
+
+ /* Not supported */
+ ri->stereo = 1; /* Always stereo */
+ ri->rfreq = 0;
+ ri->lock = 0;
+
+ return (0);
+}
+
+int
+sfr_set_info(void *v, struct radio_info *ri)
+{
+ struct sfr_softc *sc = v;
+
+ sc->mute = ri->mute ? 1 : 0;
+ sc->vol = ri->volume;
+ sc->freq = sfr_set_freq(&sc->c, ri->freq);
+ sc->vol = sfr_set_vol(sc->c.iot, sc->c.ioh, sc->vol, sc->mute);
+
+ return (0);
+}
+
+u_int32_t
+sfr_set_freq(struct tc921x_t *c, u_int32_t freq) {
+ u_int32_t data = 0ul;
+
+ data |= tc921x_encode_freq(freq);
+ data |= TC921X_D0_REF_FREQ_10_KHZ;
+ data |= TC921X_D0_PULSE_SWALLOW_FM_MODE;
+ data |= TC921X_D0_OSC_7POINT2_MHZ;
+ data |= TC921X_D0_OUT_CONTROL_ON;
+ tc921x_write_addr(c, 0xD0, data);
+
+ data = TC921X_D2_IO_PORT_OUTPUT(4);
+ tc921x_write_addr(c, 0xD2, data);
+
+ return sfr_get_freq(c);
+}
+
+u_int32_t
+sfr_get_freq(struct tc921x_t *c) {
+ return tc921x_decode_freq(tc921x_read_addr(c, 0xD1));
+}
+
+u_int8_t
+sfr_set_vol(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t vol, int mute) {
+ u_int32_t v;
+ u_int8_t ret;
+
+ ret = mute ? 0 : vol;
+
+ v = pt2254a_encode_volume(&ret, 255);
+
+ sfr_send_volume(iot, ioh,
+ pt2254a_compose_register(v, v, USE_CHANNEL, USE_CHANNEL));
+
+ return ret;
+}
+
+void
+sfr_send_volume(bus_space_tag_t iot, bus_space_handle_t ioh, u_int32_t vol) {
+ u_int8_t one, zero;
+ int i;
+
+ one = zero = SF16FMR_FREQ_STEADY;
+ one = zero |= SF16FMR_VOLU_STROBE_OFF;
+
+ one |= SF16FMR_VOLU_DATA_ON;
+ zero |= SF16FMR_VOLU_DATA_OFF;
+
+ bus_space_write_1(iot, ioh, 0, SF16FMR_VOLU_STROBE_OFF | SF16FMR_FREQ_STEADY);
+
+ for (i = 0; i < PT2254A_REGISTER_LENGTH; i++) {
+ if (vol & (1 << i)) {
+ bus_space_write_1(iot, ioh, 0,
+ one | SF16FMR_VOLU_CLOCK_OFF);
+ bus_space_write_1(iot, ioh, 0,
+ one | SF16FMR_VOLU_CLOCK_ON);
+ } else {
+ bus_space_write_1(iot, ioh, 0,
+ zero | SF16FMR_VOLU_CLOCK_OFF);
+ bus_space_write_1(iot, ioh, 0,
+ zero | SF16FMR_VOLU_CLOCK_ON);
+ }
+ }
+
+ /* Latch the data */
+ bus_space_write_1(iot, ioh, 0, SF16FMR_VOLU_STROBE_ON | SF16FMR_FREQ_STEADY);
+ bus_space_write_1(iot, ioh, 0, SF16FMR_VOLU_STROBE_OFF | SF16FMR_FREQ_STEADY);
+}