diff options
author | Hans Verkuil <hans.verkuil@cisco.com> | 2012-01-16 03:00:26 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-02-14 14:09:39 -0500 |
commit | f8c085244ef6a88c38db3ee49bda0ff4249edf34 (patch) | |
tree | 3d8dd9c3c7f69e9ecb6b05386ea75eb6504e1909 /drivers/media/radio | |
parent | 3088fba877ee8bf284b12a73332b813b5478a64d (diff) |
[media] radio-gemtek: Convert to radio-isa
Tested with actual hardware (up to two cards) and the Keene USB FM Transmitter.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio')
-rw-r--r-- | drivers/media/radio/Kconfig | 17 | ||||
-rw-r--r-- | drivers/media/radio/radio-gemtek.c | 493 |
2 files changed, 106 insertions, 404 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 645fe5716170..06a8c7a53556 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig | |||
@@ -276,6 +276,7 @@ config RADIO_AZTECH_PORT | |||
276 | config RADIO_GEMTEK | 276 | config RADIO_GEMTEK |
277 | tristate "GemTek Radio card (or compatible) support" | 277 | tristate "GemTek Radio card (or compatible) support" |
278 | depends on ISA && VIDEO_V4L2 | 278 | depends on ISA && VIDEO_V4L2 |
279 | select RADIO_ISA | ||
279 | ---help--- | 280 | ---help--- |
280 | Choose Y here if you have this FM radio card, and then fill in the | 281 | Choose Y here if you have this FM radio card, and then fill in the |
281 | I/O port address and settings below. The following cards either have | 282 | I/O port address and settings below. The following cards either have |
@@ -285,23 +286,21 @@ config RADIO_GEMTEK | |||
285 | - Typhoon Radio card (some models) | 286 | - Typhoon Radio card (some models) |
286 | - Hama Radio card | 287 | - Hama Radio card |
287 | 288 | ||
288 | In order to control your radio card, you will need to use programs | ||
289 | that are compatible with the Video For Linux API. Information on | ||
290 | this API and pointers to "v4l" programs may be found at | ||
291 | <file:Documentation/video4linux/API.html>. | ||
292 | |||
293 | To compile this driver as a module, choose M here: the | 289 | To compile this driver as a module, choose M here: the |
294 | module will be called radio-gemtek. | 290 | module will be called radio-gemtek. |
295 | 291 | ||
296 | config RADIO_GEMTEK_PORT | 292 | config RADIO_GEMTEK_PORT |
297 | hex "Fixed I/O port (0x20c, 0x30c, 0x24c, 0x34c, 0c24c or 0x28c)" | 293 | hex "Fixed I/O port (0x20c, 0x30c, 0x24c, 0x34c, 0x248 or 0x28c)" |
298 | depends on RADIO_GEMTEK=y | 294 | depends on RADIO_GEMTEK=y |
299 | default "34c" | 295 | default "34c" |
300 | help | 296 | help |
301 | Enter either 0x20c, 0x30c, 0x24c or 0x34c here. The card default is | 297 | Enter either 0x20c, 0x30c, 0x24c, 0x34c, 0x248 or 0x28c here. The |
302 | 0x34c, if you haven't changed the jumper setting on the card. On | 298 | card default is 0x34c, if you haven't changed the jumper setting |
303 | Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O | 299 | on the card. |
300 | |||
301 | On Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O | ||
304 | port is 0x20c, 0x248 or 0x28c. | 302 | port is 0x20c, 0x248 or 0x28c. |
303 | |||
305 | If automatic I/O port probing is enabled this port will be used only | 304 | If automatic I/O port probing is enabled this port will be used only |
306 | in case of automatic probing failure, ie. as a fallback. | 305 | in case of automatic probing failure, ie. as a fallback. |
307 | 306 | ||
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 36ce0611c037..9d7fdaea46be 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c | |||
@@ -1,4 +1,7 @@ | |||
1 | /* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi> | 1 | /* |
2 | * GemTek radio card driver | ||
3 | * | ||
4 | * Copyright 1998 Jonas Munsin <jmunsin@iki.fi> | ||
2 | * | 5 | * |
3 | * GemTek hasn't released any specs on the card, so the protocol had to | 6 | * GemTek hasn't released any specs on the card, so the protocol had to |
4 | * be reverse engineered with dosemu. | 7 | * be reverse engineered with dosemu. |
@@ -11,9 +14,12 @@ | |||
11 | * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> | 14 | * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> |
12 | * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> | 15 | * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> |
13 | * | 16 | * |
14 | * TODO: Allow for more than one of these foolish entities :-) | 17 | * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> |
15 | * | ||
16 | * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> | 18 | * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> |
19 | * | ||
20 | * Note: this card seems to swap the left and right audio channels! | ||
21 | * | ||
22 | * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. | ||
17 | */ | 23 | */ |
18 | 24 | ||
19 | #include <linux/module.h> /* Modules */ | 25 | #include <linux/module.h> /* Modules */ |
@@ -25,6 +31,7 @@ | |||
25 | #include <linux/io.h> /* outb, outb_p */ | 31 | #include <linux/io.h> /* outb, outb_p */ |
26 | #include <media/v4l2-ioctl.h> | 32 | #include <media/v4l2-ioctl.h> |
27 | #include <media/v4l2-device.h> | 33 | #include <media/v4l2-device.h> |
34 | #include "radio-isa.h" | ||
28 | 35 | ||
29 | /* | 36 | /* |
30 | * Module info. | 37 | * Module info. |
@@ -33,7 +40,7 @@ | |||
33 | MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>"); | 40 | MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>"); |
34 | MODULE_DESCRIPTION("A driver for the GemTek Radio card."); | 41 | MODULE_DESCRIPTION("A driver for the GemTek Radio card."); |
35 | MODULE_LICENSE("GPL"); | 42 | MODULE_LICENSE("GPL"); |
36 | MODULE_VERSION("0.0.4"); | 43 | MODULE_VERSION("1.0.0"); |
37 | 44 | ||
38 | /* | 45 | /* |
39 | * Module params. | 46 | * Module params. |
@@ -46,45 +53,29 @@ MODULE_VERSION("0.0.4"); | |||
46 | #define CONFIG_RADIO_GEMTEK_PROBE 1 | 53 | #define CONFIG_RADIO_GEMTEK_PROBE 1 |
47 | #endif | 54 | #endif |
48 | 55 | ||
49 | static int io = CONFIG_RADIO_GEMTEK_PORT; | 56 | #define GEMTEK_MAX 4 |
50 | static bool probe = CONFIG_RADIO_GEMTEK_PROBE; | ||
51 | static bool hardmute; | ||
52 | static bool shutdown = 1; | ||
53 | static bool keepmuted = 1; | ||
54 | static bool initmute = 1; | ||
55 | static int radio_nr = -1; | ||
56 | 57 | ||
57 | module_param(io, int, 0444); | 58 | static bool probe = CONFIG_RADIO_GEMTEK_PROBE; |
58 | MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic " | 59 | static bool hardmute; |
59 | "probing is disabled or fails. The most common I/O ports are: 0x20c " | 60 | static int io[GEMTEK_MAX] = { [0] = CONFIG_RADIO_GEMTEK_PORT, |
60 | "0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to " | 61 | [1 ... (GEMTEK_MAX - 1)] = -1 }; |
61 | "work for the combined sound/radiocard)."); | 62 | static int radio_nr[GEMTEK_MAX] = { [0 ... (GEMTEK_MAX - 1)] = -1 }; |
62 | 63 | ||
63 | module_param(probe, bool, 0444); | 64 | module_param(probe, bool, 0444); |
64 | MODULE_PARM_DESC(probe, "Enable automatic device probing. Note: only the most " | 65 | MODULE_PARM_DESC(probe, "Enable automatic device probing."); |
65 | "common I/O ports used by the card are probed."); | ||
66 | 66 | ||
67 | module_param(hardmute, bool, 0644); | 67 | module_param(hardmute, bool, 0644); |
68 | MODULE_PARM_DESC(hardmute, "Enable `hard muting' by shutting down PLL, may " | 68 | MODULE_PARM_DESC(hardmute, "Enable 'hard muting' by shutting down PLL, may " |
69 | "reduce static noise."); | 69 | "reduce static noise."); |
70 | 70 | ||
71 | module_param(shutdown, bool, 0644); | 71 | module_param_array(io, int, NULL, 0444); |
72 | MODULE_PARM_DESC(shutdown, "Enable shutting down PLL and muting line when " | 72 | MODULE_PARM_DESC(io, "Force I/O ports for the GemTek Radio card if automatic " |
73 | "module is unloaded."); | 73 | "probing is disabled or fails. The most common I/O ports are: 0x20c " |
74 | 74 | "0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to " | |
75 | module_param(keepmuted, bool, 0644); | 75 | "work for the combined sound/radiocard)."); |
76 | MODULE_PARM_DESC(keepmuted, "Keep card muted even when frequency is changed."); | ||
77 | |||
78 | module_param(initmute, bool, 0444); | ||
79 | MODULE_PARM_DESC(initmute, "Mute card when module is loaded."); | ||
80 | |||
81 | module_param(radio_nr, int, 0444); | ||
82 | 76 | ||
83 | /* | 77 | module_param_array(radio_nr, int, NULL, 0444); |
84 | * Functions for controlling the card. | 78 | MODULE_PARM_DESC(radio_nr, "Radio device numbers"); |
85 | */ | ||
86 | #define GEMTEK_LOWFREQ (87*16000) | ||
87 | #define GEMTEK_HIGHFREQ (108*16000) | ||
88 | 79 | ||
89 | /* | 80 | /* |
90 | * Frequency calculation constants. Intermediate frequency 10.52 MHz (nominal | 81 | * Frequency calculation constants. Intermediate frequency 10.52 MHz (nominal |
@@ -108,18 +99,11 @@ module_param(radio_nr, int, 0444); | |||
108 | #define LONG_DELAY 75 /* usec */ | 99 | #define LONG_DELAY 75 /* usec */ |
109 | 100 | ||
110 | struct gemtek { | 101 | struct gemtek { |
111 | struct v4l2_device v4l2_dev; | 102 | struct radio_isa_card isa; |
112 | struct video_device vdev; | 103 | bool muted; |
113 | struct mutex lock; | ||
114 | unsigned long lastfreq; | ||
115 | int muted; | ||
116 | int verified; | ||
117 | int io; | ||
118 | u32 bu2614data; | 104 | u32 bu2614data; |
119 | }; | 105 | }; |
120 | 106 | ||
121 | static struct gemtek gemtek_card; | ||
122 | |||
123 | #define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */ | 107 | #define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */ |
124 | #define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */ | 108 | #define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */ |
125 | #define BU2614_VOID_BITS 4 /* unused */ | 109 | #define BU2614_VOID_BITS 4 /* unused */ |
@@ -166,31 +150,24 @@ static struct gemtek gemtek_card; | |||
166 | */ | 150 | */ |
167 | static void gemtek_bu2614_transmit(struct gemtek *gt) | 151 | static void gemtek_bu2614_transmit(struct gemtek *gt) |
168 | { | 152 | { |
153 | struct radio_isa_card *isa = >->isa; | ||
169 | int i, bit, q, mute; | 154 | int i, bit, q, mute; |
170 | 155 | ||
171 | mutex_lock(>->lock); | ||
172 | |||
173 | mute = gt->muted ? GEMTEK_MT : 0x00; | 156 | mute = gt->muted ? GEMTEK_MT : 0x00; |
174 | 157 | ||
175 | outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io); | 158 | outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, isa->io); |
176 | udelay(SHORT_DELAY); | ||
177 | outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io); | ||
178 | udelay(LONG_DELAY); | 159 | udelay(LONG_DELAY); |
179 | 160 | ||
180 | for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) { | 161 | for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) { |
181 | bit = (q & 1) ? GEMTEK_DA : 0; | 162 | bit = (q & 1) ? GEMTEK_DA : 0; |
182 | outb_p(mute | GEMTEK_CE | bit, gt->io); | 163 | outb_p(mute | GEMTEK_CE | bit, isa->io); |
183 | udelay(SHORT_DELAY); | 164 | udelay(SHORT_DELAY); |
184 | outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, gt->io); | 165 | outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, isa->io); |
185 | udelay(SHORT_DELAY); | 166 | udelay(SHORT_DELAY); |
186 | } | 167 | } |
187 | 168 | ||
188 | outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io); | 169 | outb_p(mute | GEMTEK_DA | GEMTEK_CK, isa->io); |
189 | udelay(SHORT_DELAY); | 170 | udelay(SHORT_DELAY); |
190 | outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io); | ||
191 | udelay(LONG_DELAY); | ||
192 | |||
193 | mutex_unlock(>->lock); | ||
194 | } | 171 | } |
195 | 172 | ||
196 | /* | 173 | /* |
@@ -198,21 +175,27 @@ static void gemtek_bu2614_transmit(struct gemtek *gt) | |||
198 | */ | 175 | */ |
199 | static unsigned long gemtek_convfreq(unsigned long freq) | 176 | static unsigned long gemtek_convfreq(unsigned long freq) |
200 | { | 177 | { |
201 | return ((freq<<FSCALE) + IF_OFFSET + REF_FREQ/2) / REF_FREQ; | 178 | return ((freq << FSCALE) + IF_OFFSET + REF_FREQ / 2) / REF_FREQ; |
179 | } | ||
180 | |||
181 | static struct radio_isa_card *gemtek_alloc(void) | ||
182 | { | ||
183 | struct gemtek *gt = kzalloc(sizeof(*gt), GFP_KERNEL); | ||
184 | |||
185 | if (gt) | ||
186 | gt->muted = true; | ||
187 | return gt ? >->isa : NULL; | ||
202 | } | 188 | } |
203 | 189 | ||
204 | /* | 190 | /* |
205 | * Set FM-frequency. | 191 | * Set FM-frequency. |
206 | */ | 192 | */ |
207 | static void gemtek_setfreq(struct gemtek *gt, unsigned long freq) | 193 | static int gemtek_s_frequency(struct radio_isa_card *isa, u32 freq) |
208 | { | 194 | { |
209 | if (keepmuted && hardmute && gt->muted) | 195 | struct gemtek *gt = container_of(isa, struct gemtek, isa); |
210 | return; | ||
211 | 196 | ||
212 | freq = clamp_val(freq, GEMTEK_LOWFREQ, GEMTEK_HIGHFREQ); | 197 | if (hardmute && gt->muted) |
213 | 198 | return 0; | |
214 | gt->lastfreq = freq; | ||
215 | gt->muted = 0; | ||
216 | 199 | ||
217 | gemtek_bu2614_set(gt, BU2614_PORT, 0); | 200 | gemtek_bu2614_set(gt, BU2614_PORT, 0); |
218 | gemtek_bu2614_set(gt, BU2614_FMES, 0); | 201 | gemtek_bu2614_set(gt, BU2614_FMES, 0); |
@@ -220,23 +203,25 @@ static void gemtek_setfreq(struct gemtek *gt, unsigned long freq) | |||
220 | gemtek_bu2614_set(gt, BU2614_SWAL, 0); | 203 | gemtek_bu2614_set(gt, BU2614_SWAL, 0); |
221 | gemtek_bu2614_set(gt, BU2614_FMUN, 1); /* GT bit set */ | 204 | gemtek_bu2614_set(gt, BU2614_FMUN, 1); /* GT bit set */ |
222 | gemtek_bu2614_set(gt, BU2614_TEST, 0); | 205 | gemtek_bu2614_set(gt, BU2614_TEST, 0); |
223 | |||
224 | gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ); | 206 | gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ); |
225 | gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq)); | 207 | gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq)); |
226 | |||
227 | gemtek_bu2614_transmit(gt); | 208 | gemtek_bu2614_transmit(gt); |
209 | return 0; | ||
228 | } | 210 | } |
229 | 211 | ||
230 | /* | 212 | /* |
231 | * Set mute flag. | 213 | * Set mute flag. |
232 | */ | 214 | */ |
233 | static void gemtek_mute(struct gemtek *gt) | 215 | static int gemtek_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) |
234 | { | 216 | { |
217 | struct gemtek *gt = container_of(isa, struct gemtek, isa); | ||
235 | int i; | 218 | int i; |
236 | 219 | ||
237 | gt->muted = 1; | 220 | gt->muted = mute; |
238 | |||
239 | if (hardmute) { | 221 | if (hardmute) { |
222 | if (!mute) | ||
223 | return gemtek_s_frequency(isa, isa->freq); | ||
224 | |||
240 | /* Turn off PLL, disable data output */ | 225 | /* Turn off PLL, disable data output */ |
241 | gemtek_bu2614_set(gt, BU2614_PORT, 0); | 226 | gemtek_bu2614_set(gt, BU2614_PORT, 0); |
242 | gemtek_bu2614_set(gt, BU2614_FMES, 0); /* CT bit off */ | 227 | gemtek_bu2614_set(gt, BU2614_FMES, 0); /* CT bit off */ |
@@ -247,367 +232,85 @@ static void gemtek_mute(struct gemtek *gt) | |||
247 | gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF); | 232 | gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF); |
248 | gemtek_bu2614_set(gt, BU2614_FREQ, 0); | 233 | gemtek_bu2614_set(gt, BU2614_FREQ, 0); |
249 | gemtek_bu2614_transmit(gt); | 234 | gemtek_bu2614_transmit(gt); |
250 | return; | 235 | return 0; |
251 | } | 236 | } |
252 | 237 | ||
253 | mutex_lock(>->lock); | ||
254 | |||
255 | /* Read bus contents (CE, CK and DA). */ | 238 | /* Read bus contents (CE, CK and DA). */ |
256 | i = inb_p(gt->io); | 239 | i = inb_p(isa->io); |
257 | /* Write it back with mute flag set. */ | 240 | /* Write it back with mute flag set. */ |
258 | outb_p((i >> 5) | GEMTEK_MT, gt->io); | 241 | outb_p((i >> 5) | (mute ? GEMTEK_MT : 0), isa->io); |
259 | udelay(SHORT_DELAY); | 242 | udelay(SHORT_DELAY); |
260 | 243 | return 0; | |
261 | mutex_unlock(>->lock); | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * Unset mute flag. | ||
266 | */ | ||
267 | static void gemtek_unmute(struct gemtek *gt) | ||
268 | { | ||
269 | int i; | ||
270 | |||
271 | gt->muted = 0; | ||
272 | if (hardmute) { | ||
273 | /* Turn PLL back on. */ | ||
274 | gemtek_setfreq(gt, gt->lastfreq); | ||
275 | return; | ||
276 | } | ||
277 | mutex_lock(>->lock); | ||
278 | |||
279 | i = inb_p(gt->io); | ||
280 | outb_p(i >> 5, gt->io); | ||
281 | udelay(SHORT_DELAY); | ||
282 | |||
283 | mutex_unlock(>->lock); | ||
284 | } | 244 | } |
285 | 245 | ||
286 | /* | 246 | static u32 gemtek_g_rxsubchans(struct radio_isa_card *isa) |
287 | * Get signal strength (= stereo status). | ||
288 | */ | ||
289 | static inline int gemtek_getsigstr(struct gemtek *gt) | ||
290 | { | 247 | { |
291 | int sig; | 248 | if (inb_p(isa->io) & GEMTEK_NS) |
292 | 249 | return V4L2_TUNER_SUB_MONO; | |
293 | mutex_lock(>->lock); | 250 | return V4L2_TUNER_SUB_STEREO; |
294 | sig = inb_p(gt->io) & GEMTEK_NS ? 0 : 1; | ||
295 | mutex_unlock(>->lock); | ||
296 | return sig; | ||
297 | } | 251 | } |
298 | 252 | ||
299 | /* | 253 | /* |
300 | * Check if requested card acts like GemTek Radio card. | 254 | * Check if requested card acts like GemTek Radio card. |
301 | */ | 255 | */ |
302 | static int gemtek_verify(struct gemtek *gt, int port) | 256 | static bool gemtek_probe(struct radio_isa_card *isa, int io) |
303 | { | 257 | { |
304 | int i, q; | 258 | int i, q; |
305 | 259 | ||
306 | if (gt->verified == port) | 260 | q = inb_p(io); /* Read bus contents before probing. */ |
307 | return 1; | ||
308 | |||
309 | mutex_lock(>->lock); | ||
310 | |||
311 | q = inb_p(port); /* Read bus contents before probing. */ | ||
312 | /* Try to turn on CE, CK and DA respectively and check if card responds | 261 | /* Try to turn on CE, CK and DA respectively and check if card responds |
313 | properly. */ | 262 | properly. */ |
314 | for (i = 0; i < 3; ++i) { | 263 | for (i = 0; i < 3; ++i) { |
315 | outb_p(1 << i, port); | 264 | outb_p(1 << i, io); |
316 | udelay(SHORT_DELAY); | 265 | udelay(SHORT_DELAY); |
317 | 266 | ||
318 | if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) { | 267 | if ((inb_p(io) & ~GEMTEK_NS) != (0x17 | (1 << (i + 5)))) |
319 | mutex_unlock(>->lock); | 268 | return false; |
320 | return 0; | ||
321 | } | ||
322 | } | 269 | } |
323 | outb_p(q >> 5, port); /* Write bus contents back. */ | 270 | outb_p(q >> 5, io); /* Write bus contents back. */ |
324 | udelay(SHORT_DELAY); | 271 | udelay(SHORT_DELAY); |
325 | 272 | return true; | |
326 | mutex_unlock(>->lock); | ||
327 | gt->verified = port; | ||
328 | |||
329 | return 1; | ||
330 | } | ||
331 | |||
332 | /* | ||
333 | * Automatic probing for card. | ||
334 | */ | ||
335 | static int gemtek_probe(struct gemtek *gt) | ||
336 | { | ||
337 | struct v4l2_device *v4l2_dev = >->v4l2_dev; | ||
338 | int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c }; | ||
339 | int i; | ||
340 | |||
341 | if (!probe) { | ||
342 | v4l2_info(v4l2_dev, "Automatic device probing disabled.\n"); | ||
343 | return -1; | ||
344 | } | ||
345 | |||
346 | v4l2_info(v4l2_dev, "Automatic device probing enabled.\n"); | ||
347 | |||
348 | for (i = 0; i < ARRAY_SIZE(ioports); ++i) { | ||
349 | v4l2_info(v4l2_dev, "Trying I/O port 0x%x...\n", ioports[i]); | ||
350 | |||
351 | if (!request_region(ioports[i], 1, "gemtek-probe")) { | ||
352 | v4l2_warn(v4l2_dev, "I/O port 0x%x busy!\n", | ||
353 | ioports[i]); | ||
354 | continue; | ||
355 | } | ||
356 | |||
357 | if (gemtek_verify(gt, ioports[i])) { | ||
358 | v4l2_info(v4l2_dev, "Card found from I/O port " | ||
359 | "0x%x!\n", ioports[i]); | ||
360 | |||
361 | release_region(ioports[i], 1); | ||
362 | gt->io = ioports[i]; | ||
363 | return gt->io; | ||
364 | } | ||
365 | |||
366 | release_region(ioports[i], 1); | ||
367 | } | ||
368 | |||
369 | v4l2_err(v4l2_dev, "Automatic probing failed!\n"); | ||
370 | return -1; | ||
371 | } | 273 | } |
372 | 274 | ||
373 | /* | 275 | static const struct radio_isa_ops gemtek_ops = { |
374 | * Video 4 Linux stuff. | 276 | .alloc = gemtek_alloc, |
375 | */ | 277 | .probe = gemtek_probe, |
376 | 278 | .s_mute_volume = gemtek_s_mute_volume, | |
377 | static const struct v4l2_file_operations gemtek_fops = { | 279 | .s_frequency = gemtek_s_frequency, |
378 | .owner = THIS_MODULE, | 280 | .g_rxsubchans = gemtek_g_rxsubchans, |
379 | .unlocked_ioctl = video_ioctl2, | ||
380 | }; | 281 | }; |
381 | 282 | ||
382 | static int vidioc_querycap(struct file *file, void *priv, | 283 | static const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c }; |
383 | struct v4l2_capability *v) | 284 | |
384 | { | 285 | static struct radio_isa_driver gemtek_driver = { |
385 | strlcpy(v->driver, "radio-gemtek", sizeof(v->driver)); | 286 | .driver = { |
386 | strlcpy(v->card, "GemTek", sizeof(v->card)); | 287 | .match = radio_isa_match, |
387 | strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); | 288 | .probe = radio_isa_probe, |
388 | v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; | 289 | .remove = radio_isa_remove, |
389 | return 0; | 290 | .driver = { |
390 | } | 291 | .name = "radio-gemtek", |
391 | 292 | }, | |
392 | static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) | 293 | }, |
393 | { | 294 | .io_params = io, |
394 | struct gemtek *gt = video_drvdata(file); | 295 | .radio_nr_params = radio_nr, |
395 | 296 | .io_ports = gemtek_ioports, | |
396 | if (v->index > 0) | 297 | .num_of_io_ports = ARRAY_SIZE(gemtek_ioports), |
397 | return -EINVAL; | 298 | .region_size = 1, |
398 | 299 | .card = "GemTek Radio", | |
399 | strlcpy(v->name, "FM", sizeof(v->name)); | 300 | .ops = &gemtek_ops, |
400 | v->type = V4L2_TUNER_RADIO; | 301 | .has_stereo = true, |
401 | v->rangelow = GEMTEK_LOWFREQ; | ||
402 | v->rangehigh = GEMTEK_HIGHFREQ; | ||
403 | v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; | ||
404 | v->signal = 0xffff * gemtek_getsigstr(gt); | ||
405 | if (v->signal) { | ||
406 | v->audmode = V4L2_TUNER_MODE_STEREO; | ||
407 | v->rxsubchans = V4L2_TUNER_SUB_STEREO; | ||
408 | } else { | ||
409 | v->audmode = V4L2_TUNER_MODE_MONO; | ||
410 | v->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
411 | } | ||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) | ||
416 | { | ||
417 | return (v->index != 0) ? -EINVAL : 0; | ||
418 | } | ||
419 | |||
420 | static int vidioc_g_frequency(struct file *file, void *priv, | ||
421 | struct v4l2_frequency *f) | ||
422 | { | ||
423 | struct gemtek *gt = video_drvdata(file); | ||
424 | |||
425 | if (f->tuner != 0) | ||
426 | return -EINVAL; | ||
427 | f->type = V4L2_TUNER_RADIO; | ||
428 | f->frequency = gt->lastfreq; | ||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | static int vidioc_s_frequency(struct file *file, void *priv, | ||
433 | struct v4l2_frequency *f) | ||
434 | { | ||
435 | struct gemtek *gt = video_drvdata(file); | ||
436 | |||
437 | if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) | ||
438 | return -EINVAL; | ||
439 | gemtek_setfreq(gt, f->frequency); | ||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | static int vidioc_queryctrl(struct file *file, void *priv, | ||
444 | struct v4l2_queryctrl *qc) | ||
445 | { | ||
446 | switch (qc->id) { | ||
447 | case V4L2_CID_AUDIO_MUTE: | ||
448 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); | ||
449 | default: | ||
450 | return -EINVAL; | ||
451 | } | ||
452 | } | ||
453 | |||
454 | static int vidioc_g_ctrl(struct file *file, void *priv, | ||
455 | struct v4l2_control *ctrl) | ||
456 | { | ||
457 | struct gemtek *gt = video_drvdata(file); | ||
458 | |||
459 | switch (ctrl->id) { | ||
460 | case V4L2_CID_AUDIO_MUTE: | ||
461 | ctrl->value = gt->muted; | ||
462 | return 0; | ||
463 | } | ||
464 | return -EINVAL; | ||
465 | } | ||
466 | |||
467 | static int vidioc_s_ctrl(struct file *file, void *priv, | ||
468 | struct v4l2_control *ctrl) | ||
469 | { | ||
470 | struct gemtek *gt = video_drvdata(file); | ||
471 | |||
472 | switch (ctrl->id) { | ||
473 | case V4L2_CID_AUDIO_MUTE: | ||
474 | if (ctrl->value) | ||
475 | gemtek_mute(gt); | ||
476 | else | ||
477 | gemtek_unmute(gt); | ||
478 | return 0; | ||
479 | } | ||
480 | return -EINVAL; | ||
481 | } | ||
482 | |||
483 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | ||
484 | { | ||
485 | *i = 0; | ||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | ||
490 | { | ||
491 | return (i != 0) ? -EINVAL : 0; | ||
492 | } | ||
493 | |||
494 | static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) | ||
495 | { | ||
496 | a->index = 0; | ||
497 | strlcpy(a->name, "Radio", sizeof(a->name)); | ||
498 | a->capability = V4L2_AUDCAP_STEREO; | ||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) | ||
503 | { | ||
504 | return (a->index != 0) ? -EINVAL : 0; | ||
505 | } | ||
506 | |||
507 | static const struct v4l2_ioctl_ops gemtek_ioctl_ops = { | ||
508 | .vidioc_querycap = vidioc_querycap, | ||
509 | .vidioc_g_tuner = vidioc_g_tuner, | ||
510 | .vidioc_s_tuner = vidioc_s_tuner, | ||
511 | .vidioc_g_audio = vidioc_g_audio, | ||
512 | .vidioc_s_audio = vidioc_s_audio, | ||
513 | .vidioc_g_input = vidioc_g_input, | ||
514 | .vidioc_s_input = vidioc_s_input, | ||
515 | .vidioc_g_frequency = vidioc_g_frequency, | ||
516 | .vidioc_s_frequency = vidioc_s_frequency, | ||
517 | .vidioc_queryctrl = vidioc_queryctrl, | ||
518 | .vidioc_g_ctrl = vidioc_g_ctrl, | ||
519 | .vidioc_s_ctrl = vidioc_s_ctrl | ||
520 | }; | 302 | }; |
521 | 303 | ||
522 | /* | ||
523 | * Initialization / cleanup related stuff. | ||
524 | */ | ||
525 | |||
526 | static int __init gemtek_init(void) | 304 | static int __init gemtek_init(void) |
527 | { | 305 | { |
528 | struct gemtek *gt = &gemtek_card; | 306 | gemtek_driver.probe = probe; |
529 | struct v4l2_device *v4l2_dev = >->v4l2_dev; | 307 | return isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX); |
530 | int res; | ||
531 | |||
532 | strlcpy(v4l2_dev->name, "gemtek", sizeof(v4l2_dev->name)); | ||
533 | |||
534 | v4l2_info(v4l2_dev, "GemTek Radio card driver: v0.0.3\n"); | ||
535 | |||
536 | mutex_init(>->lock); | ||
537 | |||
538 | gt->verified = -1; | ||
539 | gt->io = io; | ||
540 | gemtek_probe(gt); | ||
541 | if (gt->io) { | ||
542 | if (!request_region(gt->io, 1, "gemtek")) { | ||
543 | v4l2_err(v4l2_dev, "I/O port 0x%x already in use.\n", gt->io); | ||
544 | return -EBUSY; | ||
545 | } | ||
546 | |||
547 | if (!gemtek_verify(gt, gt->io)) | ||
548 | v4l2_warn(v4l2_dev, "Card at I/O port 0x%x does not " | ||
549 | "respond properly, check your " | ||
550 | "configuration.\n", gt->io); | ||
551 | else | ||
552 | v4l2_info(v4l2_dev, "Using I/O port 0x%x.\n", gt->io); | ||
553 | } else if (probe) { | ||
554 | v4l2_err(v4l2_dev, "Automatic probing failed and no " | ||
555 | "fixed I/O port defined.\n"); | ||
556 | return -ENODEV; | ||
557 | } else { | ||
558 | v4l2_err(v4l2_dev, "Automatic probing disabled but no fixed " | ||
559 | "I/O port defined."); | ||
560 | return -EINVAL; | ||
561 | } | ||
562 | |||
563 | res = v4l2_device_register(NULL, v4l2_dev); | ||
564 | if (res < 0) { | ||
565 | v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); | ||
566 | release_region(gt->io, 1); | ||
567 | return res; | ||
568 | } | ||
569 | |||
570 | strlcpy(gt->vdev.name, v4l2_dev->name, sizeof(gt->vdev.name)); | ||
571 | gt->vdev.v4l2_dev = v4l2_dev; | ||
572 | gt->vdev.fops = &gemtek_fops; | ||
573 | gt->vdev.ioctl_ops = &gemtek_ioctl_ops; | ||
574 | gt->vdev.release = video_device_release_empty; | ||
575 | video_set_drvdata(>->vdev, gt); | ||
576 | |||
577 | /* Set defaults */ | ||
578 | gt->lastfreq = GEMTEK_LOWFREQ; | ||
579 | gt->bu2614data = 0; | ||
580 | |||
581 | if (initmute) | ||
582 | gemtek_mute(gt); | ||
583 | |||
584 | if (video_register_device(>->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { | ||
585 | v4l2_device_unregister(v4l2_dev); | ||
586 | release_region(gt->io, 1); | ||
587 | return -EBUSY; | ||
588 | } | ||
589 | |||
590 | return 0; | ||
591 | } | 308 | } |
592 | 309 | ||
593 | /* | ||
594 | * Module cleanup | ||
595 | */ | ||
596 | static void __exit gemtek_exit(void) | 310 | static void __exit gemtek_exit(void) |
597 | { | 311 | { |
598 | struct gemtek *gt = &gemtek_card; | 312 | hardmute = 1; /* Turn off PLL */ |
599 | struct v4l2_device *v4l2_dev = >->v4l2_dev; | 313 | isa_unregister_driver(&gemtek_driver.driver); |
600 | |||
601 | if (shutdown) { | ||
602 | hardmute = 1; /* Turn off PLL */ | ||
603 | gemtek_mute(gt); | ||
604 | } else { | ||
605 | v4l2_info(v4l2_dev, "Module unloaded but card not muted!\n"); | ||
606 | } | ||
607 | |||
608 | video_unregister_device(>->vdev); | ||
609 | v4l2_device_unregister(>->v4l2_dev); | ||
610 | release_region(gt->io, 1); | ||
611 | } | 314 | } |
612 | 315 | ||
613 | module_init(gemtek_init); | 316 | module_init(gemtek_init); |