1 /* $NetBSD: aztech.c,v 1.15 2009/05/12 08:44:19 cegger Exp $ */
2 /* $OpenBSD: aztech.c,v 1.2 2001/12/05 10:27:06 mickey Exp $ */
3 /* $RuOBSD: aztech.c,v 1.11 2001/10/20 13:23:47 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 /* Aztech/PackardBell FM Radio Card device driver */
34 * Sanyo LM7001J Direct PLL Frequency Synthesizer:
35 * ??? See http://www.redsword.com/tjacobs/geeb/fmcard.htm
37 * Philips TEA5712T AM/FM Stereo DTS Radio:
38 * http://www.semiconductors.philips.com/pip/TEA5712
41 #include <sys/cdefs.h>
42 __KERNEL_RCSID(0, "$NetBSD: aztech.c,v 1.15 2009/05/12 08:44:19 cegger Exp $");
44 #include <sys/param.h>
45 #include <sys/systm.h>
47 #include <sys/errno.h>
48 #include <sys/ioctl.h>
49 #include <sys/device.h>
50 #include <sys/radioio.h>
54 #include <dev/isa/isavar.h>
55 #include <dev/ic/lm700x.h>
56 #include <dev/radio_if.h>
63 #define VOLUME_RATIO(x) (255 * x / MAX_VOL)
65 #define AZ_BASE_VALID(x) ((x == 0x350) || (x == 0x358))
66 #define AZTECH_CAPABILITIES RADIO_CAPS_DETECT_STEREO | \
67 RADIO_CAPS_DETECT_SIGNAL | \
68 RADIO_CAPS_SET_MONO | \
69 RADIO_CAPS_REFERENCE_FREQ
71 #define AZ_WREN_ON (1 << 1)
72 #define AZ_WREN_OFF (0 << 1)
74 #define AZ_CLCK_ON (1 << 6)
75 #define AZ_CLCK_OFF (0 << 6)
77 #define AZ_DATA_ON (1 << 7)
78 #define AZ_DATA_OFF (0 << 7)
80 int az_probe(device_t
, cfdata_t
, void *);
81 void az_attach(device_t
, device_t self
, void *);
83 int az_get_info(void *, struct radio_info
*);
84 int az_set_info(void *, struct radio_info
*);
86 const struct radio_hw_if az_hw_if
= {
106 CFATTACH_DECL(az
, sizeof(struct az_softc
),
107 az_probe
, az_attach
, NULL
, NULL
);
109 u_int
az_find(bus_space_tag_t
, bus_space_handle_t
);
110 void az_set_mute(struct az_softc
*);
111 void az_set_freq(struct az_softc
*, u_int32_t
);
112 u_int8_t
az_state(bus_space_tag_t
, bus_space_handle_t
);
114 void az_lm700x_init(bus_space_tag_t
, bus_space_handle_t
, bus_size_t
, u_int32_t
);
115 void az_lm700x_rset(bus_space_tag_t
, bus_space_handle_t
, bus_size_t
, u_int32_t
);
117 u_int8_t
az_conv_vol(u_int8_t
);
118 u_int8_t
az_unconv_vol(u_int8_t
);
121 az_probe(device_t parent
, cfdata_t cf
, void *aux
)
123 struct isa_attach_args
*ia
= aux
;
124 bus_space_tag_t iot
= ia
->ia_iot
;
125 bus_space_handle_t ioh
;
127 int iosize
= 1, iobase
;
129 if (ISA_DIRECT_CONFIG(ia
))
135 iobase
= ia
->ia_io
[0].ir_addr
;
137 if (!AZ_BASE_VALID(iobase
)) {
138 printf("az: configured iobase 0x%x invalid", iobase
);
142 if (bus_space_map(iot
, iobase
, iosize
, 0, &ioh
))
145 r
= az_find(iot
, ioh
);
147 bus_space_unmap(iot
, ioh
, iosize
);
151 ia
->ia_io
[0].ir_size
= iosize
;
164 az_attach(device_t parent
, device_t self
, void *aux
)
166 struct az_softc
*sc
= (void *)self
;
167 struct isa_attach_args
*ia
= aux
;
169 sc
->lm
.iot
= ia
->ia_iot
;
170 sc
->rf
= LM700X_REF_050
;
171 sc
->stereo
= LM700X_STEREO
;
173 sc
->freq
= MIN_FM_FREQ
;
177 if (bus_space_map(sc
->lm
.iot
, ia
->ia_io
[0].ir_addr
,
178 ia
->ia_io
[0].ir_size
, 0, &sc
->lm
.ioh
))
179 panic(": bus_space_map() of %s failed", device_xname(&sc
->sc_dev
));
181 printf(": Aztech/PackardBell\n");
183 /* Configure struct lm700x_t lm */
185 sc
->lm
.wzcl
= AZ_WREN_ON
| AZ_CLCK_OFF
| AZ_DATA_OFF
;
186 sc
->lm
.wzch
= AZ_WREN_ON
| AZ_CLCK_ON
| AZ_DATA_OFF
;
187 sc
->lm
.wocl
= AZ_WREN_ON
| AZ_CLCK_OFF
| AZ_DATA_ON
;
188 sc
->lm
.woch
= AZ_WREN_ON
| AZ_CLCK_ON
| AZ_DATA_ON
;
190 sc
->lm
.rsetdata
= AZ_DATA_ON
| AZ_CLCK_ON
| AZ_WREN_OFF
;
191 sc
->lm
.init
= az_lm700x_init
;
192 sc
->lm
.rset
= az_lm700x_rset
;
194 az_set_freq(sc
, sc
->freq
);
196 radio_attach_mi(&az_hw_if
, sc
, &sc
->sc_dev
);
203 az_set_mute(struct az_softc
*sc
)
205 bus_space_write_1(sc
->lm
.iot
, sc
->lm
.ioh
, 0,
206 sc
->mute
? 0 : sc
->vol
);
208 bus_space_write_1(sc
->lm
.iot
, sc
->lm
.ioh
, 0,
209 sc
->mute
? 0 : sc
->vol
);
213 az_set_freq(struct az_softc
*sc
, u_int32_t nfreq
)
218 vol
= sc
->mute
? 0 : sc
->vol
;
220 if (nfreq
> MAX_FM_FREQ
)
222 if (nfreq
< MIN_FM_FREQ
)
227 reg
= lm700x_encode_freq(nfreq
, sc
->rf
);
228 reg
|= sc
->stereo
| sc
->rf
| LM700X_DIVIDER_FM
;
230 lm700x_hardware_write(&sc
->lm
, reg
, vol
);
236 * Return state of the card - tuned/not tuned, mono/stereo
239 az_state(bus_space_tag_t iot
, bus_space_handle_t ioh
)
241 return (3 ^ bus_space_read_1(iot
, ioh
, 0)) & 3;
245 * Convert volume to hardware representation.
246 * The card uses bits 00000x0x to set volume.
249 az_conv_vol(u_int8_t vol
)
251 if (vol
< VOLUME_RATIO(1))
253 else if (vol
>= VOLUME_RATIO(1) && vol
< VOLUME_RATIO(2))
255 else if (vol
>= VOLUME_RATIO(2) && vol
< VOLUME_RATIO(3))
262 * Convert volume from hardware representation
265 az_unconv_vol(u_int8_t vol
)
269 return VOLUME_RATIO(0);
271 return VOLUME_RATIO(1);
273 return VOLUME_RATIO(2);
275 return VOLUME_RATIO(3);
279 az_find(bus_space_tag_t iot
, bus_space_handle_t ioh
)
282 u_int i
, scanres
= 0;
287 sc
.lm
.wzcl
= AZ_WREN_ON
| AZ_CLCK_OFF
| AZ_DATA_OFF
;
288 sc
.lm
.wzch
= AZ_WREN_ON
| AZ_CLCK_ON
| AZ_DATA_OFF
;
289 sc
.lm
.wocl
= AZ_WREN_ON
| AZ_CLCK_OFF
| AZ_DATA_ON
;
290 sc
.lm
.woch
= AZ_WREN_ON
| AZ_CLCK_ON
| AZ_DATA_ON
;
292 sc
.lm
.rsetdata
= AZ_DATA_ON
| AZ_CLCK_ON
| AZ_WREN_OFF
;
293 sc
.lm
.init
= az_lm700x_init
;
294 sc
.lm
.rset
= az_lm700x_rset
;
295 sc
.rf
= LM700X_REF_050
;
297 sc
.stereo
= LM700X_STEREO
;
301 * Scan whole FM range. If there is a card it'll
302 * respond on some frequency.
304 for (i
= MIN_FM_FREQ
; !scanres
&& i
< MAX_FM_FREQ
; i
+= 10) {
306 scanres
+= 3 - az_state(iot
, ioh
);
313 az_lm700x_init(bus_space_tag_t iot
, bus_space_handle_t ioh
,
314 bus_size_t off
, u_int32_t data
)
321 az_lm700x_rset(bus_space_tag_t iot
, bus_space_handle_t ioh
, bus_size_t off
,
324 bus_space_write_1(iot
, ioh
, off
, data
);
328 az_get_info(void *v
, struct radio_info
*ri
)
330 struct az_softc
*sc
= v
;
333 ri
->volume
= az_unconv_vol(sc
->vol
);
334 ri
->stereo
= sc
->stereo
== LM700X_STEREO
? 1 : 0;
335 ri
->caps
= AZTECH_CAPABILITIES
;
336 ri
->rfreq
= lm700x_decode_ref(sc
->rf
);
337 ri
->info
= az_state(sc
->lm
.iot
, sc
->lm
.ioh
);
347 az_set_info(void *v
, struct radio_info
*ri
)
349 struct az_softc
*sc
= v
;
351 sc
->mute
= ri
->mute
? 1 : 0;
352 sc
->vol
= az_conv_vol(ri
->volume
);
353 sc
->stereo
= ri
->stereo
? LM700X_STEREO
: LM700X_MONO
;
354 sc
->rf
= lm700x_encode_ref(ri
->rfreq
);
356 az_set_freq(sc
, ri
->freq
);