1 /* $NetBSD: radiotrack.c,v 1.17 2009/05/12 08:44:19 cegger Exp $ */
2 /* $OpenBSD: radiotrack.c,v 1.1 2001/12/05 10:27:06 mickey Exp $ */
3 /* $RuOBSD: radiotrack.c,v 1.3 2001/10/18 16:51:36 pva Exp $ */
6 * Copyright (c) 2001 Maxim Tsyplakov <tm@oganer.net>,
7 * Vladimir Popov <jumbo@narod.ru>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 /* AIMS Lab Radiotrack FM Radio Card device driver */
34 * Sanyo LM7000 Direct PLL Frequency Synthesizer
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: radiotrack.c,v 1.17 2009/05/12 08:44:19 cegger Exp $");
40 #include <sys/param.h>
41 #include <sys/systm.h>
43 #include <sys/errno.h>
44 #include <sys/ioctl.h>
45 #include <sys/device.h>
46 #include <sys/radioio.h>
50 #include <dev/isa/isavar.h>
51 #include <dev/ic/lm700x.h>
52 #include <dev/radio_if.h>
58 #define MAX_VOL 5 /* XXX Find real value */
59 #define VOLUME_RATIO(x) (255 * x / MAX_VOL)
61 #define RT_BASE_VALID(x) \
62 ((x == 0x30C) || (x == 0x20C) || (x == 0x284) || (x == 0x384))
64 #define CARD_RADIOTRACK 0x01
65 #define CARD_SF16FMI 0x02
66 #define CARD_UNKNOWN 0xFF
68 #define RTRACK_CAPABILITIES RADIO_CAPS_DETECT_STEREO | \
69 RADIO_CAPS_DETECT_SIGNAL | \
70 RADIO_CAPS_SET_MONO | \
71 RADIO_CAPS_REFERENCE_FREQ
73 #define RT_WREN_ON (1 << 0)
74 #define RT_WREN_OFF (0 << 0)
76 #define RT_CLCK_ON (1 << 1)
77 #define RT_CLCK_OFF (0 << 1)
79 #define RT_DATA_ON (1 << 2)
80 #define RT_DATA_OFF (0 << 2)
82 #define RT_CARD_ON (1 << 3)
83 #define RT_CARD_OFF (0 << 3)
85 #define RT_SIGNAL_METER (1 << 4)
86 #define RT_SIGNAL_METER_DELAY 150000
88 #define RT_VOLUME_DOWN (1 << 6)
89 #define RT_VOLUME_UP (2 << 6)
90 #define RT_VOLUME_STEADY (3 << 6)
91 #define RT_VOLUME_DELAY 100000
93 int rt_probe(device_t
, cfdata_t
, void *);
94 void rt_attach(device_t
, device_t self
, void *);
95 int rt_get_info(void *, struct radio_info
*);
96 int rt_set_info(void *, struct radio_info
*);
98 const struct radio_hw_if rt_hw_if
= {
107 struct device sc_dev
;
119 CFATTACH_DECL(rt
, sizeof(struct rt_softc
),
120 rt_probe
, rt_attach
, NULL
, NULL
);
122 int rt_find(bus_space_tag_t
, bus_space_handle_t
);
123 void rt_set_mute(struct rt_softc
*, int);
124 void rt_set_freq(struct rt_softc
*, u_int32_t
);
125 u_int8_t
rt_state(bus_space_tag_t
, bus_space_handle_t
);
127 void rt_lm700x_init(bus_space_tag_t
, bus_space_handle_t
, bus_size_t
, u_int32_t
);
128 void rt_lm700x_rset(bus_space_tag_t
, bus_space_handle_t
, bus_size_t
, u_int32_t
);
130 u_int8_t
rt_conv_vol(u_int8_t
);
131 u_int8_t
rt_unconv_vol(u_int8_t
);
134 rt_probe(device_t parent
, cfdata_t cf
, void *aux
)
136 struct isa_attach_args
*ia
= aux
;
137 bus_space_tag_t iot
= ia
->ia_iot
;
138 bus_space_handle_t ioh
;
140 int iosize
= 1, iobase
;
142 if (ISA_DIRECT_CONFIG(ia
))
148 iobase
= ia
->ia_io
[0].ir_addr
;
150 if (!RT_BASE_VALID(iobase
)) {
151 printf("rt: configured iobase 0x%x invalid\n", iobase
);
155 if (bus_space_map(iot
, iobase
, iosize
, 0, &ioh
))
158 r
= rt_find(iot
, ioh
);
160 bus_space_unmap(iot
, ioh
, iosize
);
164 ia
->ia_io
[0].ir_size
= iosize
;
177 rt_attach(device_t parent
, device_t self
, void *aux
)
179 struct rt_softc
*sc
= (void *) self
;
180 struct isa_attach_args
*ia
= aux
;
182 sc
->lm
.iot
= ia
->ia_iot
;
183 sc
->rf
= LM700X_REF_050
;
184 sc
->stereo
= LM700X_STEREO
;
186 sc
->freq
= MIN_FM_FREQ
;
190 if (bus_space_map(sc
->lm
.iot
, ia
->ia_io
[0].ir_addr
,
191 ia
->ia_io
[0].ir_size
, 0, &sc
->lm
.ioh
))
192 panic(": bus_space_map() of %s failed", device_xname(&sc
->sc_dev
));
194 switch (ia
->ia_io
[0].ir_addr
) {
198 sc
->cardtype
= CARD_RADIOTRACK
;
199 printf(": AIMS Lab Radiotrack or compatible\n");
204 sc
->cardtype
= CARD_SF16FMI
;
205 printf(": SoundForte RadioX SF16-FMI\n");
208 sc
->cardtype
= CARD_UNKNOWN
;
209 printf(": Unknown card\n");
213 /* Configure struct lm700x_t lm */
215 sc
->lm
.wzcl
= RT_WREN_ON
| RT_CLCK_OFF
| RT_DATA_OFF
;
216 sc
->lm
.wzch
= RT_WREN_ON
| RT_CLCK_ON
| RT_DATA_OFF
;
217 sc
->lm
.wocl
= RT_WREN_ON
| RT_CLCK_OFF
| RT_DATA_ON
;
218 sc
->lm
.woch
= RT_WREN_ON
| RT_CLCK_ON
| RT_DATA_ON
;
220 sc
->lm
.rsetdata
= RT_DATA_ON
| RT_CLCK_ON
| RT_WREN_OFF
;
221 sc
->lm
.init
= rt_lm700x_init
;
222 sc
->lm
.rset
= rt_lm700x_rset
;
224 rt_set_freq(sc
, sc
->freq
);
226 radio_attach_mi(&rt_hw_if
, sc
, &sc
->sc_dev
);
233 rt_set_mute(struct rt_softc
*sc
, int vol
)
238 bus_space_write_1(sc
->lm
.iot
, sc
->lm
.ioh
, 0,
239 RT_VOLUME_DOWN
| RT_CARD_ON
);
240 DELAY(MAX_VOL
* RT_VOLUME_DELAY
);
241 bus_space_write_1(sc
->lm
.iot
, sc
->lm
.ioh
, 0,
242 RT_VOLUME_STEADY
| RT_CARD_ON
);
243 bus_space_write_1(sc
->lm
.iot
, sc
->lm
.ioh
, 0, RT_CARD_OFF
);
244 bus_space_write_1(sc
->lm
.iot
, sc
->lm
.ioh
, 0, RT_CARD_OFF
);
249 bus_space_write_1(sc
->lm
.iot
, sc
->lm
.ioh
, 0,
250 RT_VOLUME_DOWN
| RT_CARD_ON
);
252 bus_space_write_1(sc
->lm
.iot
, sc
->lm
.ioh
, 0,
253 RT_VOLUME_UP
| RT_CARD_ON
);
255 DELAY(val
* RT_VOLUME_DELAY
);
256 bus_space_write_1(sc
->lm
.iot
, sc
->lm
.ioh
, 0,
257 RT_VOLUME_STEADY
| RT_CARD_ON
);
262 rt_set_freq(struct rt_softc
*sc
, u_int32_t nfreq
)
266 if (nfreq
> MAX_FM_FREQ
)
268 if (nfreq
< MIN_FM_FREQ
)
273 reg
= lm700x_encode_freq(nfreq
, sc
->rf
);
274 reg
|= sc
->stereo
| sc
->rf
| LM700X_DIVIDER_FM
;
276 lm700x_hardware_write(&sc
->lm
, reg
, RT_VOLUME_STEADY
);
278 rt_set_mute(sc
, sc
->vol
);
282 * Return state of the card - tuned/not tuned, mono/stereo
285 rt_state(bus_space_tag_t iot
, bus_space_handle_t ioh
)
289 bus_space_write_1(iot
, ioh
, 0,
290 RT_VOLUME_STEADY
| RT_SIGNAL_METER
| RT_CARD_ON
);
291 DELAY(RT_SIGNAL_METER_DELAY
);
292 ret
= bus_space_read_1(iot
, ioh
, 0);
296 ret
= RADIO_INFO_SIGNAL
| RADIO_INFO_STEREO
;
302 ret
= RADIO_INFO_SIGNAL
;
310 * Convert volume to hardware representation.
313 rt_conv_vol(u_int8_t vol
)
315 if (vol
< VOLUME_RATIO(1))
317 else if (vol
>= VOLUME_RATIO(1) && vol
< VOLUME_RATIO(2))
319 else if (vol
>= VOLUME_RATIO(2) && vol
< VOLUME_RATIO(3))
321 else if (vol
>= VOLUME_RATIO(3) && vol
< VOLUME_RATIO(4))
328 * Convert volume from hardware representation
331 rt_unconv_vol(u_int8_t vol
)
333 return VOLUME_RATIO(vol
);
337 rt_find(bus_space_tag_t iot
, bus_space_handle_t ioh
)
341 u_int i
, scanres
= 0;
347 sc
.lm
.wzcl
= RT_WREN_ON
| RT_CLCK_OFF
| RT_DATA_OFF
;
348 sc
.lm
.wzch
= RT_WREN_ON
| RT_CLCK_ON
| RT_DATA_OFF
;
349 sc
.lm
.wocl
= RT_WREN_ON
| RT_CLCK_OFF
| RT_DATA_ON
;
350 sc
.lm
.woch
= RT_WREN_ON
| RT_CLCK_ON
| RT_DATA_ON
;
352 sc
.lm
.rsetdata
= RT_SIGNAL_METER
;
353 sc
.lm
.init
= rt_lm700x_init
;
354 sc
.lm
.rset
= rt_lm700x_rset
;
355 sc
.rf
= LM700X_REF_050
;
357 sc
.stereo
= LM700X_STEREO
;
361 * Scan whole FM range. If there is a card it'll
362 * respond on some frequency.
366 for (i
= MIN_FM_FREQ
; !scanres
&& i
< MAX_FM_FREQ
; i
+= 10) {
368 scanres
+= rt_state(iot
, ioh
);
376 rt_lm700x_init(bus_space_tag_t iot
, bus_space_handle_t ioh
,
377 bus_size_t off
, u_int32_t data
)
384 rt_lm700x_rset(bus_space_tag_t iot
, bus_space_handle_t ioh
, bus_size_t off
,
388 bus_space_write_1(iot
, ioh
, off
, RT_CARD_OFF
| data
);
390 bus_space_write_1(iot
, ioh
, off
, RT_VOLUME_STEADY
| RT_CARD_ON
| data
);
394 rt_set_info(void *v
, struct radio_info
*ri
)
396 struct rt_softc
*sc
= v
;
398 sc
->mute
= ri
->mute
? 1 : 0;
399 sc
->vol
= rt_conv_vol(ri
->volume
);
400 sc
->stereo
= ri
->stereo
? LM700X_STEREO
: LM700X_MONO
;
401 sc
->rf
= lm700x_encode_ref(ri
->rfreq
);
403 rt_set_freq(sc
, ri
->freq
);
404 rt_set_mute(sc
, sc
->vol
);
410 rt_get_info(void *v
, struct radio_info
*ri
)
412 struct rt_softc
*sc
= v
;
415 ri
->volume
= rt_unconv_vol(sc
->vol
);
416 ri
->stereo
= sc
->stereo
== LM700X_STEREO
? 0 : 1;
417 ri
->caps
= RTRACK_CAPABILITIES
;
418 ri
->rfreq
= lm700x_decode_ref(sc
->rf
);
419 ri
->info
= 3 & rt_state(sc
->lm
.iot
, sc
->lm
.ioh
);