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>
8 #include <media/v4l2-dev.h>
9 #include <linux/version.h>
11 #include <linux/mutex.h>
12 #include <media/v4l2-ioctl.h>
13 #include <linux/sched.h>
15 #include "pd-common.h"
16 #include "vendorcmds.h"
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);
22 #define TUNER_FREQ_MIN_FM 76000000
23 #define TUNER_FREQ_MAX_FM 108000000
25 static int poseidon_check_mode_radio(struct poseidon *p)
30 set_current_state(TASK_INTERRUPTIBLE);
31 schedule_timeout(HZ/2);
32 ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE);
36 ret = set_tuner_mode(p, TLG_MODE_FM_RADIO);
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);
53 static int pm_fm_suspend(struct poseidon *p)
57 usb_set_interface(p->udev, 0, 0);
62 static int pm_fm_resume(struct poseidon *p)
65 poseidon_check_mode_radio(p);
66 set_frequency(p, p->radio_data.fm_freq);
72 static int poseidon_fm_open(struct file *filp)
74 struct video_device *vfd = video_devdata(filp);
75 struct poseidon *p = video_get_drvdata(vfd);
82 if (p->state & POSEIDON_STATE_DISCONNECT) {
87 if (p->state && !(p->state & POSEIDON_STATE_FM)) {
92 usb_autopm_get_interface(p->interface);
94 p->country_code = country_code;
95 set_debug_mode(vfd, debug_mode);
97 ret = poseidon_check_mode_radio(p);
99 usb_autopm_put_interface(p->interface);
102 p->state |= POSEIDON_STATE_FM;
104 p->radio_data.users++;
106 filp->private_data = p;
108 mutex_unlock(&p->lock);
112 static int poseidon_fm_close(struct file *filp)
114 struct poseidon *p = filp->private_data;
115 struct radio_data *fm = &p->radio_data;
118 mutex_lock(&p->lock);
121 p->state &= ~POSEIDON_STATE_FM;
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);
127 usb_autopm_put_interface(p->interface);
128 mutex_unlock(&p->lock);
130 kref_put(&p->kref, poseidon_delete);
131 filp->private_data = NULL;
135 static int vidioc_querycap(struct file *file, void *priv,
136 struct v4l2_capability *v)
138 struct poseidon *p = file->private_data;
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;
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,
155 int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
157 struct tuner_fm_sig_stat_s fm_stat = {};
158 int ret, status, count = 5;
159 struct poseidon *p = file->private_data;
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;
173 mutex_lock(&p->lock);
174 ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
175 &fm_stat, &status, sizeof(fm_stat));
177 while (fm_stat.sig_lock_busy && count-- && !ret) {
178 set_current_state(TASK_INTERRUPTIBLE);
179 schedule_timeout(HZ);
181 ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
182 &fm_stat, &status, sizeof(fm_stat));
184 mutex_unlock(&p->lock);
188 } else if ((fm_stat.sig_present || fm_stat.sig_locked)
189 && fm_stat.sig_strength == 0) {
192 vt->signal = (fm_stat.sig_strength * 255 / 10) << 8;
197 int fm_get_freq(struct file *file, void *priv, struct v4l2_frequency *argp)
199 struct poseidon *p = file->private_data;
201 argp->frequency = p->radio_data.fm_freq;
205 static int set_frequency(struct poseidon *p, __u32 frequency)
208 int ret, status, radiomode;
210 mutex_lock(&p->lock);
212 radiomode = get_audio_std(TLG_MODE_FM_RADIO, p->country_code);
214 ret = send_set_req(p, TUNER_AUD_ANA_STD, radiomode, &status);
216 freq = (frequency * 125) * 500 / 1000;/* kHZ */
217 if (freq < TUNER_FREQ_MIN_FM/1000 || freq > TUNER_FREQ_MAX_FM/1000) {
222 ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status);
225 ret = send_set_req(p, TAKE_REQUEST, 0, &status);
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;
235 p->radio_data.fm_freq = frequency;
237 mutex_unlock(&p->lock);
241 int fm_set_freq(struct file *file, void *priv, struct v4l2_frequency *argp)
243 struct poseidon *p = file->private_data;
245 p->file_for_stream = file;
247 p->pm_suspend = pm_fm_suspend;
248 p->pm_resume = pm_fm_resume;
250 return set_frequency(p, argp->frequency);
253 int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv,
254 struct v4l2_control *arg)
259 int tlg_fm_vidioc_exts_ctrl(struct file *file, void *fh,
260 struct v4l2_ext_controls *a)
265 int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv,
266 struct v4l2_control *arg)
271 int tlg_fm_vidioc_queryctrl(struct file *file, void *priv,
272 struct v4l2_queryctrl *arg)
275 arg->maximum = 65535;
279 static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
281 return vt->index > 0 ? -EINVAL : 0;
283 static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *va)
285 return (va->index != 0) ? -EINVAL : 0;
288 static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
292 a->capability = V4L2_AUDCAP_STEREO;
293 strcpy(a->name, "Radio");
297 static int vidioc_s_input(struct file *filp, void *priv, u32 i)
299 return (i != 0) ? -EINVAL : 0;
302 static int vidioc_g_input(struct file *filp, void *priv, u32 *i)
304 return (*i != 0) ? -EINVAL : 0;
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,
323 static struct video_device poseidon_fm_template = {
324 .name = "Telegent-Radio",
325 .fops = &poseidon_fm_fops,
327 .release = video_device_release,
328 .ioctl_ops = &poseidon_fm_ioctl_ops,
331 int poseidon_fm_init(struct poseidon *p)
333 struct video_device *fm_dev;
335 fm_dev = vdev_init(p, &poseidon_fm_template);
339 if (video_register_device(fm_dev, VFL_TYPE_RADIO, -1) < 0) {
340 video_device_release(fm_dev);
343 p->radio_data.fm_dev = fm_dev;
347 int poseidon_fm_exit(struct poseidon *p)
349 destroy_video_device(&p->radio_data.fm_dev);