diff options
author | Ondrej Zary <linux@rainbow-software.org> | 2011-06-01 15:57:11 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-07-27 16:52:23 -0400 |
commit | 9e8fa0e644a3ccb1b5807e4155c1c7a73793b371 (patch) | |
tree | c862dfffdeb28a98d3e338cb9aa6ea3bb2a508d1 /drivers/media/radio | |
parent | d294093010c86294baaaa414a412ea117bacd8fc (diff) |
[media] radio-sf16fmr2: convert to generic TEA575x interface
Convert radio-sf16fmr2 to use generic TEA575x implementation. Most of the
driver code goes away as SF16-FMR2 is basically just a TEA5757 tuner
connected to ISA bus.
The card can optionally be equipped with PT2254A volume control (equivalent
of TC9154AP) - the volume setting is completely reworked (with balance control
added) and tested.
Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
Acked-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio')
-rw-r--r-- | drivers/media/radio/radio-sf16fmr2.c | 531 |
1 files changed, 143 insertions, 388 deletions
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index 87bad7678d9..2dd485996ba 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c | |||
@@ -1,441 +1,209 @@ | |||
1 | /* SF16FMR2 radio driver for Linux radio support | 1 | /* SF16-FMR2 radio driver for Linux |
2 | * heavily based on fmi driver... | 2 | * Copyright (c) 2011 Ondrej Zary |
3 | * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com | ||
4 | * | 3 | * |
5 | * Notes on the hardware | 4 | * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com |
6 | * | 5 | * but almost nothing remained here after conversion to generic TEA575x |
7 | * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); | 6 | * implementation |
8 | * No volume control - only mute/unmute - you have to use line volume | ||
9 | * | ||
10 | * For read stereo/mono you must wait 0.1 sec after set frequency and | ||
11 | * card unmuted so I set frequency on unmute | ||
12 | * Signal handling seem to work only on autoscanning (not implemented) | ||
13 | * | ||
14 | * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> | ||
15 | */ | 7 | */ |
16 | 8 | ||
9 | #include <linux/delay.h> | ||
17 | #include <linux/module.h> /* Modules */ | 10 | #include <linux/module.h> /* Modules */ |
18 | #include <linux/init.h> /* Initdata */ | 11 | #include <linux/init.h> /* Initdata */ |
19 | #include <linux/ioport.h> /* request_region */ | 12 | #include <linux/ioport.h> /* request_region */ |
20 | #include <linux/delay.h> /* udelay */ | ||
21 | #include <linux/videodev2.h> /* kernel radio structs */ | ||
22 | #include <linux/mutex.h> | ||
23 | #include <linux/version.h> /* for KERNEL_VERSION MACRO */ | ||
24 | #include <linux/io.h> /* outb, outb_p */ | 13 | #include <linux/io.h> /* outb, outb_p */ |
25 | #include <media/v4l2-device.h> | 14 | #include <sound/tea575x-tuner.h> |
26 | #include <media/v4l2-ioctl.h> | ||
27 | 15 | ||
28 | MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com"); | 16 | MODULE_AUTHOR("Ondrej Zary"); |
29 | MODULE_DESCRIPTION("A driver for the SF16FMR2 radio."); | 17 | MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver"); |
30 | MODULE_LICENSE("GPL"); | 18 | MODULE_LICENSE("GPL"); |
31 | 19 | ||
32 | static int io = 0x384; | 20 | struct fmr2 { |
33 | static int radio_nr = -1; | ||
34 | |||
35 | module_param(io, int, 0); | ||
36 | MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)"); | ||
37 | module_param(radio_nr, int, 0); | ||
38 | |||
39 | #define RADIO_VERSION KERNEL_VERSION(0,0,2) | ||
40 | |||
41 | #define AUD_VOL_INDEX 1 | ||
42 | |||
43 | #undef DEBUG | ||
44 | //#define DEBUG 1 | ||
45 | |||
46 | #ifdef DEBUG | ||
47 | # define debug_print(s) printk s | ||
48 | #else | ||
49 | # define debug_print(s) | ||
50 | #endif | ||
51 | |||
52 | /* this should be static vars for module size */ | ||
53 | struct fmr2 | ||
54 | { | ||
55 | struct v4l2_device v4l2_dev; | ||
56 | struct video_device vdev; | ||
57 | struct mutex lock; | ||
58 | int io; | 21 | int io; |
59 | int curvol; /* 0-15 */ | 22 | struct snd_tea575x tea; |
60 | int mute; | 23 | struct v4l2_ctrl *volume; |
61 | int stereo; /* card is producing stereo audio */ | 24 | struct v4l2_ctrl *balance; |
62 | unsigned long curfreq; /* freq in kHz */ | ||
63 | int card_type; | ||
64 | }; | 25 | }; |
65 | 26 | ||
27 | /* the port is hardwired so no need to support multiple cards */ | ||
28 | #define FMR2_PORT 0x384 | ||
66 | static struct fmr2 fmr2_card; | 29 | static struct fmr2 fmr2_card; |
67 | 30 | ||
68 | /* hw precision is 12.5 kHz | 31 | /* TEA575x tuner pins */ |
69 | * It is only useful to give freq in interval of 200 (=0.0125Mhz), | 32 | #define STR_DATA (1 << 0) |
70 | * other bits will be truncated | 33 | #define STR_CLK (1 << 1) |
71 | */ | 34 | #define STR_WREN (1 << 2) |
72 | #define RSF16_ENCODE(x) ((x) / 200 + 856) | 35 | #define STR_MOST (1 << 3) |
73 | #define RSF16_MINFREQ (87 * 16000) | 36 | /* PT2254A/TC9154A volume control pins */ |
74 | #define RSF16_MAXFREQ (108 * 16000) | 37 | #define PT_ST (1 << 4) |
75 | 38 | #define PT_CK (1 << 5) | |
76 | static inline void wait(int n, int io) | 39 | #define PT_DATA (1 << 6) |
77 | { | 40 | /* volume control presence pin */ |
78 | for (; n; --n) | 41 | #define FMR2_HASVOL (1 << 7) |
79 | inb(io); | ||
80 | } | ||
81 | |||
82 | static void outbits(int bits, unsigned int data, int nWait, int io) | ||
83 | { | ||
84 | int bit; | ||
85 | |||
86 | for (; --bits >= 0;) { | ||
87 | bit = (data >> bits) & 1; | ||
88 | outb(bit, io); | ||
89 | wait(nWait, io); | ||
90 | outb(bit | 2, io); | ||
91 | wait(nWait, io); | ||
92 | outb(bit, io); | ||
93 | wait(nWait, io); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | static inline void fmr2_mute(int io) | ||
98 | { | ||
99 | outb(0x00, io); | ||
100 | wait(4, io); | ||
101 | } | ||
102 | |||
103 | static inline void fmr2_unmute(int io) | ||
104 | { | ||
105 | outb(0x04, io); | ||
106 | wait(4, io); | ||
107 | } | ||
108 | |||
109 | static inline int fmr2_stereo_mode(int io) | ||
110 | { | ||
111 | int n = inb(io); | ||
112 | |||
113 | outb(6, io); | ||
114 | inb(io); | ||
115 | n = ((n >> 3) & 1) ^ 1; | ||
116 | debug_print((KERN_DEBUG "stereo: %d\n", n)); | ||
117 | return n; | ||
118 | } | ||
119 | |||
120 | static int fmr2_product_info(struct fmr2 *dev) | ||
121 | { | ||
122 | int n = inb(dev->io); | ||
123 | |||
124 | n &= 0xC1; | ||
125 | if (n == 0) { | ||
126 | /* this should support volume set */ | ||
127 | dev->card_type = 12; | ||
128 | return 0; | ||
129 | } | ||
130 | /* not volume (mine is 11) */ | ||
131 | dev->card_type = (n == 128) ? 11 : 0; | ||
132 | return n; | ||
133 | } | ||
134 | 42 | ||
135 | static inline int fmr2_getsigstr(struct fmr2 *dev) | 43 | static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) |
136 | { | 44 | { |
137 | /* !!! works only if scanning freq */ | 45 | struct fmr2 *fmr2 = tea->private_data; |
138 | int res = 0xffff; | 46 | u8 bits = 0; |
139 | |||
140 | outb(5, dev->io); | ||
141 | wait(4, dev->io); | ||
142 | if (!(inb(dev->io) & 1)) | ||
143 | res = 0; | ||
144 | debug_print((KERN_DEBUG "signal: %d\n", res)); | ||
145 | return res; | ||
146 | } | ||
147 | |||
148 | /* set frequency and unmute card */ | ||
149 | static int fmr2_setfreq(struct fmr2 *dev) | ||
150 | { | ||
151 | unsigned long freq = dev->curfreq; | ||
152 | |||
153 | fmr2_mute(dev->io); | ||
154 | |||
155 | /* 0x42 for mono output | ||
156 | * 0x102 forward scanning | ||
157 | * 0x182 scansione avanti | ||
158 | */ | ||
159 | outbits(9, 0x2, 3, dev->io); | ||
160 | outbits(16, RSF16_ENCODE(freq), 2, dev->io); | ||
161 | |||
162 | fmr2_unmute(dev->io); | ||
163 | 47 | ||
164 | /* wait 0.11 sec */ | 48 | bits |= (pins & TEA575X_DATA) ? STR_DATA : 0; |
165 | msleep(110); | 49 | bits |= (pins & TEA575X_CLK) ? STR_CLK : 0; |
50 | /* WRITE_ENABLE is inverted, DATA must be high during read */ | ||
51 | bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA; | ||
166 | 52 | ||
167 | /* NOTE if mute this stop radio | 53 | outb(bits, fmr2->io); |
168 | you must set freq on unmute */ | ||
169 | dev->stereo = fmr2_stereo_mode(dev->io); | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | /* !!! not tested, in my card this doesn't work !!! */ | ||
174 | static int fmr2_setvolume(struct fmr2 *dev) | ||
175 | { | ||
176 | int vol[16] = { 0x021, 0x084, 0x090, 0x104, | ||
177 | 0x110, 0x204, 0x210, 0x402, | ||
178 | 0x404, 0x408, 0x410, 0x801, | ||
179 | 0x802, 0x804, 0x808, 0x810 }; | ||
180 | int i, a; | ||
181 | int n = vol[dev->curvol & 0x0f]; | ||
182 | |||
183 | if (dev->card_type != 11) | ||
184 | return 1; | ||
185 | |||
186 | for (i = 12; --i >= 0; ) { | ||
187 | a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */ | ||
188 | outb(a | 4, dev->io); | ||
189 | wait(4, dev->io); | ||
190 | outb(a | 0x24, dev->io); | ||
191 | wait(4, dev->io); | ||
192 | outb(a | 4, dev->io); | ||
193 | wait(4, dev->io); | ||
194 | } | ||
195 | for (i = 6; --i >= 0; ) { | ||
196 | a = ((0x18 >> i) & 1) << 6; | ||
197 | outb(a | 4, dev->io); | ||
198 | wait(4, dev->io); | ||
199 | outb(a | 0x24, dev->io); | ||
200 | wait(4, dev->io); | ||
201 | outb(a | 4, dev->io); | ||
202 | wait(4, dev->io); | ||
203 | } | ||
204 | wait(4, dev->io); | ||
205 | outb(0x14, dev->io); | ||
206 | return 0; | ||
207 | } | 54 | } |
208 | 55 | ||
209 | static int vidioc_querycap(struct file *file, void *priv, | 56 | static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea) |
210 | struct v4l2_capability *v) | ||
211 | { | 57 | { |
212 | strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver)); | 58 | struct fmr2 *fmr2 = tea->private_data; |
213 | strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card)); | 59 | u8 bits = inb(fmr2->io); |
214 | strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); | ||
215 | v->version = RADIO_VERSION; | ||
216 | v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static int vidioc_g_tuner(struct file *file, void *priv, | ||
221 | struct v4l2_tuner *v) | ||
222 | { | ||
223 | struct fmr2 *fmr2 = video_drvdata(file); | ||
224 | |||
225 | if (v->index > 0) | ||
226 | return -EINVAL; | ||
227 | 60 | ||
228 | strlcpy(v->name, "FM", sizeof(v->name)); | 61 | return (bits & STR_DATA) ? TEA575X_DATA : 0 | |
229 | v->type = V4L2_TUNER_RADIO; | 62 | (bits & STR_MOST) ? TEA575X_MOST : 0; |
230 | |||
231 | v->rangelow = RSF16_MINFREQ; | ||
232 | v->rangehigh = RSF16_MAXFREQ; | ||
233 | v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO : | ||
234 | V4L2_TUNER_SUB_MONO; | ||
235 | v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW; | ||
236 | v->audmode = V4L2_TUNER_MODE_STEREO; | ||
237 | mutex_lock(&fmr2->lock); | ||
238 | v->signal = fmr2_getsigstr(fmr2); | ||
239 | mutex_unlock(&fmr2->lock); | ||
240 | return 0; | ||
241 | } | 63 | } |
242 | 64 | ||
243 | static int vidioc_s_tuner(struct file *file, void *priv, | 65 | static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output) |
244 | struct v4l2_tuner *v) | ||
245 | { | 66 | { |
246 | return v->index ? -EINVAL : 0; | ||
247 | } | 67 | } |
248 | 68 | ||
249 | static int vidioc_s_frequency(struct file *file, void *priv, | 69 | static struct snd_tea575x_ops fmr2_tea_ops = { |
250 | struct v4l2_frequency *f) | 70 | .set_pins = fmr2_tea575x_set_pins, |
251 | { | 71 | .get_pins = fmr2_tea575x_get_pins, |
252 | struct fmr2 *fmr2 = video_drvdata(file); | 72 | .set_direction = fmr2_tea575x_set_direction, |
73 | }; | ||
253 | 74 | ||
254 | if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) | 75 | /* TC9154A/PT2254A volume control */ |
255 | return -EINVAL; | 76 | |
256 | if (f->frequency < RSF16_MINFREQ || | 77 | /* 18-bit shift register bit definitions */ |
257 | f->frequency > RSF16_MAXFREQ) | 78 | #define TC9154A_ATT_MAJ_0DB (1 << 0) |
258 | return -EINVAL; | 79 | #define TC9154A_ATT_MAJ_10DB (1 << 1) |
259 | /* rounding in steps of 200 to match the freq | 80 | #define TC9154A_ATT_MAJ_20DB (1 << 2) |
260 | that will be used */ | 81 | #define TC9154A_ATT_MAJ_30DB (1 << 3) |
261 | fmr2->curfreq = (f->frequency / 200) * 200; | 82 | #define TC9154A_ATT_MAJ_40DB (1 << 4) |
262 | 83 | #define TC9154A_ATT_MAJ_50DB (1 << 5) | |
263 | /* set card freq (if not muted) */ | 84 | #define TC9154A_ATT_MAJ_60DB (1 << 6) |
264 | if (fmr2->curvol && !fmr2->mute) { | 85 | |
265 | mutex_lock(&fmr2->lock); | 86 | #define TC9154A_ATT_MIN_0DB (1 << 7) |
266 | fmr2_setfreq(fmr2); | 87 | #define TC9154A_ATT_MIN_2DB (1 << 8) |
267 | mutex_unlock(&fmr2->lock); | 88 | #define TC9154A_ATT_MIN_4DB (1 << 9) |
89 | #define TC9154A_ATT_MIN_6DB (1 << 10) | ||
90 | #define TC9154A_ATT_MIN_8DB (1 << 11) | ||
91 | /* bit 12 is ignored */ | ||
92 | #define TC9154A_CHANNEL_LEFT (1 << 13) | ||
93 | #define TC9154A_CHANNEL_RIGHT (1 << 14) | ||
94 | /* bits 15, 16, 17 must be 0 */ | ||
95 | |||
96 | #define TC9154A_ATT_MAJ(x) (1 << x) | ||
97 | #define TC9154A_ATT_MIN(x) (1 << (7 + x)) | ||
98 | |||
99 | static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins) | ||
100 | { | ||
101 | if (!fmr2->tea.mute) | ||
102 | pins |= STR_WREN; | ||
103 | |||
104 | outb(pins, fmr2->io); | ||
105 | } | ||
106 | |||
107 | static void tc9154a_set_attenuation(struct fmr2 *fmr2, int att, u32 channel) | ||
108 | { | ||
109 | int i; | ||
110 | u32 reg; | ||
111 | u8 bit; | ||
112 | |||
113 | reg = TC9154A_ATT_MAJ(att / 10) | TC9154A_ATT_MIN((att % 10) / 2); | ||
114 | reg |= channel; | ||
115 | /* write 18-bit shift register, LSB first */ | ||
116 | for (i = 0; i < 18; i++) { | ||
117 | bit = reg & (1 << i) ? PT_DATA : 0; | ||
118 | tc9154a_set_pins(fmr2, bit); | ||
119 | udelay(5); | ||
120 | tc9154a_set_pins(fmr2, bit | PT_CK); | ||
121 | udelay(5); | ||
122 | tc9154a_set_pins(fmr2, bit); | ||
268 | } | 123 | } |
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static int vidioc_g_frequency(struct file *file, void *priv, | ||
273 | struct v4l2_frequency *f) | ||
274 | { | ||
275 | struct fmr2 *fmr2 = video_drvdata(file); | ||
276 | |||
277 | if (f->tuner != 0) | ||
278 | return -EINVAL; | ||
279 | f->type = V4L2_TUNER_RADIO; | ||
280 | f->frequency = fmr2->curfreq; | ||
281 | return 0; | ||
282 | } | ||
283 | 124 | ||
284 | static int vidioc_queryctrl(struct file *file, void *priv, | 125 | /* latch register data */ |
285 | struct v4l2_queryctrl *qc) | 126 | udelay(5); |
286 | { | 127 | tc9154a_set_pins(fmr2, PT_ST); |
287 | struct fmr2 *fmr2 = video_drvdata(file); | 128 | udelay(5); |
288 | 129 | tc9154a_set_pins(fmr2, 0); | |
289 | switch (qc->id) { | ||
290 | case V4L2_CID_AUDIO_MUTE: | ||
291 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); | ||
292 | case V4L2_CID_AUDIO_VOLUME: | ||
293 | /* Only card_type == 11 implements volume */ | ||
294 | if (fmr2->card_type == 11) | ||
295 | return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0); | ||
296 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); | ||
297 | } | ||
298 | return -EINVAL; | ||
299 | } | 130 | } |
300 | 131 | ||
301 | static int vidioc_g_ctrl(struct file *file, void *priv, | 132 | static int fmr2_s_ctrl(struct v4l2_ctrl *ctrl) |
302 | struct v4l2_control *ctrl) | ||
303 | { | 133 | { |
304 | struct fmr2 *fmr2 = video_drvdata(file); | 134 | struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler); |
135 | struct fmr2 *fmr2 = tea->private_data; | ||
136 | int volume, balance, left, right; | ||
305 | 137 | ||
306 | switch (ctrl->id) { | 138 | switch (ctrl->id) { |
307 | case V4L2_CID_AUDIO_MUTE: | ||
308 | ctrl->value = fmr2->mute; | ||
309 | return 0; | ||
310 | case V4L2_CID_AUDIO_VOLUME: | 139 | case V4L2_CID_AUDIO_VOLUME: |
311 | ctrl->value = fmr2->curvol; | 140 | volume = ctrl->val; |
312 | return 0; | 141 | balance = fmr2->balance->cur.val; |
313 | } | ||
314 | return -EINVAL; | ||
315 | } | ||
316 | |||
317 | static int vidioc_s_ctrl(struct file *file, void *priv, | ||
318 | struct v4l2_control *ctrl) | ||
319 | { | ||
320 | struct fmr2 *fmr2 = video_drvdata(file); | ||
321 | |||
322 | switch (ctrl->id) { | ||
323 | case V4L2_CID_AUDIO_MUTE: | ||
324 | fmr2->mute = ctrl->value; | ||
325 | break; | 142 | break; |
326 | case V4L2_CID_AUDIO_VOLUME: | 143 | case V4L2_CID_AUDIO_BALANCE: |
327 | fmr2->curvol = ctrl->value; | 144 | balance = ctrl->val; |
145 | volume = fmr2->volume->cur.val; | ||
328 | break; | 146 | break; |
329 | default: | 147 | default: |
330 | return -EINVAL; | 148 | return -EINVAL; |
331 | } | 149 | } |
332 | 150 | ||
333 | #ifdef DEBUG | 151 | left = right = volume; |
334 | if (fmr2->curvol && !fmr2->mute) | 152 | if (balance < 0) |
335 | printk(KERN_DEBUG "unmute\n"); | 153 | right = max(0, right + balance); |
336 | else | 154 | if (balance > 0) |
337 | printk(KERN_DEBUG "mute\n"); | 155 | left = max(0, left - balance); |
338 | #endif | ||
339 | |||
340 | mutex_lock(&fmr2->lock); | ||
341 | if (fmr2->curvol && !fmr2->mute) { | ||
342 | fmr2_setvolume(fmr2); | ||
343 | /* Set frequency and unmute card */ | ||
344 | fmr2_setfreq(fmr2); | ||
345 | } else | ||
346 | fmr2_mute(fmr2->io); | ||
347 | mutex_unlock(&fmr2->lock); | ||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | ||
352 | { | ||
353 | *i = 0; | ||
354 | return 0; | ||
355 | } | ||
356 | 156 | ||
357 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | 157 | tc9154a_set_attenuation(fmr2, abs(left - 68), TC9154A_CHANNEL_LEFT); |
358 | { | 158 | tc9154a_set_attenuation(fmr2, abs(right - 68), TC9154A_CHANNEL_RIGHT); |
359 | return i ? -EINVAL : 0; | ||
360 | } | ||
361 | 159 | ||
362 | static int vidioc_g_audio(struct file *file, void *priv, | ||
363 | struct v4l2_audio *a) | ||
364 | { | ||
365 | a->index = 0; | ||
366 | strlcpy(a->name, "Radio", sizeof(a->name)); | ||
367 | a->capability = V4L2_AUDCAP_STEREO; | ||
368 | return 0; | 160 | return 0; |
369 | } | 161 | } |
370 | 162 | ||
371 | static int vidioc_s_audio(struct file *file, void *priv, | 163 | static const struct v4l2_ctrl_ops fmr2_ctrl_ops = { |
372 | struct v4l2_audio *a) | 164 | .s_ctrl = fmr2_s_ctrl, |
165 | }; | ||
166 | |||
167 | static int fmr2_tea_ext_init(struct snd_tea575x *tea) | ||
373 | { | 168 | { |
374 | return a->index ? -EINVAL : 0; | 169 | struct fmr2 *fmr2 = tea->private_data; |
375 | } | ||
376 | 170 | ||
377 | static const struct v4l2_file_operations fmr2_fops = { | 171 | if (inb(fmr2->io) & FMR2_HASVOL) { |
378 | .owner = THIS_MODULE, | 172 | fmr2->volume = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 56); |
379 | .unlocked_ioctl = video_ioctl2, | 173 | fmr2->balance = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_BALANCE, -68, 68, 2, 0); |
380 | }; | 174 | if (tea->ctrl_handler.error) { |
175 | printk(KERN_ERR "radio-sf16fmr2: can't initialize contrls\n"); | ||
176 | return tea->ctrl_handler.error; | ||
177 | } | ||
178 | } | ||
381 | 179 | ||
382 | static const struct v4l2_ioctl_ops fmr2_ioctl_ops = { | 180 | return 0; |
383 | .vidioc_querycap = vidioc_querycap, | 181 | } |
384 | .vidioc_g_tuner = vidioc_g_tuner, | ||
385 | .vidioc_s_tuner = vidioc_s_tuner, | ||
386 | .vidioc_g_audio = vidioc_g_audio, | ||
387 | .vidioc_s_audio = vidioc_s_audio, | ||
388 | .vidioc_g_input = vidioc_g_input, | ||
389 | .vidioc_s_input = vidioc_s_input, | ||
390 | .vidioc_g_frequency = vidioc_g_frequency, | ||
391 | .vidioc_s_frequency = vidioc_s_frequency, | ||
392 | .vidioc_queryctrl = vidioc_queryctrl, | ||
393 | .vidioc_g_ctrl = vidioc_g_ctrl, | ||
394 | .vidioc_s_ctrl = vidioc_s_ctrl, | ||
395 | }; | ||
396 | 182 | ||
397 | static int __init fmr2_init(void) | 183 | static int __init fmr2_init(void) |
398 | { | 184 | { |
399 | struct fmr2 *fmr2 = &fmr2_card; | 185 | struct fmr2 *fmr2 = &fmr2_card; |
400 | struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev; | ||
401 | int res; | ||
402 | 186 | ||
403 | strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name)); | 187 | fmr2->io = FMR2_PORT; |
404 | fmr2->io = io; | ||
405 | fmr2->stereo = 1; | ||
406 | mutex_init(&fmr2->lock); | ||
407 | 188 | ||
408 | if (!request_region(fmr2->io, 2, "sf16fmr2")) { | 189 | if (!request_region(fmr2->io, 2, "SF16-FMR2")) { |
409 | v4l2_err(v4l2_dev, "request_region failed!\n"); | 190 | printk(KERN_ERR "radio-sf16fmr2: I/O port 0x%x already in use\n", fmr2->io); |
410 | return -EBUSY; | 191 | return -EBUSY; |
411 | } | 192 | } |
412 | 193 | ||
413 | res = v4l2_device_register(NULL, v4l2_dev); | 194 | fmr2->tea.private_data = fmr2; |
414 | if (res < 0) { | 195 | fmr2->tea.ops = &fmr2_tea_ops; |
415 | release_region(fmr2->io, 2); | 196 | fmr2->tea.ext_init = fmr2_tea_ext_init; |
416 | v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); | 197 | strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card)); |
417 | return res; | 198 | strcpy(fmr2->tea.bus_info, "ISA"); |
418 | } | ||
419 | 199 | ||
420 | strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name)); | 200 | if (snd_tea575x_init(&fmr2->tea)) { |
421 | fmr2->vdev.v4l2_dev = v4l2_dev; | 201 | printk(KERN_ERR "radio-sf16fmr2: Unable to detect TEA575x tuner\n"); |
422 | fmr2->vdev.fops = &fmr2_fops; | ||
423 | fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops; | ||
424 | fmr2->vdev.release = video_device_release_empty; | ||
425 | video_set_drvdata(&fmr2->vdev, fmr2); | ||
426 | |||
427 | /* mute card - prevents noisy bootups */ | ||
428 | fmr2_mute(fmr2->io); | ||
429 | fmr2_product_info(fmr2); | ||
430 | |||
431 | if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { | ||
432 | v4l2_device_unregister(v4l2_dev); | ||
433 | release_region(fmr2->io, 2); | 202 | release_region(fmr2->io, 2); |
434 | return -EINVAL; | 203 | return -ENODEV; |
435 | } | 204 | } |
436 | 205 | ||
437 | v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io); | 206 | printk(KERN_INFO "radio-sf16fmr2: SF16-FMR2 radio card at 0x%x.\n", fmr2->io); |
438 | debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type)); | ||
439 | return 0; | 207 | return 0; |
440 | } | 208 | } |
441 | 209 | ||
@@ -443,22 +211,9 @@ static void __exit fmr2_exit(void) | |||
443 | { | 211 | { |
444 | struct fmr2 *fmr2 = &fmr2_card; | 212 | struct fmr2 *fmr2 = &fmr2_card; |
445 | 213 | ||
446 | video_unregister_device(&fmr2->vdev); | 214 | snd_tea575x_exit(&fmr2->tea); |
447 | v4l2_device_unregister(&fmr2->v4l2_dev); | ||
448 | release_region(fmr2->io, 2); | 215 | release_region(fmr2->io, 2); |
449 | } | 216 | } |
450 | 217 | ||
451 | module_init(fmr2_init); | 218 | module_init(fmr2_init); |
452 | module_exit(fmr2_exit); | 219 | module_exit(fmr2_exit); |
453 | |||
454 | #ifndef MODULE | ||
455 | |||
456 | static int __init fmr2_setup_io(char *str) | ||
457 | { | ||
458 | get_option(&str, &io); | ||
459 | return 1; | ||
460 | } | ||
461 | |||
462 | __setup("sf16fmr2=", fmr2_setup_io); | ||
463 | |||
464 | #endif | ||