Annotation of sys/dev/isa/aztech.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: aztech.c,v 1.4 2002/01/23 20:30:38 mickey Exp $ */
2: /* $RuOBSD: aztech.c,v 1.11 2001/10/20 13:23:47 pva Exp $ */
3:
4: /*
5: * Copyright (c) 2001 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: /* Aztech/PackardBell FM Radio Card device driver */
31:
32: /*
33: * Sanyo LM7001J Direct PLL Frequency Synthesizer:
34: * ??? See http://www.redsword.com/tjacobs/geeb/fmcard.htm
35: *
36: * Philips TEA5712T AM/FM Stereo DTS Radio:
37: * http://www.semiconductors.philips.com/pip/TEA5712
38: */
39:
40: #include <sys/param.h>
41: #include <sys/systm.h>
42: #include <sys/proc.h>
43: #include <sys/errno.h>
44: #include <sys/ioctl.h>
45: #include <sys/device.h>
46: #include <sys/radioio.h>
47:
48: #include <machine/bus.h>
49:
50: #include <dev/isa/isavar.h>
51: #include <dev/ic/lm700x.h>
52: #include <dev/radio_if.h>
53:
54: #define RF_25K 25
55: #define RF_50K 50
56: #define RF_100K 100
57:
58: #define MAX_VOL 3
59: #define VOLUME_RATIO(x) (255 * x / MAX_VOL)
60:
61: #define AZ_BASE_VALID(x) ((x == 0x350) || (x == 0x358))
62: #define AZTECH_CAPABILITIES RADIO_CAPS_DETECT_STEREO | \
63: RADIO_CAPS_DETECT_SIGNAL | \
64: RADIO_CAPS_SET_MONO | \
65: RADIO_CAPS_REFERENCE_FREQ
66:
67:
68: #define AZTECH_STEREO (1 << 0)
69: #define AZTECH_SIGNAL (1 << 1)
70:
71: #define AZ_WREN_ON (1 << 1)
72: #define AZ_WREN_OFF (0 << 1)
73:
74: #define AZ_CLCK_ON (1 << 6)
75: #define AZ_CLCK_OFF (0 << 6)
76:
77: #define AZ_DATA_ON (1 << 7)
78: #define AZ_DATA_OFF (0 << 7)
79:
80: int az_probe(struct device *, void *, void *);
81: void az_attach(struct device *, struct device * self, void *);
82:
83: int az_get_info(void *, struct radio_info *);
84: int az_set_info(void *, struct radio_info *);
85:
86: struct radio_hw_if az_hw_if = {
87: NULL, /* open */
88: NULL, /* close */
89: az_get_info,
90: az_set_info,
91: NULL
92: };
93:
94: struct az_softc {
95: struct device sc_dev;
96:
97: int mute;
98: u_int8_t vol;
99: u_int32_t freq;
100: u_int32_t rf;
101: u_int32_t stereo;
102:
103: struct lm700x_t lm;
104: };
105:
106: struct cfattach az_ca = {
107: sizeof(struct az_softc), az_probe, az_attach
108: };
109:
110: struct cfdriver az_cd = {
111: NULL, "az", DV_DULL
112: };
113:
114: u_int az_find(bus_space_tag_t, bus_space_handle_t);
115: void az_set_mute(struct az_softc *);
116: void az_set_freq(struct az_softc *, u_int32_t);
117: u_int8_t az_state(bus_space_tag_t, bus_space_handle_t);
118:
119: void az_lm700x_init(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t);
120: void az_lm700x_rset(bus_space_tag_t, bus_space_handle_t, bus_size_t, u_int32_t);
121:
122: u_int8_t az_conv_vol(u_int8_t);
123: u_int8_t az_unconv_vol(u_int8_t);
124:
125: int
126: az_probe(struct device *parent, void *self, void *aux)
127: {
128: struct isa_attach_args *ia = aux;
129: bus_space_tag_t iot = ia->ia_iot;
130: bus_space_handle_t ioh;
131:
132: int iosize = 1, iobase = ia->ia_iobase;
133:
134: if (!AZ_BASE_VALID(iobase)) {
135: printf("az: configured iobase 0x%x invalid\n", iobase);
136: return (0);
137: }
138:
139: if (bus_space_map(iot, iobase, iosize, 0, &ioh))
140: return (0);
141:
142: if (!az_find(iot, ioh)) {
143: bus_space_unmap(iot, ioh, iosize);
144: return (0);
145: }
146:
147: bus_space_unmap(iot, ioh, iosize);
148: ia->ia_iosize = iosize;
149: return (1);
150: }
151:
152: void
153: az_attach(struct device *parent, struct device *self, void *aux)
154: {
155: struct az_softc *sc = (void *) self;
156: struct isa_attach_args *ia = aux;
157:
158: sc->lm.iot = ia->ia_iot;
159: sc->rf = LM700X_REF_050;
160: sc->stereo = LM700X_STEREO;
161: sc->mute = 0;
162: sc->freq = MIN_FM_FREQ;
163: sc->vol = 0;
164:
165: /* remap I/O */
166: if (bus_space_map(sc->lm.iot, ia->ia_iobase, ia->ia_iosize,
167: 0, &sc->lm.ioh)) {
168: printf(": bus_space_map() failed\n");
169: return;
170: }
171:
172: printf(": Aztech/PackardBell\n");
173:
174: /* Configure struct lm700x_t lm */
175: sc->lm.offset = 0;
176: sc->lm.wzcl = AZ_WREN_ON | AZ_CLCK_OFF | AZ_DATA_OFF;
177: sc->lm.wzch = AZ_WREN_ON | AZ_CLCK_ON | AZ_DATA_OFF;
178: sc->lm.wocl = AZ_WREN_ON | AZ_CLCK_OFF | AZ_DATA_ON;
179: sc->lm.woch = AZ_WREN_ON | AZ_CLCK_ON | AZ_DATA_ON;
180: sc->lm.initdata = 0;
181: sc->lm.rsetdata = AZ_DATA_ON | AZ_CLCK_ON | AZ_WREN_OFF;
182: sc->lm.init = az_lm700x_init;
183: sc->lm.rset = az_lm700x_rset;
184:
185: az_set_freq(sc, sc->freq);
186:
187: radio_attach_mi(&az_hw_if, sc, &sc->sc_dev);
188: }
189:
190: /*
191: * Mute the card
192: */
193: void
194: az_set_mute(struct az_softc *sc)
195: {
196: bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0,
197: sc->mute ? 0 : sc->vol);
198: DELAY(6);
199: bus_space_write_1(sc->lm.iot, sc->lm.ioh, 0,
200: sc->mute ? 0 : sc->vol);
201: }
202:
203: void
204: az_set_freq(struct az_softc *sc, u_int32_t nfreq)
205: {
206: u_int8_t vol;
207: u_int32_t reg;
208:
209: vol = sc->mute ? 0 : sc->vol;
210:
211: if (nfreq > MAX_FM_FREQ)
212: nfreq = MAX_FM_FREQ;
213: if (nfreq < MIN_FM_FREQ)
214: nfreq = MIN_FM_FREQ;
215:
216: sc->freq = nfreq;
217:
218: reg = lm700x_encode_freq(nfreq, sc->rf);
219: reg |= sc->stereo | sc->rf | LM700X_DIVIDER_FM;
220:
221: lm700x_hardware_write(&sc->lm, reg, vol);
222:
223: az_set_mute(sc);
224: }
225:
226: /*
227: * Return state of the card - tuned/not tuned, mono/stereo
228: */
229: u_int8_t
230: az_state(bus_space_tag_t iot, bus_space_handle_t ioh)
231: {
232: u_int8_t info = 0, portdata;
233:
234: portdata = bus_space_read_1(iot, ioh, 0);
235:
236: info |= portdata & AZTECH_STEREO ? 0 : RADIO_INFO_STEREO;
237: info |= portdata & AZTECH_SIGNAL ? 0 : RADIO_INFO_SIGNAL;
238:
239: return info;
240: }
241:
242: /*
243: * Convert volume to hardware representation.
244: * The card uses bits 00000x0x to set volume.
245: */
246: u_int8_t
247: az_conv_vol(u_int8_t vol)
248: {
249: if (vol < VOLUME_RATIO(1))
250: return 0;
251: else if (vol >= VOLUME_RATIO(1) && vol < VOLUME_RATIO(2))
252: return 1;
253: else if (vol >= VOLUME_RATIO(2) && vol < VOLUME_RATIO(3))
254: return 4;
255: else
256: return 5;
257: }
258:
259: /*
260: * Convert volume from hardware representation
261: */
262: u_int8_t
263: az_unconv_vol(u_int8_t vol)
264: {
265: switch (vol) {
266: case 0:
267: return VOLUME_RATIO(0);
268: case 1:
269: return VOLUME_RATIO(1);
270: case 4:
271: return VOLUME_RATIO(2);
272: }
273: return VOLUME_RATIO(3);
274: }
275:
276: u_int
277: az_find(bus_space_tag_t iot, bus_space_handle_t ioh)
278: {
279: struct az_softc sc;
280: u_int i;
281:
282: sc.lm.iot = iot;
283: sc.lm.ioh = ioh;
284: sc.lm.offset = 0;
285: sc.lm.wzcl = AZ_WREN_ON | AZ_CLCK_OFF | AZ_DATA_OFF;
286: sc.lm.wzch = AZ_WREN_ON | AZ_CLCK_ON | AZ_DATA_OFF;
287: sc.lm.wocl = AZ_WREN_ON | AZ_CLCK_OFF | AZ_DATA_ON;
288: sc.lm.woch = AZ_WREN_ON | AZ_CLCK_ON | AZ_DATA_ON;
289: sc.lm.initdata = 0;
290: sc.lm.rsetdata = AZ_DATA_ON | AZ_CLCK_ON | AZ_WREN_OFF;
291: sc.lm.init = az_lm700x_init;
292: sc.lm.rset = az_lm700x_rset;
293: sc.rf = LM700X_REF_050;
294: sc.mute = 0;
295: sc.stereo = LM700X_STEREO;
296: sc.vol = 0;
297:
298: /*
299: * Scan whole FM range. If there is a card it'll
300: * respond on some frequency.
301: */
302: for (i = MIN_FM_FREQ; i < MAX_FM_FREQ; i += 10) {
303: az_set_freq(&sc, i);
304: if (az_state(iot, ioh))
305: return 1;
306: }
307:
308: return 0;
309: }
310:
311: void
312: az_lm700x_init(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
313: u_int32_t data)
314: {
315: /* Do nothing */
316: return;
317: }
318:
319: void
320: az_lm700x_rset(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
321: u_int32_t data)
322: {
323: bus_space_write_1(iot, ioh, off, data);
324: }
325:
326: int
327: az_get_info(void *v, struct radio_info *ri)
328: {
329: struct az_softc *sc = v;
330:
331: ri->mute = sc->mute;
332: ri->volume = az_unconv_vol(sc->vol);
333: ri->stereo = sc->stereo == LM700X_STEREO ? 1 : 0;
334: ri->caps = AZTECH_CAPABILITIES;
335: ri->rfreq = lm700x_decode_ref(sc->rf);
336: ri->info = az_state(sc->lm.iot, sc->lm.ioh);
337: ri->freq = sc->freq;
338:
339: /* UNSUPPORTED */
340: ri->lock = 0;
341:
342: return (0);
343: }
344:
345: int
346: az_set_info(void *v, struct radio_info *ri)
347: {
348: struct az_softc *sc = v;
349:
350: sc->mute = ri->mute ? 1 : 0;
351: sc->vol = az_conv_vol(ri->volume);
352: sc->stereo = ri->stereo ? LM700X_STEREO : LM700X_MONO;
353: sc->rf = lm700x_encode_ref(ri->rfreq);
354:
355: az_set_freq(sc, ri->freq);
356: az_set_mute(sc);
357:
358: return (0);
359: }
CVSweb