1/* $NetBSD: gtp.c,v 1.20 2016/07/11 11:31:51 msaitoh Exp $ */
2/*	$OpenBSD: gtp.c,v 1.1 2002/06/03 16:13:21 mickey Exp $	*/
3
4/*
5 * Copyright (c) 2002 Vladimir Popov <jumbo@narod.ru>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/* Gemtek PCI Radio Card Device Driver */
30
31#include <sys/cdefs.h>
32__KERNEL_RCSID(0, "$NetBSD: gtp.c,v 1.20 2016/07/11 11:31:51 msaitoh Exp $");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/device.h>
37#include <sys/errno.h>
38#include <sys/ioctl.h>
39#include <sys/proc.h>
40#include <sys/radioio.h>
41
42#include <sys/bus.h>
43
44#include <dev/pci/pcireg.h>
45#include <dev/pci/pcivar.h>
46#include <dev/pci/pcidevs.h>
47
48#include <dev/ic/tea5757.h>
49#include <dev/radio_if.h>
50
51#define PCI_CBIO 0x10
52
53static int	gtp_match(device_t, cfdata_t, void *);
54static void	gtp_attach(device_t, device_t, void *);
55
56static int	gtp_get_info(void *, struct radio_info *);
57static int	gtp_set_info(void *, struct radio_info *);
58static int	gtp_search(void *, int);
59
60#define GEMTEK_PCI_CAPS	RADIO_CAPS_DETECT_SIGNAL |			\
61			RADIO_CAPS_DETECT_STEREO |			\
62			RADIO_CAPS_SET_MONO |				\
63			RADIO_CAPS_HW_SEARCH |				\
64			RADIO_CAPS_HW_AFC |				\
65			RADIO_CAPS_LOCK_SENSITIVITY
66
67#define GEMTEK_PCI_MUTE		0x00
68#define GEMTEK_PCI_RSET		0x10
69
70#define GEMTEK_PCI_SIGNAL	0x08
71#define GEMTEK_PCI_STEREO	0x08
72
73#define GTP_WREN_ON		(1 << 2)
74#define GTP_WREN_OFF		(0 << 2)
75
76#define GTP_DATA_ON		(1 << 1)
77#define GTP_DATA_OFF		(0 << 1)
78
79#define GTP_CLCK_ON		(1 << 0)
80#define GTP_CLCK_OFF		(0 << 0)
81
82#define GTP_READ_CLOCK_LOW	(GTP_WREN_OFF | GTP_DATA_ON | GTP_CLCK_OFF)
83#define GTP_READ_CLOCK_HIGH	(GTP_WREN_OFF | GTP_DATA_ON | GTP_CLCK_ON)
84
85/* define our interface to the high-level radio driver */
86
87static const struct radio_hw_if gtp_hw_if = {
88	NULL, /* open */
89	NULL, /* close */
90	gtp_get_info,
91	gtp_set_info,
92	gtp_search
93};
94
95struct gtp_softc {
96	int	mute;
97	u_int8_t	vol;
98	u_int32_t	freq;
99	u_int32_t	stereo;
100	u_int32_t	lock;
101
102	struct tea5757_t	tea;
103};
104
105CFATTACH_DECL_NEW(gtp, sizeof(struct gtp_softc),
106    gtp_match, gtp_attach, NULL, NULL);
107
108static void	gtp_set_mute(struct gtp_softc *);
109static void	gtp_write_bit(bus_space_tag_t, bus_space_handle_t, bus_size_t,
110			      int);
111static void	gtp_init(bus_space_tag_t, bus_space_handle_t, bus_size_t,
112			 u_int32_t);
113static void	gtp_rset(bus_space_tag_t, bus_space_handle_t, bus_size_t,
114			 u_int32_t);
115static int	gtp_state(bus_space_tag_t, bus_space_handle_t);
116static uint32_t	gtp_hardware_read(bus_space_tag_t, bus_space_handle_t,
117				  bus_size_t);
118
119static int
120gtp_match(device_t parent, cfdata_t cf, void *aux)
121{
122	struct pci_attach_args *pa = aux;
123	/* FIXME:
124	 * Guillemot produces the card that
125	 * was originally developed by Gemtek
126	 */
127#if 0
128	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_GEMTEK &&
129	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_GEMTEK_PR103)
130		return (1);
131#else
132	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_GUILLEMOT &&
133	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_GUILLEMOT_MAXIRADIO)
134		return (1);
135#endif
136	return (0);
137}
138
139static void
140gtp_attach(device_t parent, device_t self, void *aux)
141{
142	struct gtp_softc *sc = device_private(self);
143	struct pci_attach_args *pa = aux;
144	cfdata_t cf = device_cfdata(self);
145	pci_chipset_tag_t pc = pa->pa_pc;
146	bus_size_t iosize;
147	pcireg_t csr;
148
149	pci_aprint_devinfo(pa, "Radio controller");
150
151	/* Map I/O registers */
152	if (pci_mapreg_map(pa, PCI_CBIO, PCI_MAPREG_TYPE_IO, 0, &sc->tea.iot,
153	    &sc->tea.ioh, NULL, &iosize)) {
154		aprint_error(": can't map i/o space\n");
155		return;
156	}
157
158	/* Enable the card */
159	csr = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
160	pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
161	    csr | PCI_COMMAND_MASTER_ENABLE);
162
163	sc->vol = 0;
164	sc->mute = 0;
165	sc->freq = MIN_FM_FREQ;
166	sc->stereo = TEA5757_STEREO;
167	sc->lock = TEA5757_S030;
168	sc->tea.offset = 0;
169	sc->tea.flags = cf->cf_flags;
170	sc->tea.init = gtp_init;
171	sc->tea.rset = gtp_rset;
172	sc->tea.write_bit = gtp_write_bit;
173	sc->tea.read = gtp_hardware_read;
174
175	aprint_normal(": Gemtek PR103\n");
176
177	radio_attach_mi(&gtp_hw_if, sc, self);
178}
179
180static int
181gtp_get_info(void *v, struct radio_info *ri)
182{
183	struct gtp_softc *sc = v;
184
185	ri->mute = sc->mute;
186	ri->volume = sc->vol ? 255 : 0;
187	ri->stereo = sc->stereo == TEA5757_STEREO ? 1 : 0;
188	ri->caps = GEMTEK_PCI_CAPS;
189	ri->rfreq = 0;
190	ri->lock = tea5757_decode_lock(sc->lock);
191
192	/* Frequency read unsupported */
193	ri->freq = sc->freq;
194
195	ri->info = gtp_state(sc->tea.iot, sc->tea.ioh);
196	gtp_set_mute(sc);
197
198	return (0);
199}
200
201static int
202gtp_set_info(void *v, struct radio_info *ri)
203{
204	struct gtp_softc *sc = v;
205
206	sc->mute = ri->mute ? 1 : 0;
207	sc->vol = ri->volume ? 255 : 0;
208	sc->stereo = ri->stereo ? TEA5757_STEREO: TEA5757_MONO;
209	sc->lock = tea5757_encode_lock(ri->lock);
210	ri->freq = sc->freq = tea5757_set_freq(&sc->tea,
211	    sc->lock, sc->stereo, ri->freq);
212	gtp_set_mute(sc);
213
214	return (0);
215}
216
217static int
218gtp_search(void *v, int f)
219{
220	struct gtp_softc *sc = v;
221
222	tea5757_search(&sc->tea, sc->lock, sc->stereo, f);
223	gtp_set_mute(sc);
224
225	return (0);
226}
227
228static void
229gtp_set_mute(struct gtp_softc *sc)
230{
231	if (sc->mute || !sc->vol)
232		bus_space_write_2(sc->tea.iot, sc->tea.ioh, 0, GEMTEK_PCI_MUTE);
233	else
234		sc->freq = tea5757_set_freq(&sc->tea,
235		    sc->lock, sc->stereo, sc->freq);
236}
237
238static void
239gtp_write_bit(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
240		int bit)
241{
242	u_int8_t data;
243
244	data = bit ? GTP_DATA_ON : GTP_DATA_OFF;
245	bus_space_write_1(iot, ioh, off, GTP_WREN_ON | GTP_CLCK_OFF | data);
246	bus_space_write_1(iot, ioh, off, GTP_WREN_ON | GTP_CLCK_ON  | data);
247	bus_space_write_1(iot, ioh, off, GTP_WREN_ON | GTP_CLCK_OFF | data);
248}
249
250static void
251gtp_init(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
252    u_int32_t d)
253{
254	bus_space_write_1(iot, ioh, off, GTP_WREN_ON | GTP_DATA_ON | GTP_CLCK_OFF);
255}
256
257static void
258gtp_rset(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
259    u_int32_t d)
260{
261	bus_space_write_1(iot, ioh, off, GEMTEK_PCI_RSET);
262}
263
264static uint32_t
265gtp_hardware_read(bus_space_tag_t iot, bus_space_handle_t ioh,
266    bus_size_t off)
267{
268	/* UNSUPPORTED */
269	return 0;
270}
271
272static int
273gtp_state(bus_space_tag_t iot, bus_space_handle_t ioh)
274{
275	int ret;
276
277	bus_space_write_2(iot, ioh, 0,
278	    GTP_DATA_ON | GTP_WREN_OFF | GTP_CLCK_OFF);
279	ret  = bus_space_read_2(iot, ioh, 0) &
280	    GEMTEK_PCI_STEREO?  0 : RADIO_INFO_STEREO;
281	bus_space_write_2(iot, ioh, 0,
282	    GTP_DATA_ON | GTP_WREN_OFF | GTP_CLCK_ON);
283	ret |= bus_space_read_2(iot, ioh, 0) &
284	    GEMTEK_PCI_SIGNAL?  0 : RADIO_INFO_SIGNAL;
285
286	return ret;
287}
288