diff options
author | Hans Verkuil <hans.verkuil@cisco.com> | 2012-01-16 02:55:10 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-02-14 14:08:52 -0500 |
commit | cc3c6df16b3fb953818780b52c86fc7a9f08b337 (patch) | |
tree | c21f0663ba8a96ceedeb462f8d6b29d0eb9d7d2b /drivers | |
parent | 137c579c12bbb47ac1822e1a959aa15d0fcb76c1 (diff) |
[media] radio-aimslab: Convert to radio-isa
Tested with actual hardware and the Keene USB FM Transmitter.
Improved the volume handling delays through trial and error.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/radio/Kconfig | 9 | ||||
-rw-r--r-- | drivers/media/radio/radio-aimslab.c | 439 |
2 files changed, 110 insertions, 338 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index e291e0e2669f..618c33295ad1 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig | |||
@@ -202,6 +202,7 @@ config RADIO_CADET | |||
202 | config RADIO_RTRACK | 202 | config RADIO_RTRACK |
203 | tristate "AIMSlab RadioTrack (aka RadioReveal) support" | 203 | tristate "AIMSlab RadioTrack (aka RadioReveal) support" |
204 | depends on ISA && VIDEO_V4L2 | 204 | depends on ISA && VIDEO_V4L2 |
205 | select RADIO_ISA | ||
205 | ---help--- | 206 | ---help--- |
206 | Choose Y here if you have one of these FM radio cards, and then fill | 207 | Choose Y here if you have one of these FM radio cards, and then fill |
207 | in the port address below. | 208 | in the port address below. |
@@ -215,11 +216,7 @@ config RADIO_RTRACK | |||
215 | You must also pass the module a suitable io parameter, 0x248 has | 216 | You must also pass the module a suitable io parameter, 0x248 has |
216 | been reported to be used by these cards. | 217 | been reported to be used by these cards. |
217 | 218 | ||
218 | In order to control your radio card, you will need to use programs | 219 | More information is contained in the file |
219 | that are compatible with the Video For Linux API. Information on | ||
220 | this API and pointers to "v4l" programs may be found at | ||
221 | <file:Documentation/video4linux/API.html>. More information is | ||
222 | contained in the file | ||
223 | <file:Documentation/video4linux/radiotrack.txt>. | 220 | <file:Documentation/video4linux/radiotrack.txt>. |
224 | 221 | ||
225 | To compile this driver as a module, choose M here: the | 222 | To compile this driver as a module, choose M here: the |
@@ -228,7 +225,7 @@ config RADIO_RTRACK | |||
228 | config RADIO_RTRACK_PORT | 225 | config RADIO_RTRACK_PORT |
229 | hex "RadioTrack i/o port (0x20f or 0x30f)" | 226 | hex "RadioTrack i/o port (0x20f or 0x30f)" |
230 | depends on RADIO_RTRACK=y | 227 | depends on RADIO_RTRACK=y |
231 | default "20f" | 228 | default "30f" |
232 | help | 229 | help |
233 | Enter either 0x30f or 0x20f here. The card default is 0x30f, if you | 230 | Enter either 0x30f or 0x20f here. The card default is 0x30f, if you |
234 | haven't changed the jumper setting on the card. | 231 | haven't changed the jumper setting on the card. |
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index 1c3f8440a55c..862dfce1f2fd 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c | |||
@@ -1,16 +1,13 @@ | |||
1 | /* radiotrack (radioreveal) driver for Linux radio support | 1 | /* |
2 | * (c) 1997 M. Kirkwood | 2 | * AimsLab RadioTrack (aka RadioVeveal) driver |
3 | * | ||
4 | * Copyright 1997 M. Kirkwood | ||
5 | * | ||
6 | * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> | ||
3 | * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> | 7 | * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> |
4 | * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> | 8 | * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> |
5 | * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> | 9 | * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> |
6 | * | 10 | * |
7 | * History: | ||
8 | * 1999-02-24 Russell Kroll <rkroll@exploits.org> | ||
9 | * Fine tuning/VIDEO_TUNER_LOW | ||
10 | * Frequency range expanded to start at 87 MHz | ||
11 | * | ||
12 | * TODO: Allow for more than one of these foolish entities :-) | ||
13 | * | ||
14 | * Notes on the hardware (reverse engineered from other peoples' | 11 | * Notes on the hardware (reverse engineered from other peoples' |
15 | * reverse engineering of AIMS' code :-) | 12 | * reverse engineering of AIMS' code :-) |
16 | * | 13 | * |
@@ -26,6 +23,7 @@ | |||
26 | * wait(a_wee_while); | 23 | * wait(a_wee_while); |
27 | * out(port, stop_changing_the_volume); | 24 | * out(port, stop_changing_the_volume); |
28 | * | 25 | * |
26 | * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. | ||
29 | */ | 27 | */ |
30 | 28 | ||
31 | #include <linux/module.h> /* Modules */ | 29 | #include <linux/module.h> /* Modules */ |
@@ -36,399 +34,176 @@ | |||
36 | #include <linux/io.h> /* outb, outb_p */ | 34 | #include <linux/io.h> /* outb, outb_p */ |
37 | #include <media/v4l2-device.h> | 35 | #include <media/v4l2-device.h> |
38 | #include <media/v4l2-ioctl.h> | 36 | #include <media/v4l2-ioctl.h> |
37 | #include <media/v4l2-ctrls.h> | ||
38 | #include "radio-isa.h" | ||
39 | 39 | ||
40 | MODULE_AUTHOR("M.Kirkwood"); | 40 | MODULE_AUTHOR("M. Kirkwood"); |
41 | MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card."); | 41 | MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card."); |
42 | MODULE_LICENSE("GPL"); | 42 | MODULE_LICENSE("GPL"); |
43 | MODULE_VERSION("0.0.3"); | 43 | MODULE_VERSION("1.0.0"); |
44 | 44 | ||
45 | #ifndef CONFIG_RADIO_RTRACK_PORT | 45 | #ifndef CONFIG_RADIO_RTRACK_PORT |
46 | #define CONFIG_RADIO_RTRACK_PORT -1 | 46 | #define CONFIG_RADIO_RTRACK_PORT -1 |
47 | #endif | 47 | #endif |
48 | 48 | ||
49 | static int io = CONFIG_RADIO_RTRACK_PORT; | 49 | #define RTRACK_MAX 2 |
50 | static int radio_nr = -1; | ||
51 | 50 | ||
52 | module_param(io, int, 0); | 51 | static int io[RTRACK_MAX] = { [0] = CONFIG_RADIO_RTRACK_PORT, |
53 | MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)"); | 52 | [1 ... (RTRACK_MAX - 1)] = -1 }; |
54 | module_param(radio_nr, int, 0); | 53 | static int radio_nr[RTRACK_MAX] = { [0 ... (RTRACK_MAX - 1)] = -1 }; |
55 | 54 | ||
56 | struct rtrack | 55 | module_param_array(io, int, NULL, 0444); |
57 | { | 56 | MODULE_PARM_DESC(io, "I/O addresses of the RadioTrack card (0x20f or 0x30f)"); |
58 | struct v4l2_device v4l2_dev; | 57 | module_param_array(radio_nr, int, NULL, 0444); |
59 | struct video_device vdev; | 58 | MODULE_PARM_DESC(radio_nr, "Radio device numbers"); |
60 | int port; | 59 | |
60 | struct rtrack { | ||
61 | struct radio_isa_card isa; | ||
61 | int curvol; | 62 | int curvol; |
62 | unsigned long curfreq; | ||
63 | int muted; | ||
64 | int io; | ||
65 | struct mutex lock; | ||
66 | }; | 63 | }; |
67 | 64 | ||
68 | static struct rtrack rtrack_card; | 65 | static struct radio_isa_card *rtrack_alloc(void) |
69 | |||
70 | /* local things */ | ||
71 | |||
72 | static void rt_decvol(struct rtrack *rt) | ||
73 | { | ||
74 | outb(0x58, rt->io); /* volume down + sigstr + on */ | ||
75 | msleep(100); | ||
76 | outb(0xd8, rt->io); /* volume steady + sigstr + on */ | ||
77 | } | ||
78 | |||
79 | static void rt_incvol(struct rtrack *rt) | ||
80 | { | ||
81 | outb(0x98, rt->io); /* volume up + sigstr + on */ | ||
82 | msleep(100); | ||
83 | outb(0xd8, rt->io); /* volume steady + sigstr + on */ | ||
84 | } | ||
85 | |||
86 | static void rt_mute(struct rtrack *rt) | ||
87 | { | ||
88 | rt->muted = 1; | ||
89 | mutex_lock(&rt->lock); | ||
90 | outb(0xd0, rt->io); /* volume steady, off */ | ||
91 | mutex_unlock(&rt->lock); | ||
92 | } | ||
93 | |||
94 | static int rt_setvol(struct rtrack *rt, int vol) | ||
95 | { | 66 | { |
96 | int i; | 67 | struct rtrack *rt = kzalloc(sizeof(struct rtrack), GFP_KERNEL); |
97 | |||
98 | mutex_lock(&rt->lock); | ||
99 | |||
100 | if (vol == rt->curvol) { /* requested volume = current */ | ||
101 | if (rt->muted) { /* user is unmuting the card */ | ||
102 | rt->muted = 0; | ||
103 | outb(0xd8, rt->io); /* enable card */ | ||
104 | } | ||
105 | mutex_unlock(&rt->lock); | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | if (vol == 0) { /* volume = 0 means mute the card */ | ||
110 | outb(0x48, rt->io); /* volume down but still "on" */ | ||
111 | msleep(2000); /* make sure it's totally down */ | ||
112 | outb(0xd0, rt->io); /* volume steady, off */ | ||
113 | rt->curvol = 0; /* track the volume state! */ | ||
114 | mutex_unlock(&rt->lock); | ||
115 | return 0; | ||
116 | } | ||
117 | 68 | ||
118 | rt->muted = 0; | 69 | if (rt) |
119 | if (vol > rt->curvol) | 70 | rt->curvol = 0xff; |
120 | for (i = rt->curvol; i < vol; i++) | 71 | return rt ? &rt->isa : NULL; |
121 | rt_incvol(rt); | ||
122 | else | ||
123 | for (i = rt->curvol; i > vol; i--) | ||
124 | rt_decvol(rt); | ||
125 | |||
126 | rt->curvol = vol; | ||
127 | mutex_unlock(&rt->lock); | ||
128 | return 0; | ||
129 | } | 72 | } |
130 | 73 | ||
131 | /* the 128+64 on these outb's is to keep the volume stable while tuning | 74 | /* The 128+64 on these outb's is to keep the volume stable while tuning. |
132 | * without them, the volume _will_ creep up with each frequency change | 75 | * Without them, the volume _will_ creep up with each frequency change |
133 | * and bit 4 (+16) is to keep the signal strength meter enabled | 76 | * and bit 4 (+16) is to keep the signal strength meter enabled. |
134 | */ | 77 | */ |
135 | 78 | ||
136 | static void send_0_byte(struct rtrack *rt) | 79 | static void send_0_byte(struct radio_isa_card *isa, int on) |
137 | { | 80 | { |
138 | if (rt->curvol == 0 || rt->muted) { | 81 | outb_p(128+64+16+on+1, isa->io); /* wr-enable + data low */ |
139 | outb_p(128+64+16+ 1, rt->io); /* wr-enable + data low */ | 82 | outb_p(128+64+16+on+2+1, isa->io); /* clock */ |
140 | outb_p(128+64+16+2+1, rt->io); /* clock */ | ||
141 | } | ||
142 | else { | ||
143 | outb_p(128+64+16+8+ 1, rt->io); /* on + wr-enable + data low */ | ||
144 | outb_p(128+64+16+8+2+1, rt->io); /* clock */ | ||
145 | } | ||
146 | msleep(1); | 83 | msleep(1); |
147 | } | 84 | } |
148 | 85 | ||
149 | static void send_1_byte(struct rtrack *rt) | 86 | static void send_1_byte(struct radio_isa_card *isa, int on) |
150 | { | 87 | { |
151 | if (rt->curvol == 0 || rt->muted) { | 88 | outb_p(128+64+16+on+4+1, isa->io); /* wr-enable+data high */ |
152 | outb_p(128+64+16+4 +1, rt->io); /* wr-enable+data high */ | 89 | outb_p(128+64+16+on+4+2+1, isa->io); /* clock */ |
153 | outb_p(128+64+16+4+2+1, rt->io); /* clock */ | ||
154 | } | ||
155 | else { | ||
156 | outb_p(128+64+16+8+4 +1, rt->io); /* on+wr-enable+data high */ | ||
157 | outb_p(128+64+16+8+4+2+1, rt->io); /* clock */ | ||
158 | } | ||
159 | |||
160 | msleep(1); | 90 | msleep(1); |
161 | } | 91 | } |
162 | 92 | ||
163 | static int rt_setfreq(struct rtrack *rt, unsigned long freq) | 93 | static int rtrack_s_frequency(struct radio_isa_card *isa, u32 freq) |
164 | { | 94 | { |
95 | int on = v4l2_ctrl_g_ctrl(isa->mute) ? 0 : 8; | ||
165 | int i; | 96 | int i; |
166 | 97 | ||
167 | mutex_lock(&rt->lock); /* Stop other ops interfering */ | ||
168 | |||
169 | rt->curfreq = freq; | ||
170 | |||
171 | /* now uses VIDEO_TUNER_LOW for fine tuning */ | ||
172 | |||
173 | freq += 171200; /* Add 10.7 MHz IF */ | 98 | freq += 171200; /* Add 10.7 MHz IF */ |
174 | freq /= 800; /* Convert to 50 kHz units */ | 99 | freq /= 800; /* Convert to 50 kHz units */ |
175 | 100 | ||
176 | send_0_byte(rt); /* 0: LSB of frequency */ | 101 | send_0_byte(isa, on); /* 0: LSB of frequency */ |
177 | 102 | ||
178 | for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ | 103 | for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ |
179 | if (freq & (1 << i)) | 104 | if (freq & (1 << i)) |
180 | send_1_byte(rt); | 105 | send_1_byte(isa, on); |
181 | else | 106 | else |
182 | send_0_byte(rt); | 107 | send_0_byte(isa, on); |
183 | |||
184 | send_0_byte(rt); /* 14: test bit - always 0 */ | ||
185 | send_0_byte(rt); /* 15: test bit - always 0 */ | ||
186 | |||
187 | send_0_byte(rt); /* 16: band data 0 - always 0 */ | ||
188 | send_0_byte(rt); /* 17: band data 1 - always 0 */ | ||
189 | send_0_byte(rt); /* 18: band data 2 - always 0 */ | ||
190 | send_0_byte(rt); /* 19: time base - always 0 */ | ||
191 | |||
192 | send_0_byte(rt); /* 20: spacing (0 = 25 kHz) */ | ||
193 | send_1_byte(rt); /* 21: spacing (1 = 25 kHz) */ | ||
194 | send_0_byte(rt); /* 22: spacing (0 = 25 kHz) */ | ||
195 | send_1_byte(rt); /* 23: AM/FM (FM = 1, always) */ | ||
196 | |||
197 | if (rt->curvol == 0 || rt->muted) | ||
198 | outb(0xd0, rt->io); /* volume steady + sigstr */ | ||
199 | else | ||
200 | outb(0xd8, rt->io); /* volume steady + sigstr + on */ | ||
201 | |||
202 | mutex_unlock(&rt->lock); | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static int rt_getsigstr(struct rtrack *rt) | ||
208 | { | ||
209 | int sig = 1; | ||
210 | |||
211 | mutex_lock(&rt->lock); | ||
212 | if (inb(rt->io) & 2) /* bit set = no signal present */ | ||
213 | sig = 0; | ||
214 | mutex_unlock(&rt->lock); | ||
215 | return sig; | ||
216 | } | ||
217 | |||
218 | static int vidioc_querycap(struct file *file, void *priv, | ||
219 | struct v4l2_capability *v) | ||
220 | { | ||
221 | strlcpy(v->driver, "radio-aimslab", sizeof(v->driver)); | ||
222 | strlcpy(v->card, "RadioTrack", sizeof(v->card)); | ||
223 | strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); | ||
224 | v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; | ||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | static int vidioc_g_tuner(struct file *file, void *priv, | ||
229 | struct v4l2_tuner *v) | ||
230 | { | ||
231 | struct rtrack *rt = video_drvdata(file); | ||
232 | 108 | ||
233 | if (v->index > 0) | 109 | send_0_byte(isa, on); /* 14: test bit - always 0 */ |
234 | return -EINVAL; | 110 | send_0_byte(isa, on); /* 15: test bit - always 0 */ |
235 | 111 | ||
236 | strlcpy(v->name, "FM", sizeof(v->name)); | 112 | send_0_byte(isa, on); /* 16: band data 0 - always 0 */ |
237 | v->type = V4L2_TUNER_RADIO; | 113 | send_0_byte(isa, on); /* 17: band data 1 - always 0 */ |
238 | v->rangelow = 87 * 16000; | 114 | send_0_byte(isa, on); /* 18: band data 2 - always 0 */ |
239 | v->rangehigh = 108 * 16000; | 115 | send_0_byte(isa, on); /* 19: time base - always 0 */ |
240 | v->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
241 | v->capability = V4L2_TUNER_CAP_LOW; | ||
242 | v->audmode = V4L2_TUNER_MODE_MONO; | ||
243 | v->signal = 0xffff * rt_getsigstr(rt); | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static int vidioc_s_tuner(struct file *file, void *priv, | ||
248 | struct v4l2_tuner *v) | ||
249 | { | ||
250 | return v->index ? -EINVAL : 0; | ||
251 | } | ||
252 | |||
253 | static int vidioc_s_frequency(struct file *file, void *priv, | ||
254 | struct v4l2_frequency *f) | ||
255 | { | ||
256 | struct rtrack *rt = video_drvdata(file); | ||
257 | |||
258 | if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) | ||
259 | return -EINVAL; | ||
260 | rt_setfreq(rt, f->frequency); | ||
261 | return 0; | ||
262 | } | ||
263 | 116 | ||
264 | static int vidioc_g_frequency(struct file *file, void *priv, | 117 | send_0_byte(isa, on); /* 20: spacing (0 = 25 kHz) */ |
265 | struct v4l2_frequency *f) | 118 | send_1_byte(isa, on); /* 21: spacing (1 = 25 kHz) */ |
266 | { | 119 | send_0_byte(isa, on); /* 22: spacing (0 = 25 kHz) */ |
267 | struct rtrack *rt = video_drvdata(file); | 120 | send_1_byte(isa, on); /* 23: AM/FM (FM = 1, always) */ |
268 | 121 | ||
269 | if (f->tuner != 0) | 122 | outb(0xd0 + on, isa->io); /* volume steady + sigstr */ |
270 | return -EINVAL; | ||
271 | f->type = V4L2_TUNER_RADIO; | ||
272 | f->frequency = rt->curfreq; | ||
273 | return 0; | 123 | return 0; |
274 | } | 124 | } |
275 | 125 | ||
276 | static int vidioc_queryctrl(struct file *file, void *priv, | 126 | static u32 rtrack_g_signal(struct radio_isa_card *isa) |
277 | struct v4l2_queryctrl *qc) | ||
278 | { | 127 | { |
279 | switch (qc->id) { | 128 | /* bit set = no signal present */ |
280 | case V4L2_CID_AUDIO_MUTE: | 129 | return 0xffff * !(inb(isa->io) & 2); |
281 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); | ||
282 | case V4L2_CID_AUDIO_VOLUME: | ||
283 | return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff); | ||
284 | } | ||
285 | return -EINVAL; | ||
286 | } | 130 | } |
287 | 131 | ||
288 | static int vidioc_g_ctrl(struct file *file, void *priv, | 132 | static int rtrack_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) |
289 | struct v4l2_control *ctrl) | ||
290 | { | 133 | { |
291 | struct rtrack *rt = video_drvdata(file); | 134 | struct rtrack *rt = container_of(isa, struct rtrack, isa); |
135 | int curvol = rt->curvol; | ||
292 | 136 | ||
293 | switch (ctrl->id) { | 137 | if (mute) { |
294 | case V4L2_CID_AUDIO_MUTE: | 138 | outb(0xd0, isa->io); /* volume steady + sigstr + off */ |
295 | ctrl->value = rt->muted; | ||
296 | return 0; | ||
297 | case V4L2_CID_AUDIO_VOLUME: | ||
298 | ctrl->value = rt->curvol; | ||
299 | return 0; | 139 | return 0; |
300 | } | 140 | } |
301 | return -EINVAL; | 141 | if (vol == 0) { /* volume = 0 means mute the card */ |
302 | } | 142 | outb(0x48, isa->io); /* volume down but still "on" */ |
303 | 143 | msleep(curvol * 3); /* make sure it's totally down */ | |
304 | static int vidioc_s_ctrl(struct file *file, void *priv, | 144 | } else if (curvol < vol) { |
305 | struct v4l2_control *ctrl) | 145 | outb(0x98, isa->io); /* volume up + sigstr + on */ |
306 | { | 146 | for (; curvol < vol; curvol++) |
307 | struct rtrack *rt = video_drvdata(file); | 147 | udelay(3000); |
308 | 148 | } else if (curvol > vol) { | |
309 | switch (ctrl->id) { | 149 | outb(0x58, isa->io); /* volume down + sigstr + on */ |
310 | case V4L2_CID_AUDIO_MUTE: | 150 | for (; curvol > vol; curvol--) |
311 | if (ctrl->value) | 151 | udelay(3000); |
312 | rt_mute(rt); | ||
313 | else | ||
314 | rt_setvol(rt, rt->curvol); | ||
315 | return 0; | ||
316 | case V4L2_CID_AUDIO_VOLUME: | ||
317 | rt_setvol(rt, ctrl->value); | ||
318 | return 0; | ||
319 | } | 152 | } |
320 | return -EINVAL; | 153 | outb(0xd8, isa->io); /* volume steady + sigstr + on */ |
321 | } | 154 | rt->curvol = vol; |
322 | |||
323 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | ||
324 | { | ||
325 | *i = 0; | ||
326 | return 0; | 155 | return 0; |
327 | } | 156 | } |
328 | 157 | ||
329 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | 158 | /* Mute card - prevents noisy bootups */ |
159 | static int rtrack_initialize(struct radio_isa_card *isa) | ||
330 | { | 160 | { |
331 | return i ? -EINVAL : 0; | 161 | /* this ensures that the volume is all the way up */ |
332 | } | 162 | outb(0x90, isa->io); /* volume up but still "on" */ |
333 | 163 | msleep(3000); /* make sure it's totally up */ | |
334 | static int vidioc_g_audio(struct file *file, void *priv, | 164 | outb(0xc0, isa->io); /* steady volume, mute card */ |
335 | struct v4l2_audio *a) | ||
336 | { | ||
337 | a->index = 0; | ||
338 | strlcpy(a->name, "Radio", sizeof(a->name)); | ||
339 | a->capability = V4L2_AUDCAP_STEREO; | ||
340 | return 0; | 165 | return 0; |
341 | } | 166 | } |
342 | 167 | ||
343 | static int vidioc_s_audio(struct file *file, void *priv, | 168 | static const struct radio_isa_ops rtrack_ops = { |
344 | struct v4l2_audio *a) | 169 | .alloc = rtrack_alloc, |
345 | { | 170 | .init = rtrack_initialize, |
346 | return a->index ? -EINVAL : 0; | 171 | .s_mute_volume = rtrack_s_mute_volume, |
347 | } | 172 | .s_frequency = rtrack_s_frequency, |
348 | 173 | .g_signal = rtrack_g_signal, | |
349 | static const struct v4l2_file_operations rtrack_fops = { | ||
350 | .owner = THIS_MODULE, | ||
351 | .unlocked_ioctl = video_ioctl2, | ||
352 | }; | 174 | }; |
353 | 175 | ||
354 | static const struct v4l2_ioctl_ops rtrack_ioctl_ops = { | 176 | static const int rtrack_ioports[] = { 0x20f, 0x30f }; |
355 | .vidioc_querycap = vidioc_querycap, | 177 | |
356 | .vidioc_g_tuner = vidioc_g_tuner, | 178 | static struct radio_isa_driver rtrack_driver = { |
357 | .vidioc_s_tuner = vidioc_s_tuner, | 179 | .driver = { |
358 | .vidioc_g_audio = vidioc_g_audio, | 180 | .match = radio_isa_match, |
359 | .vidioc_s_audio = vidioc_s_audio, | 181 | .probe = radio_isa_probe, |
360 | .vidioc_g_input = vidioc_g_input, | 182 | .remove = radio_isa_remove, |
361 | .vidioc_s_input = vidioc_s_input, | 183 | .driver = { |
362 | .vidioc_g_frequency = vidioc_g_frequency, | 184 | .name = "radio-aimslab", |
363 | .vidioc_s_frequency = vidioc_s_frequency, | 185 | }, |
364 | .vidioc_queryctrl = vidioc_queryctrl, | 186 | }, |
365 | .vidioc_g_ctrl = vidioc_g_ctrl, | 187 | .io_params = io, |
366 | .vidioc_s_ctrl = vidioc_s_ctrl, | 188 | .radio_nr_params = radio_nr, |
189 | .io_ports = rtrack_ioports, | ||
190 | .num_of_io_ports = ARRAY_SIZE(rtrack_ioports), | ||
191 | .region_size = 2, | ||
192 | .card = "AIMSlab RadioTrack/RadioReveal", | ||
193 | .ops = &rtrack_ops, | ||
194 | .has_stereo = true, | ||
195 | .max_volume = 0xff, | ||
367 | }; | 196 | }; |
368 | 197 | ||
369 | static int __init rtrack_init(void) | 198 | static int __init rtrack_init(void) |
370 | { | 199 | { |
371 | struct rtrack *rt = &rtrack_card; | 200 | return isa_register_driver(&rtrack_driver.driver, RTRACK_MAX); |
372 | struct v4l2_device *v4l2_dev = &rt->v4l2_dev; | ||
373 | int res; | ||
374 | |||
375 | strlcpy(v4l2_dev->name, "rtrack", sizeof(v4l2_dev->name)); | ||
376 | rt->io = io; | ||
377 | |||
378 | if (rt->io == -1) { | ||
379 | v4l2_err(v4l2_dev, "you must set an I/O address with io=0x20f or 0x30f\n"); | ||
380 | return -EINVAL; | ||
381 | } | ||
382 | |||
383 | if (!request_region(rt->io, 2, "rtrack")) { | ||
384 | v4l2_err(v4l2_dev, "port 0x%x already in use\n", rt->io); | ||
385 | return -EBUSY; | ||
386 | } | ||
387 | |||
388 | res = v4l2_device_register(NULL, v4l2_dev); | ||
389 | if (res < 0) { | ||
390 | release_region(rt->io, 2); | ||
391 | v4l2_err(v4l2_dev, "could not register v4l2_device\n"); | ||
392 | return res; | ||
393 | } | ||
394 | |||
395 | strlcpy(rt->vdev.name, v4l2_dev->name, sizeof(rt->vdev.name)); | ||
396 | rt->vdev.v4l2_dev = v4l2_dev; | ||
397 | rt->vdev.fops = &rtrack_fops; | ||
398 | rt->vdev.ioctl_ops = &rtrack_ioctl_ops; | ||
399 | rt->vdev.release = video_device_release_empty; | ||
400 | video_set_drvdata(&rt->vdev, rt); | ||
401 | |||
402 | /* Set up the I/O locking */ | ||
403 | |||
404 | mutex_init(&rt->lock); | ||
405 | |||
406 | /* mute card - prevents noisy bootups */ | ||
407 | |||
408 | /* this ensures that the volume is all the way down */ | ||
409 | outb(0x48, rt->io); /* volume down but still "on" */ | ||
410 | msleep(2000); /* make sure it's totally down */ | ||
411 | outb(0xc0, rt->io); /* steady volume, mute card */ | ||
412 | |||
413 | if (video_register_device(&rt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { | ||
414 | v4l2_device_unregister(&rt->v4l2_dev); | ||
415 | release_region(rt->io, 2); | ||
416 | return -EINVAL; | ||
417 | } | ||
418 | v4l2_info(v4l2_dev, "AIMSlab RadioTrack/RadioReveal card driver.\n"); | ||
419 | |||
420 | return 0; | ||
421 | } | 201 | } |
422 | 202 | ||
423 | static void __exit rtrack_exit(void) | 203 | static void __exit rtrack_exit(void) |
424 | { | 204 | { |
425 | struct rtrack *rt = &rtrack_card; | 205 | isa_unregister_driver(&rtrack_driver.driver); |
426 | |||
427 | video_unregister_device(&rt->vdev); | ||
428 | v4l2_device_unregister(&rt->v4l2_dev); | ||
429 | release_region(rt->io, 2); | ||
430 | } | 206 | } |
431 | 207 | ||
432 | module_init(rtrack_init); | 208 | module_init(rtrack_init); |
433 | module_exit(rtrack_exit); | 209 | module_exit(rtrack_exit); |
434 | |||