bdbb0c11b3a900b6a3301d4774aa54fbfb0328ed
[safe/jmp/linux-2.6] / drivers / media / video / tlg2300 / pd-radio.c
1 #include <linux/init.h>
2 #include <linux/list.h>
3 #include <linux/module.h>
4 #include <linux/kernel.h>
5 #include <linux/bitmap.h>
6 #include <linux/usb.h>
7 #include <linux/i2c.h>
8 #include <media/v4l2-dev.h>
9 #include <linux/version.h>
10 #include <linux/mm.h>
11 #include <linux/mutex.h>
12 #include <media/v4l2-ioctl.h>
13 #include <linux/sched.h>
14
15 #include "pd-common.h"
16 #include "vendorcmds.h"
17
18 static int set_frequency(struct poseidon *p, __u32 frequency);
19 static int poseidon_fm_close(struct file *filp);
20 static int poseidon_fm_open(struct file *filp);
21
22 #define TUNER_FREQ_MIN_FM 76000000
23 #define TUNER_FREQ_MAX_FM 108000000
24
25 static int poseidon_check_mode_radio(struct poseidon *p)
26 {
27         int ret, radiomode;
28         u32 status;
29
30         set_current_state(TASK_INTERRUPTIBLE);
31         schedule_timeout(HZ/2);
32         ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE);
33         if (ret < 0)
34                 goto out;
35
36         ret = set_tuner_mode(p, TLG_MODE_FM_RADIO);
37         if (ret != 0)
38                 goto out;
39
40         ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status);
41         radiomode = get_audio_std(TLG_MODE_FM_RADIO, p->country_code);
42         ret = send_set_req(p, TUNER_AUD_ANA_STD, radiomode, &status);
43         ret |= send_set_req(p, TUNER_AUD_MODE,
44                                 TLG_TUNE_TVAUDIO_MODE_STEREO, &status);
45         ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL,
46                                 ATV_AUDIO_RATE_48K, &status);
47         ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status);
48 out:
49         return ret;
50 }
51
52 #ifdef CONFIG_PM
53 static int pm_fm_suspend(struct poseidon *p)
54 {
55         logpm(p);
56         pm_alsa_suspend(p);
57         usb_set_interface(p->udev, 0, 0);
58         msleep(300);
59         return 0;
60 }
61
62 static int pm_fm_resume(struct poseidon *p)
63 {
64         logpm(p);
65         poseidon_check_mode_radio(p);
66         set_frequency(p, p->radio_data.fm_freq);
67         pm_alsa_resume(p);
68         return 0;
69 }
70 #endif
71
72 static int poseidon_fm_open(struct file *filp)
73 {
74         struct video_device *vfd = video_devdata(filp);
75         struct poseidon *p = video_get_drvdata(vfd);
76         int ret = 0;
77
78         if (!p)
79                 return -1;
80
81         mutex_lock(&p->lock);
82         if (p->state & POSEIDON_STATE_DISCONNECT) {
83                 ret = -ENODEV;
84                 goto out;
85         }
86
87         if (p->state && !(p->state & POSEIDON_STATE_FM)) {
88                 ret = -EBUSY;
89                 goto out;
90         }
91
92         usb_autopm_get_interface(p->interface);
93         if (0 == p->state) {
94                 p->country_code = country_code;
95                 set_debug_mode(vfd, debug_mode);
96
97                 ret = poseidon_check_mode_radio(p);
98                 if (ret < 0) {
99                         usb_autopm_put_interface(p->interface);
100                         goto out;
101                 }
102                 p->state |= POSEIDON_STATE_FM;
103         }
104         p->radio_data.users++;
105         kref_get(&p->kref);
106         filp->private_data = p;
107 out:
108         mutex_unlock(&p->lock);
109         return ret;
110 }
111
112 static int poseidon_fm_close(struct file *filp)
113 {
114         struct poseidon *p = filp->private_data;
115         struct radio_data *fm = &p->radio_data;
116         uint32_t status;
117
118         mutex_lock(&p->lock);
119         fm->users--;
120         if (0 == fm->users)
121                 p->state &= ~POSEIDON_STATE_FM;
122
123         if (fm->is_radio_streaming && filp == p->file_for_stream) {
124                 fm->is_radio_streaming = 0;
125                 send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status);
126         }
127         usb_autopm_put_interface(p->interface);
128         mutex_unlock(&p->lock);
129
130         kref_put(&p->kref, poseidon_delete);
131         filp->private_data = NULL;
132         return 0;
133 }
134
135 static int vidioc_querycap(struct file *file, void *priv,
136                         struct v4l2_capability *v)
137 {
138         struct poseidon *p = file->private_data;
139
140         strlcpy(v->driver, "tele-radio", sizeof(v->driver));
141         strlcpy(v->card, "Telegent Poseidon", sizeof(v->card));
142         usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info));
143         v->version = KERNEL_VERSION(0, 0, 1);
144         v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
145         return 0;
146 }
147
148 static const struct v4l2_file_operations poseidon_fm_fops = {
149         .owner         = THIS_MODULE,
150         .open          = poseidon_fm_open,
151         .release       = poseidon_fm_close,
152         .ioctl         = video_ioctl2,
153 };
154
155 int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
156 {
157         struct tuner_fm_sig_stat_s fm_stat = {};
158         int ret, status, count = 5;
159         struct poseidon *p = file->private_data;
160
161         if (vt->index != 0)
162                 return -EINVAL;
163
164         vt->type        = V4L2_TUNER_RADIO;
165         vt->capability  = V4L2_TUNER_CAP_STEREO;
166         vt->rangelow    = TUNER_FREQ_MIN_FM / 62500;
167         vt->rangehigh   = TUNER_FREQ_MAX_FM / 62500;
168         vt->rxsubchans  = V4L2_TUNER_SUB_STEREO;
169         vt->audmode     = V4L2_TUNER_MODE_STEREO;
170         vt->signal      = 0;
171         vt->afc         = 0;
172
173         mutex_lock(&p->lock);
174         ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
175                               &fm_stat, &status, sizeof(fm_stat));
176
177         while (fm_stat.sig_lock_busy && count-- && !ret) {
178                 set_current_state(TASK_INTERRUPTIBLE);
179                 schedule_timeout(HZ);
180
181                 ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
182                                   &fm_stat, &status, sizeof(fm_stat));
183         }
184         mutex_unlock(&p->lock);
185
186         if (ret || status) {
187                 vt->signal = 0;
188         } else if ((fm_stat.sig_present || fm_stat.sig_locked)
189                         && fm_stat.sig_strength == 0) {
190                 vt->signal = 0xffff;
191         } else
192                 vt->signal = (fm_stat.sig_strength * 255 / 10) << 8;
193
194         return 0;
195 }
196
197 int fm_get_freq(struct file *file, void *priv, struct v4l2_frequency *argp)
198 {
199         struct poseidon *p = file->private_data;
200
201         argp->frequency = p->radio_data.fm_freq;
202         return 0;
203 }
204
205 static int set_frequency(struct poseidon *p, __u32 frequency)
206 {
207         __u32 freq ;
208         int ret, status, radiomode;
209
210         mutex_lock(&p->lock);
211
212         radiomode = get_audio_std(TLG_MODE_FM_RADIO, p->country_code);
213         /*NTSC 8,PAL 2 */
214         ret = send_set_req(p, TUNER_AUD_ANA_STD, radiomode, &status);
215
216         freq =  (frequency * 125) * 500 / 1000;/* kHZ */
217         if (freq < TUNER_FREQ_MIN_FM/1000 || freq > TUNER_FREQ_MAX_FM/1000) {
218                 ret = -EINVAL;
219                 goto error;
220         }
221
222         ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status);
223         if (ret < 0)
224                 goto error ;
225         ret = send_set_req(p, TAKE_REQUEST, 0, &status);
226
227         set_current_state(TASK_INTERRUPTIBLE);
228         schedule_timeout(HZ/4);
229         if (!p->radio_data.is_radio_streaming) {
230                 ret = send_set_req(p, TAKE_REQUEST, 0, &status);
231                 ret = send_set_req(p, PLAY_SERVICE,
232                                 TLG_TUNE_PLAY_SVC_START, &status);
233                 p->radio_data.is_radio_streaming = 1;
234         }
235         p->radio_data.fm_freq = frequency;
236 error:
237         mutex_unlock(&p->lock);
238         return ret;
239 }
240
241 int fm_set_freq(struct file *file, void *priv, struct v4l2_frequency *argp)
242 {
243         struct poseidon *p = file->private_data;
244
245         p->file_for_stream  = file;
246 #ifdef CONFIG_PM
247         p->pm_suspend = pm_fm_suspend;
248         p->pm_resume  = pm_fm_resume;
249 #endif
250         return set_frequency(p, argp->frequency);
251 }
252
253 int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv,
254                 struct v4l2_control *arg)
255 {
256     return 0;
257 }
258
259 int tlg_fm_vidioc_exts_ctrl(struct file *file, void *fh,
260                 struct v4l2_ext_controls *a)
261 {
262     return 0;
263 }
264
265 int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv,
266                 struct v4l2_control *arg)
267 {
268     return 0;
269 }
270
271 int tlg_fm_vidioc_queryctrl(struct file *file, void *priv,
272                 struct v4l2_queryctrl *arg)
273 {
274         arg->minimum = 0;
275         arg->maximum = 65535;
276         return 0;
277 }
278
279 static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
280 {
281         return vt->index > 0 ? -EINVAL : 0;
282 }
283 static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *va)
284 {
285         return (va->index != 0) ? -EINVAL : 0;
286 }
287
288 static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
289 {
290         a->index    = 0;
291         a->mode    = 0;
292         a->capability = V4L2_AUDCAP_STEREO;
293         strcpy(a->name, "Radio");
294         return 0;
295 }
296
297 static int vidioc_s_input(struct file *filp, void *priv, u32 i)
298 {
299         return (i != 0) ? -EINVAL : 0;
300 }
301
302 static int vidioc_g_input(struct file *filp, void *priv, u32 *i)
303 {
304         return (*i != 0) ? -EINVAL : 0;
305 }
306
307 static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = {
308         .vidioc_querycap    = vidioc_querycap,
309         .vidioc_g_audio     = vidioc_g_audio,
310         .vidioc_s_audio     = vidioc_s_audio,
311         .vidioc_g_input     = vidioc_g_input,
312         .vidioc_s_input     = vidioc_s_input,
313         .vidioc_queryctrl   = tlg_fm_vidioc_queryctrl,
314         .vidioc_g_ctrl      = tlg_fm_vidioc_g_ctrl,
315         .vidioc_s_ctrl      = tlg_fm_vidioc_s_ctrl,
316         .vidioc_s_ext_ctrls = tlg_fm_vidioc_exts_ctrl,
317         .vidioc_s_tuner     = vidioc_s_tuner,
318         .vidioc_g_tuner     = tlg_fm_vidioc_g_tuner,
319         .vidioc_g_frequency = fm_get_freq,
320         .vidioc_s_frequency = fm_set_freq,
321 };
322
323 static struct video_device poseidon_fm_template = {
324         .name       = "Telegent-Radio",
325         .fops       = &poseidon_fm_fops,
326         .minor      = -1,
327         .release    = video_device_release,
328         .ioctl_ops  = &poseidon_fm_ioctl_ops,
329 };
330
331 int poseidon_fm_init(struct poseidon *p)
332 {
333         struct video_device *fm_dev;
334
335         fm_dev = vdev_init(p, &poseidon_fm_template);
336         if (fm_dev == NULL)
337                 return -1;
338
339         if (video_register_device(fm_dev, VFL_TYPE_RADIO, -1) < 0) {
340                 video_device_release(fm_dev);
341                 return -1;
342         }
343         p->radio_data.fm_dev = fm_dev;
344         return 0;
345 }
346
347 int poseidon_fm_exit(struct poseidon *p)
348 {
349         destroy_video_device(&p->radio_data.fm_dev);
350         return 0;
351 }