1 /* $OpenBSD: radiotrack.c,v 1.4 2002/08/28 21:20:48 mickey Exp $ */
2 /* $RuOBSD: radiotrack.c,v 1.3 2001/10/18 16:51:36 pva Exp $ */
3 
4 /*
5  * Copyright (c) 2001, 2002 Maxim Tsyplakov <tm@oganer.net>,
6  *			    Vladimir Popov <jumbo@narod.ru>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /* AIMS Lab Radiotrack FM Radio Card device driver */
31 
32 /*
33  * Sanyo LM7000 Direct PLL Frequency Synthesizer
34  */
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/ioctl.h>
39 #include <sys/device.h>
40 #include <sys/radioio.h>
41 
42 #include <machine/bus.h>
43 
44 #include <dev/ic/lm700x.h>
45 #include <dev/isa/isavar.h>
46 #include <dev/isa/rtreg.h>
47 #include <dev/isa/rtvar.h>
48 #include <dev/radio_if.h>
49 
50 void	rtattach(struct rt_softc *);
51 int	rt_get_info(void *, struct radio_info *);
52 int	rt_set_info(void *, struct radio_info *);
53 
54 struct radio_hw_if rt_hw_if = {
55 	NULL,	/* open */
56 	NULL,	/* close */
57 	rt_get_info,
58 	rt_set_info,
59 	NULL
60 };
61 
62 struct cfdriver rt_cd = {
63 	NULL, "rt", DV_DULL
64 };
65 
66 void	rt_set_mute(struct rt_softc *, int);
67 void	rt_set_freq(struct rt_softc *, u_int32_t);
68 u_int8_t	rt_state(bus_space_tag_t, bus_space_handle_t);
69 
70 void	sfi_lm700x_init(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t);
71 void	rt_lm700x_init(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t);
72 void	rt_lm700x_rset(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t);
73 
74 u_int8_t	rt_conv_vol(u_int8_t);
75 u_int8_t	rt_unconv_vol(u_int8_t);
76 
77 void
rtattach(struct rt_softc * sc)78 rtattach(struct rt_softc *sc) {
79 	sc->sc_freq = MIN_FM_FREQ;
80 	sc->sc_mute = 0;
81 	sc->sc_vol = 0;
82 	sc->sc_rf = LM700X_REF_050;
83 	sc->sc_stereo = LM700X_STEREO;
84 
85 	sc->lm.wzcl = RT_WREN_ON | RT_CLCK_OFF | RT_DATA_OFF;
86 	sc->lm.wzch = RT_WREN_ON | RT_CLCK_ON  | RT_DATA_OFF;
87 	sc->lm.wocl = RT_WREN_ON | RT_CLCK_OFF | RT_DATA_ON;
88 	sc->lm.woch = RT_WREN_ON | RT_CLCK_ON  | RT_DATA_ON;
89 
90 	switch (sc->sc_ct) {
91 	case CARD_RADIOTRACK:
92 		sc->lm.initdata = 0;
93 		sc->lm.rsetdata = RT_SIGNAL_METER;
94 		sc->lm.init = rt_lm700x_init;
95 		sc->lm.rset = rt_lm700x_rset;
96 		break;
97 	case CARD_SF16FMI:
98 		sc->lm.initdata = RT_CARD_OFF;
99 		sc->lm.rsetdata = RT_CARD_ON;
100 		sc->lm.init = sfi_lm700x_init;
101 		sc->lm.rset = sfi_lm700x_init;
102 		break;
103 	}
104 
105 	rt_set_freq(sc, sc->sc_freq);
106 	rt_set_mute(sc, sc->sc_vol);
107 
108 	radio_attach_mi(&rt_hw_if, sc, &sc->sc_dev);
109 }
110 
111 int
rt_set_info(void * v,struct radio_info * ri)112 rt_set_info(void *v, struct radio_info *ri)
113 {
114 	struct rt_softc *sc = v;
115 
116 	sc->sc_mute = ri->mute ? 1 : 0;
117 	sc->sc_rf = lm700x_encode_ref(ri->rfreq);
118 
119 	switch (sc->sc_ct) {
120 	case CARD_RADIOTRACK:
121 		sc->sc_vol = rt_conv_vol(ri->volume);
122 		break;
123 	case CARD_SF16FMI:
124 		sc->sc_vol = ri->volume ? 1 : 0;
125 		break;
126 	}
127 	/*
128 	 * Though SF16-FMI does not set stereo/mono
129 	 * it won't hurt to have this
130 	 */
131 	sc->sc_stereo = ri->stereo ? LM700X_STEREO : LM700X_MONO;
132 
133 	rt_set_freq(sc, ri->freq);
134 	rt_set_mute(sc, sc->sc_vol);
135 
136 	return (0);
137 }
138 
139 int
rt_get_info(void * v,struct radio_info * ri)140 rt_get_info(void *v, struct radio_info *ri)
141 {
142 	struct rt_softc *sc = v;
143 
144 	switch (sc->sc_ct) {
145 	case CARD_RADIOTRACK:
146 		ri->caps = RTRACK_CAPABILITIES;
147 		ri->info = 3 & rt_state(sc->lm.iot, sc->lm.ioh);
148 		ri->volume = rt_unconv_vol(sc->sc_vol);
149 		break;
150 	case CARD_SF16FMI:
151 		ri->caps = SF16FMI_CAPABILITIES;
152 		ri->volume = sc->sc_vol ? 255 : 0;
153 		ri->info = 0; /* UNSUPPORTED */
154 		break;
155 	default:
156 		/* No such card */
157 		return (1);
158 	}
159 
160 	ri->mute = sc->sc_mute;
161 	ri->stereo = sc->sc_stereo == LM700X_STEREO ? 0 : 1;
162 	ri->rfreq = lm700x_decode_ref(sc->sc_rf);
163 	ri->freq = sc->sc_freq;
164 
165 	/* UNSUPPORTED */
166 	ri->lock = 0;
167 
168 	return (0);
169 }
170 
171 /*
172  * Mute the card
173  */
174 void
rt_set_mute(struct rt_softc * sc,int vol)175 rt_set_mute(struct rt_softc *sc, int vol)
176 {
177 	int val;
178 
179 	if (sc->sc_ct == CARD_UNKNOWN)
180 		return;
181 
182 	if (sc->sc_ct == CARD_SF16FMI) {
183 		val = vol ? RT_CARD_ON : RT_CARD_OFF;
184 		bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0,
185 				sc->sc_mute ? RT_CARD_OFF : val);
186 		return;
187 	}
188 
189 	/* CARD_RADIOTRACK */
190 	if (sc->sc_mute) {
191 		bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0,
192 				RT_VOLUME_DOWN | RT_CARD_ON);
193 		DELAY(MAX_VOL * RT_VOLUME_DELAY);
194 		bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0,
195 				RT_VOLUME_STEADY | RT_CARD_ON);
196 		bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0, RT_CARD_OFF);
197 		bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0, RT_CARD_OFF);
198 	} else {
199 		val = sc->sc_vol - vol;
200 		if (val < 0) {
201 			val *= -1;
202 			bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0,
203 					RT_VOLUME_DOWN | RT_CARD_ON);
204 		} else {
205 			bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0,
206 					RT_VOLUME_UP | RT_CARD_ON);
207 		}
208 		DELAY(val * RT_VOLUME_DELAY);
209 		bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0,
210 				RT_VOLUME_STEADY | RT_CARD_ON);
211 	}
212 }
213 
214 void
rt_set_freq(struct rt_softc * sc,u_int32_t nfreq)215 rt_set_freq(struct rt_softc *sc, u_int32_t nfreq)
216 {
217 	u_int32_t reg;
218 
219 	if (nfreq > MAX_FM_FREQ)
220 		nfreq = MAX_FM_FREQ;
221 	if (nfreq < MIN_FM_FREQ)
222 		nfreq = MIN_FM_FREQ;
223 
224 	sc->sc_freq = nfreq;
225 
226 	reg = lm700x_encode_freq(nfreq, sc->sc_rf);
227 	reg |= sc->sc_stereo | sc->sc_rf | LM700X_DIVIDER_FM;
228 
229 	lm700x_hardware_write(&sc->lm, reg, RT_VOLUME_STEADY);
230 
231 	rt_set_mute(sc, sc->sc_vol);
232 }
233 
234 /*
235  * Return state of the card - tuned/not tuned, mono/stereo
236  */
237 u_int8_t
rt_state(bus_space_tag_t iot,bus_space_handle_t ioh)238 rt_state(bus_space_tag_t iot, bus_space_handle_t ioh)
239 {
240 	u_int8_t ret;
241 
242 	bus_space_write_1(iot, ioh, 0,
243 			RT_VOLUME_STEADY | RT_SIGNAL_METER | RT_CARD_ON);
244 	DELAY(RT_SIGNAL_METER_DELAY);
245 	ret = bus_space_read_1(iot, ioh, 0);
246 
247 	switch (ret) {
248 	case 0xFD:
249 		ret = RADIO_INFO_SIGNAL | RADIO_INFO_STEREO;
250 		break;
251 	case 0xFF:
252 		ret = 0;
253 		break;
254 	default:
255 		ret = RADIO_INFO_SIGNAL;
256 		break;
257 	}
258 
259 	return ret;
260 }
261 
262 /*
263  * Convert volume to hardware representation.
264  */
265 u_int8_t
rt_conv_vol(u_int8_t vol)266 rt_conv_vol(u_int8_t vol)
267 {
268 	if (vol < VOLUME_RATIO(1))
269 		return 0;
270 	else if (vol >= VOLUME_RATIO(1) && vol < VOLUME_RATIO(2))
271 		return 1;
272 	else if (vol >= VOLUME_RATIO(2) && vol < VOLUME_RATIO(3))
273 		return 2;
274 	else if (vol >= VOLUME_RATIO(3) && vol < VOLUME_RATIO(4))
275 		return 3;
276 	else
277 		return 4;
278 }
279 
280 /*
281  * Convert volume from hardware representation
282  */
283 u_int8_t
rt_unconv_vol(u_int8_t vol)284 rt_unconv_vol(u_int8_t vol)
285 {
286 	return VOLUME_RATIO(vol);
287 }
288 
289 void
sfi_lm700x_init(bus_space_tag_t iot,bus_space_handle_t ioh,bus_size_t off,u_int32_t data)290 sfi_lm700x_init(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
291 		u_int32_t data)
292 {
293 	bus_space_write_1(iot, ioh, off, data);
294 }
295 
296 void
rt_lm700x_init(bus_space_tag_t iot,bus_space_handle_t ioh,bus_size_t off,u_int32_t data)297 rt_lm700x_init(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
298 		u_int32_t data)
299 {
300 	/* Do nothing */
301 	return;
302 }
303 
304 void
rt_lm700x_rset(bus_space_tag_t iot,bus_space_handle_t ioh,bus_size_t off,u_int32_t data)305 rt_lm700x_rset(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
306 		u_int32_t data)
307 {
308 	DELAY(1000);
309 	bus_space_write_1(iot, ioh, off, RT_CARD_OFF | data);
310 	DELAY(50000);
311 	bus_space_write_1(iot, ioh, off, RT_VOLUME_STEADY | RT_CARD_ON | data);
312 }
313