V4L/DVB: Add driver for Telegent tlg2300
pd-common.h contains the common data structures, while vendorcmds.h contains the vendor commands for firmware. [mchehab@redhat.com: Folded the 10 patches with the driver] Signed-off-by: Huang Shijie <shijie8@gmail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
433763faec
commit
5b3f03f044
13 changed files with 4280 additions and 0 deletions
231
Documentation/video4linux/README.tlg2300
Normal file
231
Documentation/video4linux/README.tlg2300
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
tlg2300 release notes
|
||||||
|
====================
|
||||||
|
|
||||||
|
This is a v4l2/dvb device driver for the tlg2300 chip.
|
||||||
|
|
||||||
|
|
||||||
|
current status
|
||||||
|
==============
|
||||||
|
|
||||||
|
video
|
||||||
|
- support mmap and read().(no overlay)
|
||||||
|
|
||||||
|
audio
|
||||||
|
- The driver will register a ALSA card for the audio input.
|
||||||
|
|
||||||
|
vbi
|
||||||
|
- Works for almost TV norms.
|
||||||
|
|
||||||
|
dvb-t
|
||||||
|
- works for DVB-T
|
||||||
|
|
||||||
|
FM
|
||||||
|
- Works for radio.
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
TESTED APPLICATIONS:
|
||||||
|
|
||||||
|
-VLC1.0.4 test the video and dvb. The GUI is friendly to use.
|
||||||
|
|
||||||
|
-Mplayer test the video.
|
||||||
|
|
||||||
|
-Mplayer test the FM. The mplayer should be compiled with --enable-radio and
|
||||||
|
--enable-radio-capture.
|
||||||
|
The command runs as this(The alsa audio registers to card 1):
|
||||||
|
#mplayer radio://103.7/capture/ -radio adevice=hw=1,0:arate=48000 \
|
||||||
|
-rawaudio rate=48000:channels=2
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
KNOWN PROBLEMS:
|
||||||
|
|
||||||
|
country code
|
||||||
|
- The firmware of the chip needs the country code to determine
|
||||||
|
the stardards of video and audio when it runs for analog TV or radio.
|
||||||
|
The DVB-T does not need the country code.
|
||||||
|
|
||||||
|
So you must set the country-code correctly. The V4L2 does not have
|
||||||
|
the interface,the driver has to provide a parameter `country_code'.
|
||||||
|
|
||||||
|
You could set the coutry code in two ways, take USA as example
|
||||||
|
(The USA's country code is 1):
|
||||||
|
|
||||||
|
[1] add the following line in /etc/modprobe.conf before you insert the
|
||||||
|
card into USB hub's port :
|
||||||
|
poseidon country_code=1
|
||||||
|
|
||||||
|
[2] You can also modify the parameter at runtime (before you run the
|
||||||
|
application such as VLC)
|
||||||
|
#echo 1 > /sys/module/poseidon/parameter/country_code
|
||||||
|
|
||||||
|
The known country codes show below:
|
||||||
|
country code : country
|
||||||
|
93 "Afghanistan"
|
||||||
|
355 "Albania"
|
||||||
|
213 "Algeria"
|
||||||
|
684 "American Samoa"
|
||||||
|
376 "Andorra"
|
||||||
|
244 "Angola"
|
||||||
|
54 "Argentina"
|
||||||
|
374 "Armenia"
|
||||||
|
61 "Australia"
|
||||||
|
43 "Austria"
|
||||||
|
994 "Azerbaijan"
|
||||||
|
973 "Bahrain"
|
||||||
|
880 "Bangladesh"
|
||||||
|
375 "Belarus"
|
||||||
|
32 "Belgium"
|
||||||
|
501 "Belize"
|
||||||
|
229 "Benin"
|
||||||
|
591 "Bolivia"
|
||||||
|
387 "Bosnia and Herzegovina"
|
||||||
|
267 "Botswana"
|
||||||
|
55 "Brazil"
|
||||||
|
673 "Brunei Darussalam"
|
||||||
|
359 "Bulgalia"
|
||||||
|
226 "Burkina Faso"
|
||||||
|
257 "Burundi"
|
||||||
|
237 "Cameroon"
|
||||||
|
1 "Canada"
|
||||||
|
236 "Central African Republic"
|
||||||
|
235 "Chad"
|
||||||
|
56 "Chile"
|
||||||
|
86 "China"
|
||||||
|
57 "Colombia"
|
||||||
|
242 "Congo"
|
||||||
|
243 "Congo, Dem. Rep. of "
|
||||||
|
506 "Costa Rica"
|
||||||
|
385 "Croatia"
|
||||||
|
53 "Cuba or Guantanamo Bay"
|
||||||
|
357 "Cyprus"
|
||||||
|
420 "Czech Republic"
|
||||||
|
45 "Denmark"
|
||||||
|
246 "Diego Garcia"
|
||||||
|
253 "Djibouti"
|
||||||
|
593 "Ecuador"
|
||||||
|
20 "Egypt"
|
||||||
|
503 "El Salvador"
|
||||||
|
240 "Equatorial Guinea"
|
||||||
|
372 "Estonia"
|
||||||
|
251 "Ethiopia"
|
||||||
|
358 "Finland"
|
||||||
|
33 "France"
|
||||||
|
594 "French Guiana"
|
||||||
|
689 "French Polynesia"
|
||||||
|
241 "Gabonese Republic"
|
||||||
|
220 "Gambia"
|
||||||
|
995 "Georgia"
|
||||||
|
49 "Germany"
|
||||||
|
233 "Ghana"
|
||||||
|
350 "Gibraltar"
|
||||||
|
30 "Greece"
|
||||||
|
299 "Greenland"
|
||||||
|
671 "Guam"
|
||||||
|
502 "Guatemala"
|
||||||
|
592 "Guyana"
|
||||||
|
509 "Haiti"
|
||||||
|
504 "Honduras"
|
||||||
|
852 "Hong Kong SAR, China"
|
||||||
|
36 "Hungary"
|
||||||
|
354 "Iceland"
|
||||||
|
91 "India"
|
||||||
|
98 "Iran"
|
||||||
|
964 "Iraq"
|
||||||
|
353 "Ireland"
|
||||||
|
972 "Israel"
|
||||||
|
39 "Italy or Vatican City"
|
||||||
|
225 "Ivory Coast"
|
||||||
|
81 "Japan"
|
||||||
|
962 "Jordan"
|
||||||
|
7 "Kazakhstan or Kyrgyzstan"
|
||||||
|
254 "Kenya"
|
||||||
|
686 "Kiribati"
|
||||||
|
965 "Kuwait"
|
||||||
|
856 "Laos"
|
||||||
|
371 "Latvia"
|
||||||
|
961 "Lebanon"
|
||||||
|
266 "Lesotho"
|
||||||
|
231 "Liberia"
|
||||||
|
218 "Libya"
|
||||||
|
41 "Liechtenstein or Switzerland"
|
||||||
|
370 "Lithuania"
|
||||||
|
352 "Luxembourg"
|
||||||
|
853 "Macau SAR, China"
|
||||||
|
261 "Madagascar"
|
||||||
|
60 "Malaysia"
|
||||||
|
960 "Maldives"
|
||||||
|
223 "Mali Republic"
|
||||||
|
356 "Malta"
|
||||||
|
692 "Marshall Islands"
|
||||||
|
596 "Martinique"
|
||||||
|
222 "Mauritania"
|
||||||
|
230 "Mauritus"
|
||||||
|
52 "Mexico"
|
||||||
|
691 "Micronesia"
|
||||||
|
373 "Moldova"
|
||||||
|
377 "Monaco"
|
||||||
|
976 "Mongolia"
|
||||||
|
212 "Morocco"
|
||||||
|
258 "Mozambique"
|
||||||
|
95 "Myanmar"
|
||||||
|
264 "Namibia"
|
||||||
|
674 "Nauru"
|
||||||
|
31 "Netherlands"
|
||||||
|
687 "New Caledonia"
|
||||||
|
64 "New Zealand"
|
||||||
|
505 "Nicaragua"
|
||||||
|
227 "Niger"
|
||||||
|
234 "Nigeria"
|
||||||
|
850 "North Korea"
|
||||||
|
47 "Norway"
|
||||||
|
968 "Oman"
|
||||||
|
92 "Pakistan"
|
||||||
|
680 "Palau"
|
||||||
|
507 "Panama"
|
||||||
|
675 "Papua New Guinea"
|
||||||
|
595 "Paraguay"
|
||||||
|
51 "Peru"
|
||||||
|
63 "Philippines"
|
||||||
|
48 "Poland"
|
||||||
|
351 "Portugal"
|
||||||
|
974 "Qatar"
|
||||||
|
262 "Reunion Island"
|
||||||
|
40 "Romania"
|
||||||
|
7 "Russia"
|
||||||
|
378 "San Marino"
|
||||||
|
239 "Sao Tome and Principe"
|
||||||
|
966 "Saudi Arabia"
|
||||||
|
221 "Senegal"
|
||||||
|
248 "Seychelles Republic"
|
||||||
|
232 "Sierra Leone"
|
||||||
|
65 "Singapore"
|
||||||
|
421 "Slovak Republic"
|
||||||
|
386 "Slovenia"
|
||||||
|
27 "South Africa"
|
||||||
|
82 "South Korea "
|
||||||
|
34 "Spain"
|
||||||
|
94 "Sri Lanka"
|
||||||
|
508 "St. Pierre and Miquelon"
|
||||||
|
249 "Sudan"
|
||||||
|
597 "Suriname"
|
||||||
|
268 "Swaziland"
|
||||||
|
46 "Sweden"
|
||||||
|
963 "Syria"
|
||||||
|
886 "Taiwan Region"
|
||||||
|
255 "Tanzania"
|
||||||
|
66 "Thailand"
|
||||||
|
228 "Togolese Republic"
|
||||||
|
216 "Tunisia"
|
||||||
|
90 "Turkey"
|
||||||
|
993 "Turkmenistan"
|
||||||
|
256 "Uganda"
|
||||||
|
380 "Ukraine"
|
||||||
|
971 "United Arab Emirates"
|
||||||
|
44 "United Kingdom"
|
||||||
|
1 "United States of America"
|
||||||
|
598 "Uruguay"
|
||||||
|
58 "Venezuela"
|
||||||
|
84 "Vietnam"
|
||||||
|
967 "Yemen"
|
||||||
|
260 "Zambia"
|
||||||
|
255 "Zanzibar"
|
||||||
|
263 "Zimbabwe"
|
|
@ -4676,6 +4676,14 @@ F: drivers/media/common/saa7146*
|
||||||
F: drivers/media/video/*7146*
|
F: drivers/media/video/*7146*
|
||||||
F: include/media/*7146*
|
F: include/media/*7146*
|
||||||
|
|
||||||
|
TLG2300 VIDEO4LINUX-2 DRIVER
|
||||||
|
M Huang Shijie <shijie8@gmail.com>
|
||||||
|
M Kang Yong <kangyong@telegent.com>
|
||||||
|
M Zhang Xiaobing <xbzhang@telegent.com>
|
||||||
|
S: Supported
|
||||||
|
F: drivers/media/video/tlg2300
|
||||||
|
|
||||||
|
|
||||||
SC1200 WDT DRIVER
|
SC1200 WDT DRIVER
|
||||||
M: Zwane Mwaikambo <zwane@arm.linux.org.uk>
|
M: Zwane Mwaikambo <zwane@arm.linux.org.uk>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
|
|
@ -949,6 +949,8 @@ source "drivers/media/video/hdpvr/Kconfig"
|
||||||
|
|
||||||
source "drivers/media/video/em28xx/Kconfig"
|
source "drivers/media/video/em28xx/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/media/video/tlg2300/Kconfig"
|
||||||
|
|
||||||
source "drivers/media/video/cx231xx/Kconfig"
|
source "drivers/media/video/cx231xx/Kconfig"
|
||||||
|
|
||||||
source "drivers/media/video/usbvision/Kconfig"
|
source "drivers/media/video/usbvision/Kconfig"
|
||||||
|
|
|
@ -99,6 +99,7 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o
|
||||||
obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
|
obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
|
||||||
obj-$(CONFIG_VIDEO_CX88) += cx88/
|
obj-$(CONFIG_VIDEO_CX88) += cx88/
|
||||||
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
|
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
|
||||||
|
obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
|
||||||
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
|
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
|
||||||
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
|
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
|
||||||
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
|
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
|
||||||
|
|
16
drivers/media/video/tlg2300/Kconfig
Normal file
16
drivers/media/video/tlg2300/Kconfig
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
config VIDEO_TLG2300
|
||||||
|
tristate "Telegent TLG2300 USB video capture support"
|
||||||
|
depends on VIDEO_DEV && I2C && INPUT && SND && DVB_CORE
|
||||||
|
select VIDEO_TUNER
|
||||||
|
select VIDEO_TVEEPROM
|
||||||
|
select VIDEO_IR
|
||||||
|
select VIDEOBUF_VMALLOC
|
||||||
|
select SND_PCM
|
||||||
|
select VIDEOBUF_DVB
|
||||||
|
|
||||||
|
---help---
|
||||||
|
This is a video4linux driver for Telegent tlg2300 based TV cards.
|
||||||
|
The driver supports V4L2, DVB-T and radio.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called poseidon
|
9
drivers/media/video/tlg2300/Makefile
Normal file
9
drivers/media/video/tlg2300/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o
|
||||||
|
|
||||||
|
EXTRA_CFLAGS += -Idrivers/media/video
|
||||||
|
EXTRA_CFLAGS += -Idrivers/media/common/tuners
|
||||||
|
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
|
||||||
|
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
|
||||||
|
|
332
drivers/media/video/tlg2300/pd-alsa.c
Normal file
332
drivers/media/video/tlg2300/pd-alsa.c
Normal file
|
@ -0,0 +1,332 @@
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/usb.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/sound.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/soundcard.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/vmalloc.h>
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <sound/core.h>
|
||||||
|
#include <sound/pcm.h>
|
||||||
|
#include <sound/pcm_params.h>
|
||||||
|
#include <sound/info.h>
|
||||||
|
#include <sound/initval.h>
|
||||||
|
#include <sound/control.h>
|
||||||
|
#include <media/v4l2-common.h>
|
||||||
|
#include "pd-common.h"
|
||||||
|
#include "vendorcmds.h"
|
||||||
|
|
||||||
|
static void complete_handler_audio(struct urb *urb);
|
||||||
|
#define AUDIO_EP (0x83)
|
||||||
|
#define AUDIO_BUF_SIZE (512)
|
||||||
|
#define PERIOD_SIZE (1024 * 8)
|
||||||
|
#define PERIOD_MIN (4)
|
||||||
|
#define PERIOD_MAX PERIOD_MIN
|
||||||
|
|
||||||
|
static struct snd_pcm_hardware snd_pd_hw_capture = {
|
||||||
|
.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||||
|
SNDRV_PCM_INFO_MMAP |
|
||||||
|
SNDRV_PCM_INFO_INTERLEAVED |
|
||||||
|
SNDRV_PCM_INFO_MMAP_VALID,
|
||||||
|
|
||||||
|
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||||
|
.rates = SNDRV_PCM_RATE_48000,
|
||||||
|
|
||||||
|
.rate_min = 48000,
|
||||||
|
.rate_max = 48000,
|
||||||
|
.channels_min = 2,
|
||||||
|
.channels_max = 2,
|
||||||
|
.buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN,
|
||||||
|
.period_bytes_min = PERIOD_SIZE,
|
||||||
|
.period_bytes_max = PERIOD_SIZE,
|
||||||
|
.periods_min = PERIOD_MIN,
|
||||||
|
.periods_max = PERIOD_MAX,
|
||||||
|
/*
|
||||||
|
.buffer_bytes_max = 62720 * 8,
|
||||||
|
.period_bytes_min = 64,
|
||||||
|
.period_bytes_max = 12544,
|
||||||
|
.periods_min = 2,
|
||||||
|
.periods_max = 98
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
static int snd_pd_capture_open(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct poseidon *p = snd_pcm_substream_chip(substream);
|
||||||
|
struct poseidon_audio *pa = &p->audio;
|
||||||
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
|
|
||||||
|
if (!p)
|
||||||
|
return -ENODEV;
|
||||||
|
pa->users++;
|
||||||
|
pa->card_close = 0;
|
||||||
|
pa->capture_pcm_substream = substream;
|
||||||
|
runtime->private_data = p;
|
||||||
|
|
||||||
|
runtime->hw = snd_pd_hw_capture;
|
||||||
|
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
|
||||||
|
usb_autopm_get_interface(p->interface);
|
||||||
|
kref_get(&p->kref);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_pd_pcm_close(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct poseidon *p = snd_pcm_substream_chip(substream);
|
||||||
|
struct poseidon_audio *pa = &p->audio;
|
||||||
|
|
||||||
|
pa->users--;
|
||||||
|
pa->card_close = 1;
|
||||||
|
usb_autopm_put_interface(p->interface);
|
||||||
|
kref_put(&p->kref, poseidon_delete);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_pcm_hw_params *hw_params)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
|
unsigned int size;
|
||||||
|
|
||||||
|
size = params_buffer_bytes(hw_params);
|
||||||
|
if (runtime->dma_area) {
|
||||||
|
if (runtime->dma_bytes > size)
|
||||||
|
return 0;
|
||||||
|
vfree(runtime->dma_area);
|
||||||
|
}
|
||||||
|
runtime->dma_area = vmalloc(size);
|
||||||
|
if (!runtime->dma_area)
|
||||||
|
return -ENOMEM;
|
||||||
|
else
|
||||||
|
runtime->dma_bytes = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int audio_buf_free(struct poseidon *p)
|
||||||
|
{
|
||||||
|
struct poseidon_audio *pa = &p->audio;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < AUDIO_BUFS; i++)
|
||||||
|
if (pa->urb_array[i])
|
||||||
|
usb_kill_urb(pa->urb_array[i]);
|
||||||
|
free_all_urb_generic(pa->urb_array, AUDIO_BUFS);
|
||||||
|
logpm();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct poseidon *p = snd_pcm_substream_chip(substream);
|
||||||
|
|
||||||
|
logpm();
|
||||||
|
audio_buf_free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_pd_prepare(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define AUDIO_TRAILER_SIZE (16)
|
||||||
|
static inline void handle_audio_data(struct urb *urb, int *period_elapsed)
|
||||||
|
{
|
||||||
|
struct poseidon_audio *pa = urb->context;
|
||||||
|
struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime;
|
||||||
|
|
||||||
|
int stride = runtime->frame_bits >> 3;
|
||||||
|
int len = urb->actual_length / stride;
|
||||||
|
unsigned char *cp = urb->transfer_buffer;
|
||||||
|
unsigned int oldptr = pa->rcv_position;
|
||||||
|
|
||||||
|
if (urb->actual_length == AUDIO_BUF_SIZE - 4)
|
||||||
|
len -= (AUDIO_TRAILER_SIZE / stride);
|
||||||
|
|
||||||
|
/* do the copy */
|
||||||
|
if (oldptr + len >= runtime->buffer_size) {
|
||||||
|
unsigned int cnt = runtime->buffer_size - oldptr;
|
||||||
|
|
||||||
|
memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride);
|
||||||
|
memcpy(runtime->dma_area, (cp + cnt * stride),
|
||||||
|
(len * stride - cnt * stride));
|
||||||
|
} else
|
||||||
|
memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
|
||||||
|
|
||||||
|
/* update the statas */
|
||||||
|
snd_pcm_stream_lock(pa->capture_pcm_substream);
|
||||||
|
pa->rcv_position += len;
|
||||||
|
if (pa->rcv_position >= runtime->buffer_size)
|
||||||
|
pa->rcv_position -= runtime->buffer_size;
|
||||||
|
|
||||||
|
pa->copied_position += (len);
|
||||||
|
if (pa->copied_position >= runtime->period_size) {
|
||||||
|
pa->copied_position -= runtime->period_size;
|
||||||
|
*period_elapsed = 1;
|
||||||
|
}
|
||||||
|
snd_pcm_stream_unlock(pa->capture_pcm_substream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void complete_handler_audio(struct urb *urb)
|
||||||
|
{
|
||||||
|
struct poseidon_audio *pa = urb->context;
|
||||||
|
struct snd_pcm_substream *substream = pa->capture_pcm_substream;
|
||||||
|
int period_elapsed = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (1 == pa->card_close || pa->capture_stream != STREAM_ON)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (urb->status != 0) {
|
||||||
|
/*if (urb->status == -ESHUTDOWN)*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (substream) {
|
||||||
|
if (urb->actual_length) {
|
||||||
|
handle_audio_data(urb, &period_elapsed);
|
||||||
|
if (period_elapsed)
|
||||||
|
snd_pcm_period_elapsed(substream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
|
if (ret < 0)
|
||||||
|
log("audio urb failed (errcod = %i)", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fire_audio_urb(struct poseidon *p)
|
||||||
|
{
|
||||||
|
int i, ret = 0;
|
||||||
|
struct poseidon_audio *pa = &p->audio;
|
||||||
|
|
||||||
|
alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS,
|
||||||
|
p->udev, AUDIO_EP,
|
||||||
|
AUDIO_BUF_SIZE, GFP_ATOMIC,
|
||||||
|
complete_handler_audio, pa);
|
||||||
|
|
||||||
|
for (i = 0; i < AUDIO_BUFS; i++) {
|
||||||
|
ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL);
|
||||||
|
if (ret)
|
||||||
|
log("urb err : %d", ret);
|
||||||
|
}
|
||||||
|
log();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||||
|
{
|
||||||
|
struct poseidon *p = snd_pcm_substream_chip(substream);
|
||||||
|
struct poseidon_audio *pa = &p->audio;
|
||||||
|
|
||||||
|
if (debug_mode)
|
||||||
|
log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream);
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case SNDRV_PCM_TRIGGER_RESUME:
|
||||||
|
case SNDRV_PCM_TRIGGER_START:
|
||||||
|
if (pa->capture_stream == STREAM_ON)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pa->rcv_position = pa->copied_position = 0;
|
||||||
|
pa->capture_stream = STREAM_ON;
|
||||||
|
|
||||||
|
if (in_hibernation(p))
|
||||||
|
return 0;
|
||||||
|
fire_audio_urb(p);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||||
|
pa->capture_stream = STREAM_SUSPEND;
|
||||||
|
return 0;
|
||||||
|
case SNDRV_PCM_TRIGGER_STOP:
|
||||||
|
pa->capture_stream = STREAM_OFF;
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static snd_pcm_uframes_t
|
||||||
|
snd_pd_capture_pointer(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct poseidon *p = snd_pcm_substream_chip(substream);
|
||||||
|
struct poseidon_audio *pa = &p->audio;
|
||||||
|
return pa->rcv_position;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs,
|
||||||
|
unsigned long offset)
|
||||||
|
{
|
||||||
|
void *pageptr = subs->runtime->dma_area + offset;
|
||||||
|
return vmalloc_to_page(pageptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct snd_pcm_ops pcm_capture_ops = {
|
||||||
|
.open = snd_pd_capture_open,
|
||||||
|
.close = snd_pd_pcm_close,
|
||||||
|
.ioctl = snd_pcm_lib_ioctl,
|
||||||
|
.hw_params = snd_pd_hw_capture_params,
|
||||||
|
.hw_free = snd_pd_hw_capture_free,
|
||||||
|
.prepare = snd_pd_prepare,
|
||||||
|
.trigger = snd_pd_capture_trigger,
|
||||||
|
.pointer = snd_pd_capture_pointer,
|
||||||
|
.page = snd_pcm_pd_get_page,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
int pm_alsa_suspend(struct poseidon *p)
|
||||||
|
{
|
||||||
|
logpm(p);
|
||||||
|
audio_buf_free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pm_alsa_resume(struct poseidon *p)
|
||||||
|
{
|
||||||
|
logpm(p);
|
||||||
|
fire_audio_urb(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int poseidon_audio_init(struct poseidon *p)
|
||||||
|
{
|
||||||
|
struct poseidon_audio *pa = &p->audio;
|
||||||
|
struct snd_card *card;
|
||||||
|
struct snd_pcm *pcm;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = snd_card_create(-1, "Telegent", THIS_MODULE, 0, &card);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
|
||||||
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
|
||||||
|
pcm->info_flags = 0;
|
||||||
|
pcm->private_data = p;
|
||||||
|
strcpy(pcm->name, "poseidon audio capture");
|
||||||
|
|
||||||
|
strcpy(card->driver, "ALSA driver");
|
||||||
|
strcpy(card->shortname, "poseidon Audio");
|
||||||
|
strcpy(card->longname, "poseidon ALSA Audio");
|
||||||
|
|
||||||
|
if (snd_card_register(card)) {
|
||||||
|
snd_card_free(card);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
pa->card = card;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int poseidon_audio_free(struct poseidon *p)
|
||||||
|
{
|
||||||
|
struct poseidon_audio *pa = &p->audio;
|
||||||
|
|
||||||
|
if (pa->card)
|
||||||
|
snd_card_free(pa->card);
|
||||||
|
return 0;
|
||||||
|
}
|
280
drivers/media/video/tlg2300/pd-common.h
Normal file
280
drivers/media/video/tlg2300/pd-common.h
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
#ifndef PD_COMMON_H
|
||||||
|
#define PD_COMMON_H
|
||||||
|
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/wait.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/videodev2.h>
|
||||||
|
#include <linux/semaphore.h>
|
||||||
|
#include <linux/usb.h>
|
||||||
|
#include <linux/poll.h>
|
||||||
|
#include <media/videobuf-vmalloc.h>
|
||||||
|
#include <media/v4l2-device.h>
|
||||||
|
|
||||||
|
#include "dvb_frontend.h"
|
||||||
|
#include "dvbdev.h"
|
||||||
|
#include "dvb_demux.h"
|
||||||
|
#include "dmxdev.h"
|
||||||
|
|
||||||
|
#define SBUF_NUM 8
|
||||||
|
#define MAX_BUFFER_NUM 6
|
||||||
|
#define PK_PER_URB 32
|
||||||
|
#define ISO_PKT_SIZE 3072
|
||||||
|
|
||||||
|
#define POSEIDON_STATE_NONE (0x0000)
|
||||||
|
#define POSEIDON_STATE_ANALOG (0x0001)
|
||||||
|
#define POSEIDON_STATE_FM (0x0002)
|
||||||
|
#define POSEIDON_STATE_DVBT (0x0004)
|
||||||
|
#define POSEIDON_STATE_VBI (0x0008)
|
||||||
|
#define POSEIDON_STATE_DISCONNECT (0x0080)
|
||||||
|
|
||||||
|
#define PM_SUSPEND_DELAY 3
|
||||||
|
|
||||||
|
#define V4L_PAL_VBI_LINES 18
|
||||||
|
#define V4L_NTSC_VBI_LINES 12
|
||||||
|
#define V4L_PAL_VBI_FRAMESIZE (V4L_PAL_VBI_LINES * 1440 * 2)
|
||||||
|
#define V4L_NTSC_VBI_FRAMESIZE (V4L_NTSC_VBI_LINES * 1440 * 2)
|
||||||
|
|
||||||
|
#define TUNER_FREQ_MIN (45000000)
|
||||||
|
#define TUNER_FREQ_MAX (862000000)
|
||||||
|
|
||||||
|
struct vbi_data {
|
||||||
|
struct video_device *v_dev;
|
||||||
|
struct video_data *video;
|
||||||
|
struct front_face *front;
|
||||||
|
|
||||||
|
unsigned int copied;
|
||||||
|
unsigned int vbi_size; /* the whole size of two fields */
|
||||||
|
int users;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the running context of the video, it is useful for
|
||||||
|
* resume()
|
||||||
|
*/
|
||||||
|
struct running_context {
|
||||||
|
u32 freq; /* VIDIOC_S_FREQUENCY */
|
||||||
|
int audio_idx; /* VIDIOC_S_TUNER */
|
||||||
|
v4l2_std_id tvnormid; /* VIDIOC_S_STD */
|
||||||
|
int sig_index; /* VIDIOC_S_INPUT */
|
||||||
|
struct v4l2_pix_format pix; /* VIDIOC_S_FMT */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct video_data {
|
||||||
|
/* v4l2 video device */
|
||||||
|
struct video_device *v_dev;
|
||||||
|
|
||||||
|
/* the working context */
|
||||||
|
struct running_context context;
|
||||||
|
|
||||||
|
/* for data copy */
|
||||||
|
int field_count;
|
||||||
|
|
||||||
|
char *dst;
|
||||||
|
int lines_copied;
|
||||||
|
int prev_left;
|
||||||
|
|
||||||
|
int lines_per_field;
|
||||||
|
int lines_size;
|
||||||
|
|
||||||
|
/* for communication */
|
||||||
|
u8 endpoint_addr;
|
||||||
|
struct urb *urb_array[SBUF_NUM];
|
||||||
|
struct vbi_data *vbi;
|
||||||
|
struct poseidon *pd;
|
||||||
|
struct front_face *front;
|
||||||
|
|
||||||
|
int is_streaming;
|
||||||
|
int users;
|
||||||
|
|
||||||
|
/* for bubble handler */
|
||||||
|
struct work_struct bubble_work;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum pcm_stream_state {
|
||||||
|
STREAM_OFF,
|
||||||
|
STREAM_ON,
|
||||||
|
STREAM_SUSPEND,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define AUDIO_BUFS (3)
|
||||||
|
#define CAPTURE_STREAM_EN 1
|
||||||
|
struct poseidon_audio {
|
||||||
|
struct urb *urb_array[AUDIO_BUFS];
|
||||||
|
unsigned int copied_position;
|
||||||
|
struct snd_pcm_substream *capture_pcm_substream;
|
||||||
|
|
||||||
|
unsigned int rcv_position;
|
||||||
|
struct snd_card *card;
|
||||||
|
int card_close;
|
||||||
|
|
||||||
|
int users;
|
||||||
|
int pm_state;
|
||||||
|
enum pcm_stream_state capture_stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct radio_data {
|
||||||
|
__u32 fm_freq;
|
||||||
|
int users;
|
||||||
|
unsigned int is_radio_streaming;
|
||||||
|
struct video_device *fm_dev;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DVB_SBUF_NUM 4
|
||||||
|
#define DVB_URB_BUF_SIZE 0x2000
|
||||||
|
struct pd_dvb_adapter {
|
||||||
|
struct dvb_adapter dvb_adap;
|
||||||
|
struct dvb_frontend dvb_fe;
|
||||||
|
struct dmxdev dmxdev;
|
||||||
|
struct dvb_demux demux;
|
||||||
|
|
||||||
|
atomic_t users;
|
||||||
|
atomic_t active_feed;
|
||||||
|
|
||||||
|
/* data transfer */
|
||||||
|
s32 is_streaming;
|
||||||
|
struct urb *urb_array[DVB_SBUF_NUM];
|
||||||
|
struct poseidon *pd_device;
|
||||||
|
u8 ep_addr;
|
||||||
|
u8 reserved[3];
|
||||||
|
|
||||||
|
/* data for power resume*/
|
||||||
|
struct dvb_frontend_parameters fe_param;
|
||||||
|
|
||||||
|
/* for channel scanning */
|
||||||
|
int prev_freq;
|
||||||
|
int bandwidth;
|
||||||
|
unsigned long last_jiffies;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct front_face {
|
||||||
|
/* use this field to distinguish VIDEO and VBI */
|
||||||
|
enum v4l2_buf_type type;
|
||||||
|
|
||||||
|
/* for host */
|
||||||
|
struct videobuf_queue q;
|
||||||
|
|
||||||
|
/* the bridge for host and device */
|
||||||
|
struct videobuf_buffer *curr_frame;
|
||||||
|
|
||||||
|
/* for device */
|
||||||
|
spinlock_t queue_lock;
|
||||||
|
struct list_head active;
|
||||||
|
struct poseidon *pd;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct poseidon {
|
||||||
|
struct list_head device_list;
|
||||||
|
|
||||||
|
struct mutex lock;
|
||||||
|
struct kref kref;
|
||||||
|
|
||||||
|
/* for V4L2 */
|
||||||
|
struct v4l2_device v4l2_dev;
|
||||||
|
|
||||||
|
/* hardware info */
|
||||||
|
struct usb_device *udev;
|
||||||
|
struct usb_interface *interface;
|
||||||
|
int cur_transfer_mode;
|
||||||
|
|
||||||
|
struct video_data video_data; /* video */
|
||||||
|
struct vbi_data vbi_data; /* vbi */
|
||||||
|
struct poseidon_audio audio; /* audio (alsa) */
|
||||||
|
struct radio_data radio_data; /* FM */
|
||||||
|
struct pd_dvb_adapter dvb_data; /* DVB */
|
||||||
|
|
||||||
|
u32 state;
|
||||||
|
int country_code;
|
||||||
|
struct file *file_for_stream; /* the active stream*/
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
int (*pm_suspend)(struct poseidon *);
|
||||||
|
int (*pm_resume)(struct poseidon *);
|
||||||
|
pm_message_t msg;
|
||||||
|
|
||||||
|
struct work_struct pm_work;
|
||||||
|
u8 portnum;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct poseidon_format {
|
||||||
|
char *name;
|
||||||
|
int fourcc; /* video4linux 2 */
|
||||||
|
int depth; /* bit/pixel */
|
||||||
|
int flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct poseidon_tvnorm {
|
||||||
|
v4l2_std_id v4l2_id;
|
||||||
|
char name[12];
|
||||||
|
u32 tlg_tvnorm;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* video */
|
||||||
|
int pd_video_init(struct poseidon *);
|
||||||
|
void pd_video_exit(struct poseidon *);
|
||||||
|
int stop_all_video_stream(struct poseidon *);
|
||||||
|
|
||||||
|
/* alsa audio */
|
||||||
|
int poseidon_audio_init(struct poseidon *);
|
||||||
|
int poseidon_audio_free(struct poseidon *);
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
int pm_alsa_suspend(struct poseidon *);
|
||||||
|
int pm_alsa_resume(struct poseidon *);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* dvb */
|
||||||
|
int pd_dvb_usb_device_init(struct poseidon *);
|
||||||
|
void pd_dvb_usb_device_exit(struct poseidon *);
|
||||||
|
void pd_dvb_usb_device_cleanup(struct poseidon *);
|
||||||
|
int pd_dvb_get_adapter_num(struct pd_dvb_adapter *);
|
||||||
|
void dvb_stop_streaming(struct pd_dvb_adapter *);
|
||||||
|
|
||||||
|
/* FM */
|
||||||
|
int poseidon_fm_init(struct poseidon *);
|
||||||
|
int poseidon_fm_exit(struct poseidon *);
|
||||||
|
struct video_device *vdev_init(struct poseidon *, struct video_device *);
|
||||||
|
|
||||||
|
/* vendor command ops */
|
||||||
|
int send_set_req(struct poseidon*, u8, s32, s32*);
|
||||||
|
int send_get_req(struct poseidon*, u8, s32, void*, s32*, s32);
|
||||||
|
s32 set_tuner_mode(struct poseidon*, unsigned char);
|
||||||
|
enum tlg__analog_audio_standard get_audio_std(s32, s32);
|
||||||
|
|
||||||
|
/* bulk urb alloc/free */
|
||||||
|
int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
|
||||||
|
struct usb_device *udev, u8 ep_addr,
|
||||||
|
int buf_size, gfp_t gfp_flags,
|
||||||
|
usb_complete_t complete_fn, void *context);
|
||||||
|
void free_all_urb_generic(struct urb **urb_array, int num);
|
||||||
|
|
||||||
|
/* misc */
|
||||||
|
void poseidon_delete(struct kref *kref);
|
||||||
|
void destroy_video_device(struct video_device **v_dev);
|
||||||
|
extern int country_code;
|
||||||
|
extern int debug_mode;
|
||||||
|
void set_debug_mode(struct video_device *vfd, int debug_mode);
|
||||||
|
|
||||||
|
#define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE)
|
||||||
|
#define get_pm_count(p) (atomic_read(&(p)->interface->pm_usage_cnt))
|
||||||
|
|
||||||
|
#define log(a, ...) printk(KERN_DEBUG "\t[ %s : %.3d ] "a"\n", \
|
||||||
|
__func__, __LINE__, ## __VA_ARGS__)
|
||||||
|
|
||||||
|
/* for power management */
|
||||||
|
#define logpm(pd) do {\
|
||||||
|
if (debug_mode & 0x10)\
|
||||||
|
log();\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define logs(f) do { \
|
||||||
|
if ((debug_mode & 0x4) && \
|
||||||
|
(f)->type == V4L2_BUF_TYPE_VBI_CAPTURE) \
|
||||||
|
log("type : VBI");\
|
||||||
|
\
|
||||||
|
if ((debug_mode & 0x8) && \
|
||||||
|
(f)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) \
|
||||||
|
log("type : VIDEO");\
|
||||||
|
} while (0)
|
||||||
|
#endif
|
593
drivers/media/video/tlg2300/pd-dvb.c
Normal file
593
drivers/media/video/tlg2300/pd-dvb.c
Normal file
|
@ -0,0 +1,593 @@
|
||||||
|
#include "pd-common.h"
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/usb.h>
|
||||||
|
#include <linux/dvb/dmx.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
|
||||||
|
#include "vendorcmds.h"
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/atomic.h>
|
||||||
|
|
||||||
|
static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb);
|
||||||
|
|
||||||
|
static int dvb_bandwidth[][2] = {
|
||||||
|
{ TLG_BW_8, BANDWIDTH_8_MHZ },
|
||||||
|
{ TLG_BW_7, BANDWIDTH_7_MHZ },
|
||||||
|
{ TLG_BW_6, BANDWIDTH_6_MHZ }
|
||||||
|
};
|
||||||
|
static int dvb_bandwidth_length = ARRAY_SIZE(dvb_bandwidth);
|
||||||
|
|
||||||
|
static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb);
|
||||||
|
static int poseidon_check_mode_dvbt(struct poseidon *pd)
|
||||||
|
{
|
||||||
|
s32 ret = 0, cmd_status = 0;
|
||||||
|
|
||||||
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
|
schedule_timeout(HZ/4);
|
||||||
|
|
||||||
|
ret = usb_set_interface(pd->udev, 0, BULK_ALTERNATE_IFACE);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = set_tuner_mode(pd, TLG_MODE_CAPS_DVB_T);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* signal source */
|
||||||
|
ret = send_set_req(pd, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &cmd_status);
|
||||||
|
if (ret|cmd_status)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* acquire :
|
||||||
|
* 1 == open
|
||||||
|
* 0 == release
|
||||||
|
*/
|
||||||
|
static int poseidon_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = fe->demodulator_priv;
|
||||||
|
struct pd_dvb_adapter *pd_dvb;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!pd)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
pd_dvb = container_of(fe, struct pd_dvb_adapter, dvb_fe);
|
||||||
|
if (acquire) {
|
||||||
|
mutex_lock(&pd->lock);
|
||||||
|
if (pd->state & POSEIDON_STATE_DISCONNECT) {
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto open_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pd->state && !(pd->state & POSEIDON_STATE_DVBT)) {
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto open_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_autopm_get_interface(pd->interface);
|
||||||
|
if (0 == pd->state) {
|
||||||
|
ret = poseidon_check_mode_dvbt(pd);
|
||||||
|
if (ret < 0) {
|
||||||
|
usb_autopm_put_interface(pd->interface);
|
||||||
|
goto open_out;
|
||||||
|
}
|
||||||
|
pd->state |= POSEIDON_STATE_DVBT;
|
||||||
|
pd_dvb->bandwidth = 0;
|
||||||
|
pd_dvb->prev_freq = 0;
|
||||||
|
}
|
||||||
|
atomic_inc(&pd_dvb->users);
|
||||||
|
kref_get(&pd->kref);
|
||||||
|
open_out:
|
||||||
|
mutex_unlock(&pd->lock);
|
||||||
|
} else {
|
||||||
|
dvb_stop_streaming(pd_dvb);
|
||||||
|
|
||||||
|
if (atomic_dec_and_test(&pd_dvb->users)) {
|
||||||
|
mutex_lock(&pd->lock);
|
||||||
|
pd->state &= ~POSEIDON_STATE_DVBT;
|
||||||
|
mutex_unlock(&pd->lock);
|
||||||
|
}
|
||||||
|
kref_put(&pd->kref, poseidon_delete);
|
||||||
|
usb_autopm_put_interface(pd->interface);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void poseidon_fe_release(struct dvb_frontend *fe)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = fe->demodulator_priv;
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
pd->pm_suspend = NULL;
|
||||||
|
pd->pm_resume = NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static s32 poseidon_fe_sleep(struct dvb_frontend *fe)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* return true if we can satisfy the conditions, else return false.
|
||||||
|
*/
|
||||||
|
static bool check_scan_ok(__u32 freq, int bandwidth,
|
||||||
|
struct pd_dvb_adapter *adapter)
|
||||||
|
{
|
||||||
|
if (bandwidth < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (adapter->prev_freq == freq
|
||||||
|
&& adapter->bandwidth == bandwidth) {
|
||||||
|
long nl = jiffies - adapter->last_jiffies;
|
||||||
|
unsigned int msec ;
|
||||||
|
|
||||||
|
msec = jiffies_to_msecs(abs(nl));
|
||||||
|
return msec > 15000 ? true : false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the firmware delays too long for an invalid frequency.
|
||||||
|
*/
|
||||||
|
static int fw_delay_overflow(struct pd_dvb_adapter *adapter)
|
||||||
|
{
|
||||||
|
long nl = jiffies - adapter->last_jiffies;
|
||||||
|
unsigned int msec ;
|
||||||
|
|
||||||
|
msec = jiffies_to_msecs(abs(nl));
|
||||||
|
return msec > 800 ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int poseidon_set_fe(struct dvb_frontend *fe,
|
||||||
|
struct dvb_frontend_parameters *fep)
|
||||||
|
{
|
||||||
|
s32 ret = 0, cmd_status = 0;
|
||||||
|
s32 i, bandwidth = -1;
|
||||||
|
struct poseidon *pd = fe->demodulator_priv;
|
||||||
|
struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
|
||||||
|
|
||||||
|
if (in_hibernation(pd))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
mutex_lock(&pd->lock);
|
||||||
|
for (i = 0; i < dvb_bandwidth_length; i++)
|
||||||
|
if (fep->u.ofdm.bandwidth == dvb_bandwidth[i][1])
|
||||||
|
bandwidth = dvb_bandwidth[i][0];
|
||||||
|
|
||||||
|
if (check_scan_ok(fep->frequency, bandwidth, pd_dvb)) {
|
||||||
|
ret = send_set_req(pd, TUNE_FREQ_SELECT,
|
||||||
|
fep->frequency / 1000, &cmd_status);
|
||||||
|
if (ret | cmd_status) {
|
||||||
|
log("error line");
|
||||||
|
goto front_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = send_set_req(pd, DVBT_BANDW_SEL,
|
||||||
|
bandwidth, &cmd_status);
|
||||||
|
if (ret | cmd_status) {
|
||||||
|
log("error line");
|
||||||
|
goto front_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
|
||||||
|
if (ret | cmd_status) {
|
||||||
|
log("error line");
|
||||||
|
goto front_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save the context for future */
|
||||||
|
memcpy(&pd_dvb->fe_param, fep, sizeof(*fep));
|
||||||
|
pd_dvb->bandwidth = bandwidth;
|
||||||
|
pd_dvb->prev_freq = fep->frequency;
|
||||||
|
pd_dvb->last_jiffies = jiffies;
|
||||||
|
}
|
||||||
|
front_out:
|
||||||
|
mutex_unlock(&pd->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int pm_dvb_suspend(struct poseidon *pd)
|
||||||
|
{
|
||||||
|
struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
|
||||||
|
dvb_stop_streaming(pd_dvb);
|
||||||
|
dvb_urb_cleanup(pd_dvb);
|
||||||
|
msleep(500);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pm_dvb_resume(struct poseidon *pd)
|
||||||
|
{
|
||||||
|
struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
|
||||||
|
|
||||||
|
poseidon_check_mode_dvbt(pd);
|
||||||
|
msleep(300);
|
||||||
|
poseidon_set_fe(&pd_dvb->dvb_fe, &pd_dvb->fe_param);
|
||||||
|
|
||||||
|
dvb_start_streaming(pd_dvb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static s32 poseidon_fe_init(struct dvb_frontend *fe)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = fe->demodulator_priv;
|
||||||
|
struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
pd->pm_suspend = pm_dvb_suspend;
|
||||||
|
pd->pm_resume = pm_dvb_resume;
|
||||||
|
#endif
|
||||||
|
memset(&pd_dvb->fe_param, 0,
|
||||||
|
sizeof(struct dvb_frontend_parameters));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int poseidon_get_fe(struct dvb_frontend *fe,
|
||||||
|
struct dvb_frontend_parameters *fep)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = fe->demodulator_priv;
|
||||||
|
struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
|
||||||
|
|
||||||
|
memcpy(fep, &pd_dvb->fe_param, sizeof(*fep));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int poseidon_fe_get_tune_settings(struct dvb_frontend *fe,
|
||||||
|
struct dvb_frontend_tune_settings *tune)
|
||||||
|
{
|
||||||
|
tune->min_delay_ms = 1000;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int poseidon_read_status(struct dvb_frontend *fe, fe_status_t *stat)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = fe->demodulator_priv;
|
||||||
|
s32 ret = -1, cmd_status;
|
||||||
|
struct tuner_dtv_sig_stat_s status = {};
|
||||||
|
|
||||||
|
if (in_hibernation(pd))
|
||||||
|
return -EBUSY;
|
||||||
|
mutex_lock(&pd->lock);
|
||||||
|
|
||||||
|
ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T,
|
||||||
|
&status, &cmd_status, sizeof(status));
|
||||||
|
if (ret | cmd_status) {
|
||||||
|
log("get tuner status error");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug_mode)
|
||||||
|
log("P : %d, L %d, LB :%d", status.sig_present,
|
||||||
|
status.sig_locked, status.sig_lock_busy);
|
||||||
|
|
||||||
|
if (status.sig_lock_busy) {
|
||||||
|
goto out;
|
||||||
|
} else if (status.sig_present || status.sig_locked) {
|
||||||
|
*stat |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER
|
||||||
|
| FE_HAS_SYNC | FE_HAS_VITERBI;
|
||||||
|
} else {
|
||||||
|
if (fw_delay_overflow(&pd->dvb_data))
|
||||||
|
*stat |= FE_TIMEDOUT;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
mutex_unlock(&pd->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int poseidon_read_ber(struct dvb_frontend *fe, u32 *ber)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = fe->demodulator_priv;
|
||||||
|
struct tuner_ber_rate_s tlg_ber = {};
|
||||||
|
s32 ret = -1, cmd_status;
|
||||||
|
|
||||||
|
mutex_lock(&pd->lock);
|
||||||
|
ret = send_get_req(pd, TUNER_BER_RATE, 0,
|
||||||
|
&tlg_ber, &cmd_status, sizeof(tlg_ber));
|
||||||
|
if (ret | cmd_status)
|
||||||
|
goto out;
|
||||||
|
*ber = tlg_ber.ber_rate;
|
||||||
|
out:
|
||||||
|
mutex_unlock(&pd->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static s32 poseidon_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = fe->demodulator_priv;
|
||||||
|
struct tuner_dtv_sig_stat_s status = {};
|
||||||
|
s32 ret = 0, cmd_status;
|
||||||
|
|
||||||
|
mutex_lock(&pd->lock);
|
||||||
|
ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T,
|
||||||
|
&status, &cmd_status, sizeof(status));
|
||||||
|
if (ret | cmd_status)
|
||||||
|
goto out;
|
||||||
|
if ((status.sig_present || status.sig_locked) && !status.sig_strength)
|
||||||
|
*strength = 0xFFFF;
|
||||||
|
else
|
||||||
|
*strength = status.sig_strength;
|
||||||
|
out:
|
||||||
|
mutex_unlock(&pd->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int poseidon_read_snr(struct dvb_frontend *fe, u16 *snr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int poseidon_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
|
||||||
|
{
|
||||||
|
*unc = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dvb_frontend_ops poseidon_frontend_ops = {
|
||||||
|
.info = {
|
||||||
|
.name = "Poseidon DVB-T",
|
||||||
|
.type = FE_OFDM,
|
||||||
|
.frequency_min = 174000000,
|
||||||
|
.frequency_max = 862000000,
|
||||||
|
.frequency_stepsize = 62500,/* FIXME */
|
||||||
|
.caps = FE_CAN_INVERSION_AUTO |
|
||||||
|
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
|
||||||
|
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
|
||||||
|
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
|
||||||
|
FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
|
||||||
|
FE_CAN_GUARD_INTERVAL_AUTO |
|
||||||
|
FE_CAN_RECOVER |
|
||||||
|
FE_CAN_HIERARCHY_AUTO,
|
||||||
|
},
|
||||||
|
|
||||||
|
.release = poseidon_fe_release,
|
||||||
|
|
||||||
|
.init = poseidon_fe_init,
|
||||||
|
.sleep = poseidon_fe_sleep,
|
||||||
|
|
||||||
|
.set_frontend = poseidon_set_fe,
|
||||||
|
.get_frontend = poseidon_get_fe,
|
||||||
|
.get_tune_settings = poseidon_fe_get_tune_settings,
|
||||||
|
|
||||||
|
.read_status = poseidon_read_status,
|
||||||
|
.read_ber = poseidon_read_ber,
|
||||||
|
.read_signal_strength = poseidon_read_signal_strength,
|
||||||
|
.read_snr = poseidon_read_snr,
|
||||||
|
.read_ucblocks = poseidon_read_unc_blocks,
|
||||||
|
|
||||||
|
.ts_bus_ctrl = poseidon_ts_bus_ctrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dvb_urb_irq(struct urb *urb)
|
||||||
|
{
|
||||||
|
struct pd_dvb_adapter *pd_dvb = urb->context;
|
||||||
|
int len = urb->transfer_buffer_length;
|
||||||
|
struct dvb_demux *demux = &pd_dvb->demux;
|
||||||
|
s32 ret;
|
||||||
|
|
||||||
|
if (!pd_dvb->is_streaming || urb->status) {
|
||||||
|
if (urb->status == -EPROTO)
|
||||||
|
goto resend;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urb->actual_length == len)
|
||||||
|
dvb_dmx_swfilter(demux, urb->transfer_buffer, len);
|
||||||
|
else if (urb->actual_length == len - 4) {
|
||||||
|
int offset;
|
||||||
|
u8 *buf = urb->transfer_buffer;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The packet size is 512,
|
||||||
|
* last packet contains 456 bytes tsp data
|
||||||
|
*/
|
||||||
|
for (offset = 456; offset < len; offset += 512) {
|
||||||
|
if (!strncmp(buf + offset, "DVHS", 4)) {
|
||||||
|
dvb_dmx_swfilter(demux, buf, offset);
|
||||||
|
if (len > offset + 52 + 4) {
|
||||||
|
/*16 bytes trailer + 36 bytes padding */
|
||||||
|
buf += offset + 52;
|
||||||
|
len -= offset + 52 + 4;
|
||||||
|
dvb_dmx_swfilter(demux, buf, len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resend:
|
||||||
|
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
|
if (ret)
|
||||||
|
log(" usb_submit_urb failed: error %d", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dvb_urb_init(struct pd_dvb_adapter *pd_dvb)
|
||||||
|
{
|
||||||
|
if (pd_dvb->urb_array[0])
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
alloc_bulk_urbs_generic(pd_dvb->urb_array, DVB_SBUF_NUM,
|
||||||
|
pd_dvb->pd_device->udev, pd_dvb->ep_addr,
|
||||||
|
DVB_URB_BUF_SIZE, GFP_KERNEL,
|
||||||
|
dvb_urb_irq, pd_dvb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb)
|
||||||
|
{
|
||||||
|
free_all_urb_generic(pd_dvb->urb_array, DVB_SBUF_NUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = pd_dvb->pd_device;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (pd->state & POSEIDON_STATE_DISCONNECT)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
mutex_lock(&pd->lock);
|
||||||
|
if (!pd_dvb->is_streaming) {
|
||||||
|
s32 i, cmd_status = 0;
|
||||||
|
/*
|
||||||
|
* Once upon a time, there was a difficult bug lying here.
|
||||||
|
* ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
|
||||||
|
*/
|
||||||
|
|
||||||
|
ret = send_set_req(pd, PLAY_SERVICE, 1, &cmd_status);
|
||||||
|
if (ret | cmd_status)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = dvb_urb_init(pd_dvb);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
pd_dvb->is_streaming = 1;
|
||||||
|
for (i = 0; i < DVB_SBUF_NUM; i++) {
|
||||||
|
ret = usb_submit_urb(pd_dvb->urb_array[i],
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (ret) {
|
||||||
|
log(" submit urb error %d", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
mutex_unlock(&pd->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dvb_stop_streaming(struct pd_dvb_adapter *pd_dvb)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = pd_dvb->pd_device;
|
||||||
|
|
||||||
|
mutex_lock(&pd->lock);
|
||||||
|
if (pd_dvb->is_streaming) {
|
||||||
|
s32 i, ret, cmd_status = 0;
|
||||||
|
|
||||||
|
pd_dvb->is_streaming = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < DVB_SBUF_NUM; i++)
|
||||||
|
if (pd_dvb->urb_array[i])
|
||||||
|
usb_kill_urb(pd_dvb->urb_array[i]);
|
||||||
|
|
||||||
|
ret = send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
|
||||||
|
&cmd_status);
|
||||||
|
if (ret | cmd_status)
|
||||||
|
log("error");
|
||||||
|
}
|
||||||
|
mutex_unlock(&pd->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pd_start_feed(struct dvb_demux_feed *feed)
|
||||||
|
{
|
||||||
|
struct pd_dvb_adapter *pd_dvb = feed->demux->priv;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!pd_dvb)
|
||||||
|
return -1;
|
||||||
|
if (atomic_inc_return(&pd_dvb->active_feed) == 1)
|
||||||
|
ret = dvb_start_streaming(pd_dvb);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pd_stop_feed(struct dvb_demux_feed *feed)
|
||||||
|
{
|
||||||
|
struct pd_dvb_adapter *pd_dvb = feed->demux->priv;
|
||||||
|
|
||||||
|
if (!pd_dvb)
|
||||||
|
return -1;
|
||||||
|
if (atomic_dec_and_test(&pd_dvb->active_feed))
|
||||||
|
dvb_stop_streaming(pd_dvb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
||||||
|
int pd_dvb_usb_device_init(struct poseidon *pd)
|
||||||
|
{
|
||||||
|
struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
|
||||||
|
struct dvb_demux *dvbdemux;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pd_dvb->ep_addr = 0x82;
|
||||||
|
atomic_set(&pd_dvb->users, 0);
|
||||||
|
atomic_set(&pd_dvb->active_feed, 0);
|
||||||
|
pd_dvb->pd_device = pd;
|
||||||
|
|
||||||
|
ret = dvb_register_adapter(&pd_dvb->dvb_adap,
|
||||||
|
"Poseidon dvbt adapter",
|
||||||
|
THIS_MODULE,
|
||||||
|
NULL /* for hibernation correctly*/,
|
||||||
|
adapter_nr);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error1;
|
||||||
|
|
||||||
|
/* register frontend */
|
||||||
|
pd_dvb->dvb_fe.demodulator_priv = pd;
|
||||||
|
memcpy(&pd_dvb->dvb_fe.ops, &poseidon_frontend_ops,
|
||||||
|
sizeof(struct dvb_frontend_ops));
|
||||||
|
ret = dvb_register_frontend(&pd_dvb->dvb_adap, &pd_dvb->dvb_fe);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error2;
|
||||||
|
|
||||||
|
/* register demux device */
|
||||||
|
dvbdemux = &pd_dvb->demux;
|
||||||
|
dvbdemux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
|
||||||
|
dvbdemux->priv = pd_dvb;
|
||||||
|
dvbdemux->feednum = dvbdemux->filternum = 64;
|
||||||
|
dvbdemux->start_feed = pd_start_feed;
|
||||||
|
dvbdemux->stop_feed = pd_stop_feed;
|
||||||
|
dvbdemux->write_to_decoder = NULL;
|
||||||
|
|
||||||
|
ret = dvb_dmx_init(dvbdemux);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error3;
|
||||||
|
|
||||||
|
pd_dvb->dmxdev.filternum = pd_dvb->demux.filternum;
|
||||||
|
pd_dvb->dmxdev.demux = &pd_dvb->demux.dmx;
|
||||||
|
pd_dvb->dmxdev.capabilities = 0;
|
||||||
|
|
||||||
|
ret = dvb_dmxdev_init(&pd_dvb->dmxdev, &pd_dvb->dvb_adap);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error3;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error3:
|
||||||
|
dvb_unregister_frontend(&pd_dvb->dvb_fe);
|
||||||
|
error2:
|
||||||
|
dvb_unregister_adapter(&pd_dvb->dvb_adap);
|
||||||
|
error1:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pd_dvb_usb_device_exit(struct poseidon *pd)
|
||||||
|
{
|
||||||
|
struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
|
||||||
|
|
||||||
|
while (atomic_read(&pd_dvb->users) != 0
|
||||||
|
|| atomic_read(&pd_dvb->active_feed) != 0) {
|
||||||
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
|
schedule_timeout(HZ);
|
||||||
|
}
|
||||||
|
dvb_dmxdev_release(&pd_dvb->dmxdev);
|
||||||
|
dvb_unregister_frontend(&pd_dvb->dvb_fe);
|
||||||
|
dvb_unregister_adapter(&pd_dvb->dvb_adap);
|
||||||
|
pd_dvb_usb_device_cleanup(pd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pd_dvb_usb_device_cleanup(struct poseidon *pd)
|
||||||
|
{
|
||||||
|
struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
|
||||||
|
|
||||||
|
dvb_urb_cleanup(pd_dvb);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pd_dvb_get_adapter_num(struct pd_dvb_adapter *pd_dvb)
|
||||||
|
{
|
||||||
|
return pd_dvb->dvb_adap.num;
|
||||||
|
}
|
566
drivers/media/video/tlg2300/pd-main.c
Normal file
566
drivers/media/video/tlg2300/pd-main.c
Normal file
|
@ -0,0 +1,566 @@
|
||||||
|
/*
|
||||||
|
* device driver for Telegent tlg2300 based TV cards
|
||||||
|
*
|
||||||
|
* Author :
|
||||||
|
* Kang Yong <kangyong@telegent.com>
|
||||||
|
* Zhang Xiaobing <xbzhang@telegent.com>
|
||||||
|
* Huang Shijie <zyziii@telegent.com> or <shijie8@gmail.com>
|
||||||
|
*
|
||||||
|
* (c) 2009 Telegent Systems
|
||||||
|
* (c) 2010 Telegent Systems
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/kref.h>
|
||||||
|
#include <linux/suspend.h>
|
||||||
|
#include <linux/usb/quirks.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/firmware.h>
|
||||||
|
#include <linux/smp_lock.h>
|
||||||
|
|
||||||
|
#include "vendorcmds.h"
|
||||||
|
#include "pd-common.h"
|
||||||
|
|
||||||
|
#define VENDOR_ID 0x1B24
|
||||||
|
#define PRODUCT_ID 0x4001
|
||||||
|
static struct usb_device_id id_table[] = {
|
||||||
|
{ USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 0) },
|
||||||
|
{ USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 1) },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(usb, id_table);
|
||||||
|
|
||||||
|
int debug_mode;
|
||||||
|
module_param(debug_mode, int, 0644);
|
||||||
|
MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose");
|
||||||
|
|
||||||
|
const char *firmware_name = "tlg2300_firmware.bin";
|
||||||
|
struct usb_driver poseidon_driver;
|
||||||
|
static LIST_HEAD(pd_device_list);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* send set request to USB firmware.
|
||||||
|
*/
|
||||||
|
s32 send_set_req(struct poseidon *pd, u8 cmdid, s32 param, s32 *cmd_status)
|
||||||
|
{
|
||||||
|
s32 ret;
|
||||||
|
s8 data[32] = {};
|
||||||
|
u16 lower_16, upper_16;
|
||||||
|
|
||||||
|
if (pd->state & POSEIDON_STATE_DISCONNECT)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
mdelay(30);
|
||||||
|
|
||||||
|
if (param == 0) {
|
||||||
|
upper_16 = lower_16 = 0;
|
||||||
|
} else {
|
||||||
|
/* send 32 bit param as two 16 bit param,little endian */
|
||||||
|
lower_16 = (unsigned short)(param & 0xffff);
|
||||||
|
upper_16 = (unsigned short)((param >> 16) & 0xffff);
|
||||||
|
}
|
||||||
|
ret = usb_control_msg(pd->udev,
|
||||||
|
usb_rcvctrlpipe(pd->udev, 0),
|
||||||
|
REQ_SET_CMD | cmdid,
|
||||||
|
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
lower_16,
|
||||||
|
upper_16,
|
||||||
|
&data,
|
||||||
|
sizeof(*cmd_status),
|
||||||
|
USB_CTRL_GET_TIMEOUT);
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
return -ENXIO;
|
||||||
|
} else {
|
||||||
|
/* 1st 4 bytes into cmd_status */
|
||||||
|
memcpy((char *)cmd_status, &(data[0]), sizeof(*cmd_status));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* send get request to Poseidon firmware.
|
||||||
|
*/
|
||||||
|
s32 send_get_req(struct poseidon *pd, u8 cmdid, s32 param,
|
||||||
|
void *buf, s32 *cmd_status, s32 datalen)
|
||||||
|
{
|
||||||
|
s32 ret;
|
||||||
|
s8 data[128] = {};
|
||||||
|
u16 lower_16, upper_16;
|
||||||
|
|
||||||
|
if (pd->state & POSEIDON_STATE_DISCONNECT)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
mdelay(30);
|
||||||
|
if (param == 0) {
|
||||||
|
upper_16 = lower_16 = 0;
|
||||||
|
} else {
|
||||||
|
/*send 32 bit param as two 16 bit param, little endian */
|
||||||
|
lower_16 = (unsigned short)(param & 0xffff);
|
||||||
|
upper_16 = (unsigned short)((param >> 16) & 0xffff);
|
||||||
|
}
|
||||||
|
ret = usb_control_msg(pd->udev,
|
||||||
|
usb_rcvctrlpipe(pd->udev, 0),
|
||||||
|
REQ_GET_CMD | cmdid,
|
||||||
|
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
lower_16,
|
||||||
|
upper_16,
|
||||||
|
&data,
|
||||||
|
(datalen + sizeof(*cmd_status)),
|
||||||
|
USB_CTRL_GET_TIMEOUT);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
return -ENXIO;
|
||||||
|
} else {
|
||||||
|
/* 1st 4 bytes into cmd_status, remaining data into cmd_data */
|
||||||
|
memcpy((char *)cmd_status, &data[0], sizeof(*cmd_status));
|
||||||
|
memcpy((char *)buf, &data[sizeof(*cmd_status)], datalen);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pm_notifier_block(struct notifier_block *nb,
|
||||||
|
unsigned long event, void *dummy)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = NULL;
|
||||||
|
struct list_head *node, *next;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case PM_POST_HIBERNATION:
|
||||||
|
list_for_each_safe(node, next, &pd_device_list) {
|
||||||
|
struct usb_device *udev;
|
||||||
|
struct usb_interface *iface;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
pd = container_of(node, struct poseidon, device_list);
|
||||||
|
udev = pd->udev;
|
||||||
|
iface = pd->interface;
|
||||||
|
|
||||||
|
/* It will cause the system to reload the firmware */
|
||||||
|
rc = usb_lock_device_for_reset(udev, iface);
|
||||||
|
if (rc >= 0) {
|
||||||
|
usb_reset_device(udev);
|
||||||
|
usb_unlock_device(udev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
log("event :%ld\n", event);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct notifier_block pm_notifer = {
|
||||||
|
.notifier_call = pm_notifier_block,
|
||||||
|
};
|
||||||
|
|
||||||
|
int set_tuner_mode(struct poseidon *pd, unsigned char mode)
|
||||||
|
{
|
||||||
|
s32 ret, cmd_status;
|
||||||
|
|
||||||
|
if (pd->state & POSEIDON_STATE_DISCONNECT)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
ret = send_set_req(pd, TUNE_MODE_SELECT, mode, &cmd_status);
|
||||||
|
if (ret || cmd_status)
|
||||||
|
return -ENXIO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum tlg__analog_audio_standard get_audio_std(s32 mode, s32 country_code)
|
||||||
|
{
|
||||||
|
s32 nicam[] = {27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64,
|
||||||
|
65, 86, 351, 352, 353, 354, 358, 372, 852, 972};
|
||||||
|
s32 btsc[] = {1, 52, 54, 55, 886};
|
||||||
|
s32 eiaj[] = {81};
|
||||||
|
s32 i;
|
||||||
|
|
||||||
|
if (mode == TLG_MODE_FM_RADIO) {
|
||||||
|
if (country_code == 1)
|
||||||
|
return TLG_TUNE_ASTD_FM_US;
|
||||||
|
else
|
||||||
|
return TLG_TUNE_ASTD_FM_EUR;
|
||||||
|
} else if (mode == TLG_MODE_ANALOG_TV_UNCOMP) {
|
||||||
|
for (i = 0; i < sizeof(nicam) / sizeof(s32); i++) {
|
||||||
|
if (country_code == nicam[i])
|
||||||
|
return TLG_TUNE_ASTD_NICAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(btsc) / sizeof(s32); i++) {
|
||||||
|
if (country_code == btsc[i])
|
||||||
|
return TLG_TUNE_ASTD_BTSC;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(eiaj) / sizeof(s32); i++) {
|
||||||
|
if (country_code == eiaj[i])
|
||||||
|
return TLG_TUNE_ASTD_EIAJ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TLG_TUNE_ASTD_A2;
|
||||||
|
} else {
|
||||||
|
return TLG_TUNE_ASTD_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void poseidon_delete(struct kref *kref)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = container_of(kref, struct poseidon, kref);
|
||||||
|
|
||||||
|
if (!pd)
|
||||||
|
return;
|
||||||
|
list_del_init(&pd->device_list);
|
||||||
|
|
||||||
|
pd_dvb_usb_device_cleanup(pd);
|
||||||
|
/* clean_audio_data(&pd->audio_data);*/
|
||||||
|
|
||||||
|
if (pd->udev) {
|
||||||
|
usb_put_dev(pd->udev);
|
||||||
|
pd->udev = NULL;
|
||||||
|
}
|
||||||
|
if (pd->interface) {
|
||||||
|
usb_put_intf(pd->interface);
|
||||||
|
pd->interface = NULL;
|
||||||
|
}
|
||||||
|
kfree(pd);
|
||||||
|
log();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int firmware_download(struct usb_device *udev)
|
||||||
|
{
|
||||||
|
int ret = 0, actual_length;
|
||||||
|
const struct firmware *fw = NULL;
|
||||||
|
void *fwbuf = NULL;
|
||||||
|
size_t fwlength = 0, offset;
|
||||||
|
size_t max_packet_size;
|
||||||
|
|
||||||
|
ret = request_firmware(&fw, firmware_name, &udev->dev);
|
||||||
|
if (ret) {
|
||||||
|
log("download err : %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
fwlength = fw->size;
|
||||||
|
|
||||||
|
fwbuf = kzalloc(fwlength, GFP_KERNEL);
|
||||||
|
if (!fwbuf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
memcpy(fwbuf, fw->data, fwlength);
|
||||||
|
|
||||||
|
max_packet_size = udev->ep_out[0x1]->desc.wMaxPacketSize;
|
||||||
|
log("\t\t download size : %d", (int)max_packet_size);
|
||||||
|
|
||||||
|
for (offset = 0; offset < fwlength; offset += max_packet_size) {
|
||||||
|
actual_length = 0;
|
||||||
|
ret = usb_bulk_msg(udev,
|
||||||
|
usb_sndbulkpipe(udev, 0x01), /* ep 1 */
|
||||||
|
fwbuf + offset,
|
||||||
|
min(max_packet_size, fwlength - offset),
|
||||||
|
&actual_length,
|
||||||
|
HZ * 10);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
kfree(fwbuf);
|
||||||
|
out:
|
||||||
|
release_firmware(fw);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
/* one-to-one map : poseidon{} <----> usb_device{}'s port */
|
||||||
|
static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
|
||||||
|
{
|
||||||
|
pd->portnum = udev->portnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int get_autopm_ref(struct poseidon *pd)
|
||||||
|
{
|
||||||
|
return pd->video_data.users + pd->vbi_data.users + pd->audio.users
|
||||||
|
+ atomic_read(&pd->dvb_data.users) + pd->radio_data.users;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fixup something for poseidon */
|
||||||
|
static inline struct poseidon *fixup(struct poseidon *pd)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
|
||||||
|
/* old udev and interface have gone, so put back reference . */
|
||||||
|
count = get_autopm_ref(pd);
|
||||||
|
log("count : %d, ref count : %d", count, get_pm_count(pd));
|
||||||
|
while (count--)
|
||||||
|
usb_autopm_put_interface(pd->interface);
|
||||||
|
/*usb_autopm_set_interface(pd->interface); */
|
||||||
|
|
||||||
|
usb_put_dev(pd->udev);
|
||||||
|
usb_put_intf(pd->interface);
|
||||||
|
log("event : %d\n", pd->msg.event);
|
||||||
|
return pd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct poseidon *find_old_poseidon(struct usb_device *udev)
|
||||||
|
{
|
||||||
|
struct poseidon *pd;
|
||||||
|
|
||||||
|
list_for_each_entry(pd, &pd_device_list, device_list) {
|
||||||
|
if (pd->portnum == udev->portnum && in_hibernation(pd))
|
||||||
|
return fixup(pd);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is the card working now ? */
|
||||||
|
static inline int is_working(struct poseidon *pd)
|
||||||
|
{
|
||||||
|
return get_pm_count(pd) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct poseidon *get_pd(struct usb_interface *intf)
|
||||||
|
{
|
||||||
|
return usb_get_intfdata(intf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = get_pd(intf);
|
||||||
|
|
||||||
|
if (!pd)
|
||||||
|
return 0;
|
||||||
|
if (!is_working(pd)) {
|
||||||
|
if (get_pm_count(pd) <= 0 && !in_hibernation(pd)) {
|
||||||
|
pd->msg.event = PM_EVENT_AUTO_SUSPEND;
|
||||||
|
pd->pm_resume = NULL; /* a good guard */
|
||||||
|
printk(KERN_DEBUG "\n\t+ TLG2300 auto suspend +\n\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pd->msg = msg; /* save it here */
|
||||||
|
logpm(pd);
|
||||||
|
return pd->pm_suspend ? pd->pm_suspend(pd) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int poseidon_resume(struct usb_interface *intf)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = get_pd(intf);
|
||||||
|
|
||||||
|
if (!pd)
|
||||||
|
return 0;
|
||||||
|
printk(KERN_DEBUG "\n\t ++ TLG2300 resume ++\n\n");
|
||||||
|
|
||||||
|
if (!is_working(pd)) {
|
||||||
|
if (PM_EVENT_AUTO_SUSPEND == pd->msg.event)
|
||||||
|
pd->msg = PMSG_ON;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (in_hibernation(pd)) {
|
||||||
|
logpm(pd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
logpm(pd);
|
||||||
|
return pd->pm_resume ? pd->pm_resume(pd) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hibernation_resume(struct work_struct *w)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = container_of(w, struct poseidon, pm_work);
|
||||||
|
int count;
|
||||||
|
|
||||||
|
pd->msg.event = 0; /* clear it here */
|
||||||
|
pd->state &= ~POSEIDON_STATE_DISCONNECT;
|
||||||
|
|
||||||
|
/* set the new interface's reference */
|
||||||
|
count = get_autopm_ref(pd);
|
||||||
|
while (count--)
|
||||||
|
usb_autopm_get_interface(pd->interface);
|
||||||
|
|
||||||
|
/* resume the context */
|
||||||
|
logpm(pd);
|
||||||
|
if (pd->pm_resume)
|
||||||
|
pd->pm_resume(pd);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool check_firmware(struct usb_device *udev, int *down_firmware)
|
||||||
|
{
|
||||||
|
void *buf;
|
||||||
|
int ret;
|
||||||
|
struct cmd_firmware_vers_s *cmd_firm;
|
||||||
|
|
||||||
|
buf = kzalloc(sizeof(*cmd_firm) + sizeof(u32), GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
ret = usb_control_msg(udev,
|
||||||
|
usb_rcvctrlpipe(udev, 0),
|
||||||
|
REQ_GET_CMD | GET_FW_ID,
|
||||||
|
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
buf,
|
||||||
|
sizeof(*cmd_firm) + sizeof(u32),
|
||||||
|
USB_CTRL_GET_TIMEOUT);
|
||||||
|
kfree(buf);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
*down_firmware = 1;
|
||||||
|
return firmware_download(udev);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int poseidon_probe(struct usb_interface *interface,
|
||||||
|
const struct usb_device_id *id)
|
||||||
|
{
|
||||||
|
struct usb_device *udev = interface_to_usbdev(interface);
|
||||||
|
struct poseidon *pd = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
int new_one = 0;
|
||||||
|
|
||||||
|
/* download firmware */
|
||||||
|
check_firmware(udev, &ret);
|
||||||
|
if (ret)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Do I recovery from the hibernate ? */
|
||||||
|
pd = find_old_poseidon(udev);
|
||||||
|
if (!pd) {
|
||||||
|
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
|
||||||
|
if (!pd)
|
||||||
|
return -ENOMEM;
|
||||||
|
kref_init(&pd->kref);
|
||||||
|
set_map_flags(pd, udev);
|
||||||
|
new_one = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pd->udev = usb_get_dev(udev);
|
||||||
|
pd->interface = usb_get_intf(interface);
|
||||||
|
usb_set_intfdata(interface, pd);
|
||||||
|
|
||||||
|
if (new_one) {
|
||||||
|
struct device *dev = &interface->dev;
|
||||||
|
|
||||||
|
logpm(pd);
|
||||||
|
pd->country_code = 86;
|
||||||
|
mutex_init(&pd->lock);
|
||||||
|
|
||||||
|
/* register v4l2 device */
|
||||||
|
snprintf(pd->v4l2_dev.name, sizeof(pd->v4l2_dev.name), "%s %s",
|
||||||
|
dev->driver->name, dev_name(dev));
|
||||||
|
ret = v4l2_device_register(NULL, &pd->v4l2_dev);
|
||||||
|
|
||||||
|
/* register devices in directory /dev */
|
||||||
|
ret = pd_video_init(pd);
|
||||||
|
poseidon_audio_init(pd);
|
||||||
|
poseidon_fm_init(pd);
|
||||||
|
pd_dvb_usb_device_init(pd);
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&pd->device_list);
|
||||||
|
list_add_tail(&pd->device_list, &pd_device_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
device_init_wakeup(&udev->dev, 1);
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
pd->udev->autosuspend_disabled = 0;
|
||||||
|
pd->udev->autosuspend_delay = HZ * PM_SUSPEND_DELAY;
|
||||||
|
|
||||||
|
if (in_hibernation(pd)) {
|
||||||
|
INIT_WORK(&pd->pm_work, hibernation_resume);
|
||||||
|
schedule_work(&pd->pm_work);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void poseidon_disconnect(struct usb_interface *interface)
|
||||||
|
{
|
||||||
|
struct poseidon *pd = get_pd(interface);
|
||||||
|
|
||||||
|
if (!pd)
|
||||||
|
return;
|
||||||
|
logpm(pd);
|
||||||
|
if (in_hibernation(pd))
|
||||||
|
return;
|
||||||
|
|
||||||
|
mutex_lock(&pd->lock);
|
||||||
|
pd->state |= POSEIDON_STATE_DISCONNECT;
|
||||||
|
mutex_unlock(&pd->lock);
|
||||||
|
|
||||||
|
/* stop urb transferring */
|
||||||
|
stop_all_video_stream(pd);
|
||||||
|
dvb_stop_streaming(&pd->dvb_data);
|
||||||
|
|
||||||
|
/*unregister v4l2 device */
|
||||||
|
v4l2_device_unregister(&pd->v4l2_dev);
|
||||||
|
|
||||||
|
lock_kernel();
|
||||||
|
{
|
||||||
|
pd_dvb_usb_device_exit(pd);
|
||||||
|
poseidon_fm_exit(pd);
|
||||||
|
|
||||||
|
poseidon_audio_free(pd);
|
||||||
|
pd_video_exit(pd);
|
||||||
|
}
|
||||||
|
unlock_kernel();
|
||||||
|
|
||||||
|
usb_set_intfdata(interface, NULL);
|
||||||
|
kref_put(&pd->kref, poseidon_delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct usb_driver poseidon_driver = {
|
||||||
|
.name = "poseidon",
|
||||||
|
.probe = poseidon_probe,
|
||||||
|
.disconnect = poseidon_disconnect,
|
||||||
|
.id_table = id_table,
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
.suspend = poseidon_suspend,
|
||||||
|
.resume = poseidon_resume,
|
||||||
|
#endif
|
||||||
|
.supports_autosuspend = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init poseidon_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = usb_register(&poseidon_driver);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
register_pm_notifier(&pm_notifer);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit poseidon_exit(void)
|
||||||
|
{
|
||||||
|
log();
|
||||||
|
unregister_pm_notifier(&pm_notifer);
|
||||||
|
usb_deregister(&poseidon_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(poseidon_init);
|
||||||
|
module_exit(poseidon_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Telegent Systems");
|
||||||
|
MODULE_DESCRIPTION("For tlg2300-based USB device ");
|
||||||
|
MODULE_LICENSE("GPL");
|
351
drivers/media/video/tlg2300/pd-radio.c
Normal file
351
drivers/media/video/tlg2300/pd-radio.c
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/bitmap.h>
|
||||||
|
#include <linux/usb.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <media/v4l2-dev.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <media/v4l2-ioctl.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
|
||||||
|
#include "pd-common.h"
|
||||||
|
#include "vendorcmds.h"
|
||||||
|
|
||||||
|
static int set_frequency(struct poseidon *p, __u32 frequency);
|
||||||
|
static int poseidon_fm_close(struct file *filp);
|
||||||
|
static int poseidon_fm_open(struct file *filp);
|
||||||
|
|
||||||
|
#define TUNER_FREQ_MIN_FM 76000000
|
||||||
|
#define TUNER_FREQ_MAX_FM 108000000
|
||||||
|
|
||||||
|
static int poseidon_check_mode_radio(struct poseidon *p)
|
||||||
|
{
|
||||||
|
int ret, radiomode;
|
||||||
|
u32 status;
|
||||||
|
|
||||||
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
|
schedule_timeout(HZ/2);
|
||||||
|
ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = set_tuner_mode(p, TLG_MODE_FM_RADIO);
|
||||||
|
if (ret != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status);
|
||||||
|
radiomode = get_audio_std(TLG_MODE_FM_RADIO, p->country_code);
|
||||||
|
ret = send_set_req(p, TUNER_AUD_ANA_STD, radiomode, &status);
|
||||||
|
ret |= send_set_req(p, TUNER_AUD_MODE,
|
||||||
|
TLG_TUNE_TVAUDIO_MODE_STEREO, &status);
|
||||||
|
ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL,
|
||||||
|
ATV_AUDIO_RATE_48K, &status);
|
||||||
|
ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status);
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int pm_fm_suspend(struct poseidon *p)
|
||||||
|
{
|
||||||
|
logpm(p);
|
||||||
|
pm_alsa_suspend(p);
|
||||||
|
usb_set_interface(p->udev, 0, 0);
|
||||||
|
msleep(300);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pm_fm_resume(struct poseidon *p)
|
||||||
|
{
|
||||||
|
logpm(p);
|
||||||
|
poseidon_check_mode_radio(p);
|
||||||
|
set_frequency(p, p->radio_data.fm_freq);
|
||||||
|
pm_alsa_resume(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int poseidon_fm_open(struct file *filp)
|
||||||
|
{
|
||||||
|
struct video_device *vfd = video_devdata(filp);
|
||||||
|
struct poseidon *p = video_get_drvdata(vfd);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!p)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
mutex_lock(&p->lock);
|
||||||
|
if (p->state & POSEIDON_STATE_DISCONNECT) {
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->state && !(p->state & POSEIDON_STATE_FM)) {
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_autopm_get_interface(p->interface);
|
||||||
|
if (0 == p->state) {
|
||||||
|
p->country_code = country_code;
|
||||||
|
set_debug_mode(vfd, debug_mode);
|
||||||
|
|
||||||
|
ret = poseidon_check_mode_radio(p);
|
||||||
|
if (ret < 0) {
|
||||||
|
usb_autopm_put_interface(p->interface);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
p->state |= POSEIDON_STATE_FM;
|
||||||
|
}
|
||||||
|
p->radio_data.users++;
|
||||||
|
kref_get(&p->kref);
|
||||||
|
filp->private_data = p;
|
||||||
|
out:
|
||||||
|
mutex_unlock(&p->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int poseidon_fm_close(struct file *filp)
|
||||||
|
{
|
||||||
|
struct poseidon *p = filp->private_data;
|
||||||
|
struct radio_data *fm = &p->radio_data;
|
||||||
|
uint32_t status;
|
||||||
|
|
||||||
|
mutex_lock(&p->lock);
|
||||||
|
fm->users--;
|
||||||
|
if (0 == fm->users)
|
||||||
|
p->state &= ~POSEIDON_STATE_FM;
|
||||||
|
|
||||||
|
if (fm->is_radio_streaming && filp == p->file_for_stream) {
|
||||||
|
fm->is_radio_streaming = 0;
|
||||||
|
send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status);
|
||||||
|
}
|
||||||
|
usb_autopm_put_interface(p->interface);
|
||||||
|
mutex_unlock(&p->lock);
|
||||||
|
|
||||||
|
kref_put(&p->kref, poseidon_delete);
|
||||||
|
filp->private_data = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vidioc_querycap(struct file *file, void *priv,
|
||||||
|
struct v4l2_capability *v)
|
||||||
|
{
|
||||||
|
struct poseidon *p = file->private_data;
|
||||||
|
|
||||||
|
strlcpy(v->driver, "tele-radio", sizeof(v->driver));
|
||||||
|
strlcpy(v->card, "Telegent Poseidon", sizeof(v->card));
|
||||||
|
usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info));
|
||||||
|
v->version = KERNEL_VERSION(0, 0, 1);
|
||||||
|
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct v4l2_file_operations poseidon_fm_fops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.open = poseidon_fm_open,
|
||||||
|
.release = poseidon_fm_close,
|
||||||
|
.ioctl = video_ioctl2,
|
||||||
|
};
|
||||||
|
|
||||||
|
int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
|
||||||
|
{
|
||||||
|
struct tuner_fm_sig_stat_s fm_stat = {};
|
||||||
|
int ret, status, count = 5;
|
||||||
|
struct poseidon *p = file->private_data;
|
||||||
|
|
||||||
|
if (vt->index != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
vt->type = V4L2_TUNER_RADIO;
|
||||||
|
vt->capability = V4L2_TUNER_CAP_STEREO;
|
||||||
|
vt->rangelow = TUNER_FREQ_MIN_FM / 62500;
|
||||||
|
vt->rangehigh = TUNER_FREQ_MAX_FM / 62500;
|
||||||
|
vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
|
||||||
|
vt->audmode = V4L2_TUNER_MODE_STEREO;
|
||||||
|
vt->signal = 0;
|
||||||
|
vt->afc = 0;
|
||||||
|
|
||||||
|
mutex_lock(&p->lock);
|
||||||
|
ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
|
||||||
|
&fm_stat, &status, sizeof(fm_stat));
|
||||||
|
|
||||||
|
while (fm_stat.sig_lock_busy && count-- && !ret) {
|
||||||
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
|
schedule_timeout(HZ);
|
||||||
|
|
||||||
|
ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
|
||||||
|
&fm_stat, &status, sizeof(fm_stat));
|
||||||
|
}
|
||||||
|
mutex_unlock(&p->lock);
|
||||||
|
|
||||||
|
if (ret || status) {
|
||||||
|
vt->signal = 0;
|
||||||
|
} else if ((fm_stat.sig_present || fm_stat.sig_locked)
|
||||||
|
&& fm_stat.sig_strength == 0) {
|
||||||
|
vt->signal = 0xffff;
|
||||||
|
} else
|
||||||
|
vt->signal = (fm_stat.sig_strength * 255 / 10) << 8;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fm_get_freq(struct file *file, void *priv, struct v4l2_frequency *argp)
|
||||||
|
{
|
||||||
|
struct poseidon *p = file->private_data;
|
||||||
|
|
||||||
|
argp->frequency = p->radio_data.fm_freq;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_frequency(struct poseidon *p, __u32 frequency)
|
||||||
|
{
|
||||||
|
__u32 freq ;
|
||||||
|
int ret, status, radiomode;
|
||||||
|
|
||||||
|
mutex_lock(&p->lock);
|
||||||
|
|
||||||
|
radiomode = get_audio_std(TLG_MODE_FM_RADIO, p->country_code);
|
||||||
|
/*NTSC 8,PAL 2 */
|
||||||
|
ret = send_set_req(p, TUNER_AUD_ANA_STD, radiomode, &status);
|
||||||
|
|
||||||
|
freq = (frequency * 125) * 500 / 1000;/* kHZ */
|
||||||
|
if (freq < TUNER_FREQ_MIN_FM/1000 || freq > TUNER_FREQ_MAX_FM/1000) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error ;
|
||||||
|
ret = send_set_req(p, TAKE_REQUEST, 0, &status);
|
||||||
|
|
||||||
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
|
schedule_timeout(HZ/4);
|
||||||
|
if (!p->radio_data.is_radio_streaming) {
|
||||||
|
ret = send_set_req(p, TAKE_REQUEST, 0, &status);
|
||||||
|
ret = send_set_req(p, PLAY_SERVICE,
|
||||||
|
TLG_TUNE_PLAY_SVC_START, &status);
|
||||||
|
p->radio_data.is_radio_streaming = 1;
|
||||||
|
}
|
||||||
|
p->radio_data.fm_freq = frequency;
|
||||||
|
error:
|
||||||
|
mutex_unlock(&p->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fm_set_freq(struct file *file, void *priv, struct v4l2_frequency *argp)
|
||||||
|
{
|
||||||
|
struct poseidon *p = file->private_data;
|
||||||
|
|
||||||
|
p->file_for_stream = file;
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
p->pm_suspend = pm_fm_suspend;
|
||||||
|
p->pm_resume = pm_fm_resume;
|
||||||
|
#endif
|
||||||
|
return set_frequency(p, argp->frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv,
|
||||||
|
struct v4l2_control *arg)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tlg_fm_vidioc_exts_ctrl(struct file *file, void *fh,
|
||||||
|
struct v4l2_ext_controls *a)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv,
|
||||||
|
struct v4l2_control *arg)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tlg_fm_vidioc_queryctrl(struct file *file, void *priv,
|
||||||
|
struct v4l2_queryctrl *arg)
|
||||||
|
{
|
||||||
|
arg->minimum = 0;
|
||||||
|
arg->maximum = 65535;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
|
||||||
|
{
|
||||||
|
return vt->index > 0 ? -EINVAL : 0;
|
||||||
|
}
|
||||||
|
static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *va)
|
||||||
|
{
|
||||||
|
return (va->index != 0) ? -EINVAL : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
|
||||||
|
{
|
||||||
|
a->index = 0;
|
||||||
|
a->mode = 0;
|
||||||
|
a->capability = V4L2_AUDCAP_STEREO;
|
||||||
|
strcpy(a->name, "Radio");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vidioc_s_input(struct file *filp, void *priv, u32 i)
|
||||||
|
{
|
||||||
|
return (i != 0) ? -EINVAL : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vidioc_g_input(struct file *filp, void *priv, u32 *i)
|
||||||
|
{
|
||||||
|
return (*i != 0) ? -EINVAL : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = {
|
||||||
|
.vidioc_querycap = vidioc_querycap,
|
||||||
|
.vidioc_g_audio = vidioc_g_audio,
|
||||||
|
.vidioc_s_audio = vidioc_s_audio,
|
||||||
|
.vidioc_g_input = vidioc_g_input,
|
||||||
|
.vidioc_s_input = vidioc_s_input,
|
||||||
|
.vidioc_queryctrl = tlg_fm_vidioc_queryctrl,
|
||||||
|
.vidioc_g_ctrl = tlg_fm_vidioc_g_ctrl,
|
||||||
|
.vidioc_s_ctrl = tlg_fm_vidioc_s_ctrl,
|
||||||
|
.vidioc_s_ext_ctrls = tlg_fm_vidioc_exts_ctrl,
|
||||||
|
.vidioc_s_tuner = vidioc_s_tuner,
|
||||||
|
.vidioc_g_tuner = tlg_fm_vidioc_g_tuner,
|
||||||
|
.vidioc_g_frequency = fm_get_freq,
|
||||||
|
.vidioc_s_frequency = fm_set_freq,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct video_device poseidon_fm_template = {
|
||||||
|
.name = "Telegent-Radio",
|
||||||
|
.fops = &poseidon_fm_fops,
|
||||||
|
.minor = -1,
|
||||||
|
.release = video_device_release,
|
||||||
|
.ioctl_ops = &poseidon_fm_ioctl_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
int poseidon_fm_init(struct poseidon *p)
|
||||||
|
{
|
||||||
|
struct video_device *fm_dev;
|
||||||
|
|
||||||
|
fm_dev = vdev_init(p, &poseidon_fm_template);
|
||||||
|
if (fm_dev == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (video_register_device(fm_dev, VFL_TYPE_RADIO, -1) < 0) {
|
||||||
|
video_device_release(fm_dev);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
p->radio_data.fm_dev = fm_dev;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int poseidon_fm_exit(struct poseidon *p)
|
||||||
|
{
|
||||||
|
destroy_video_device(&p->radio_data.fm_dev);
|
||||||
|
return 0;
|
||||||
|
}
|
1648
drivers/media/video/tlg2300/pd-video.c
Normal file
1648
drivers/media/video/tlg2300/pd-video.c
Normal file
File diff suppressed because it is too large
Load diff
243
drivers/media/video/tlg2300/vendorcmds.h
Normal file
243
drivers/media/video/tlg2300/vendorcmds.h
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
#ifndef VENDOR_CMD_H_
|
||||||
|
#define VENDOR_CMD_H_
|
||||||
|
|
||||||
|
#define BULK_ALTERNATE_IFACE (2)
|
||||||
|
#define ISO_3K_BULK_ALTERNATE_IFACE (1)
|
||||||
|
#define REQ_SET_CMD (0X00)
|
||||||
|
#define REQ_GET_CMD (0X80)
|
||||||
|
|
||||||
|
enum tlg__analog_audio_standard {
|
||||||
|
TLG_TUNE_ASTD_NONE = 0x00000000,
|
||||||
|
TLG_TUNE_ASTD_A2 = 0x00000001,
|
||||||
|
TLG_TUNE_ASTD_NICAM = 0x00000002,
|
||||||
|
TLG_TUNE_ASTD_EIAJ = 0x00000004,
|
||||||
|
TLG_TUNE_ASTD_BTSC = 0x00000008,
|
||||||
|
TLG_TUNE_ASTD_FM_US = 0x00000010,
|
||||||
|
TLG_TUNE_ASTD_FM_EUR = 0x00000020,
|
||||||
|
TLG_TUNE_ASTD_ALL = 0x0000003f
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* identifiers for Custom Parameter messages.
|
||||||
|
* @typedef cmd_custom_param_id_t
|
||||||
|
*/
|
||||||
|
enum cmd_custom_param_id {
|
||||||
|
CUST_PARM_ID_NONE = 0x00,
|
||||||
|
CUST_PARM_ID_BRIGHTNESS_CTRL = 0x01,
|
||||||
|
CUST_PARM_ID_CONTRAST_CTRL = 0x02,
|
||||||
|
CUST_PARM_ID_HUE_CTRL = 0x03,
|
||||||
|
CUST_PARM_ID_SATURATION_CTRL = 0x04,
|
||||||
|
CUST_PARM_ID_AUDIO_SNR_THRESHOLD = 0x10,
|
||||||
|
CUST_PARM_ID_AUDIO_AGC_THRESHOLD = 0x11,
|
||||||
|
CUST_PARM_ID_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tuner_custom_parameter_s {
|
||||||
|
uint16_t param_id; /* Parameter identifier */
|
||||||
|
uint16_t param_value; /* Parameter value */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tuner_ber_rate_s {
|
||||||
|
uint32_t ber_rate; /* BER sample rate in seconds */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tuner_atv_sig_stat_s {
|
||||||
|
uint32_t sig_present;
|
||||||
|
uint32_t sig_locked;
|
||||||
|
uint32_t sig_lock_busy;
|
||||||
|
uint32_t sig_strength; /* milliDb */
|
||||||
|
uint32_t tv_audio_chan; /* mono/stereo/sap*/
|
||||||
|
uint32_t mvision_stat; /* macrovision status */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tuner_dtv_sig_stat_s {
|
||||||
|
uint32_t sig_present; /* Boolean*/
|
||||||
|
uint32_t sig_locked; /* Boolean */
|
||||||
|
uint32_t sig_lock_busy; /* Boolean (Can this time-out?) */
|
||||||
|
uint32_t sig_strength; /* milliDb*/
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tuner_fm_sig_stat_s {
|
||||||
|
uint32_t sig_present; /* Boolean*/
|
||||||
|
uint32_t sig_locked; /* Boolean */
|
||||||
|
uint32_t sig_lock_busy; /* Boolean */
|
||||||
|
uint32_t sig_stereo_mono;/* TBD*/
|
||||||
|
uint32_t sig_strength; /* milliDb*/
|
||||||
|
};
|
||||||
|
|
||||||
|
enum _tag_tlg_tune_srv_cmd {
|
||||||
|
TLG_TUNE_PLAY_SVC_START = 1,
|
||||||
|
TLG_TUNE_PLAY_SVC_STOP
|
||||||
|
};
|
||||||
|
|
||||||
|
enum _tag_tune_atv_audio_mode_caps {
|
||||||
|
TLG_TUNE_TVAUDIO_MODE_MONO = 0x00000001,
|
||||||
|
TLG_TUNE_TVAUDIO_MODE_STEREO = 0x00000002,
|
||||||
|
TLG_TUNE_TVAUDIO_MODE_LANG_A = 0x00000010,/* Primary language*/
|
||||||
|
TLG_TUNE_TVAUDIO_MODE_LANG_B = 0x00000020,/* 2nd avail language*/
|
||||||
|
TLG_TUNE_TVAUDIO_MODE_LANG_C = 0x00000040
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum _tag_tuner_atv_audio_rates {
|
||||||
|
ATV_AUDIO_RATE_NONE = 0x00,/* Audio not supported*/
|
||||||
|
ATV_AUDIO_RATE_32K = 0x01,/* Audio rate = 32 KHz*/
|
||||||
|
ATV_AUDIO_RATE_48K = 0x02, /* Audio rate = 48 KHz*/
|
||||||
|
ATV_AUDIO_RATE_31_25K = 0x04 /* Audio rate = 31.25KHz */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum _tag_tune_atv_vid_res_caps {
|
||||||
|
TLG_TUNE_VID_RES_NONE = 0x00000000,
|
||||||
|
TLG_TUNE_VID_RES_720 = 0x00000001,
|
||||||
|
TLG_TUNE_VID_RES_704 = 0x00000002,
|
||||||
|
TLG_TUNE_VID_RES_360 = 0x00000004
|
||||||
|
};
|
||||||
|
|
||||||
|
enum _tag_tuner_analog_video_format {
|
||||||
|
TLG_TUNER_VID_FORMAT_YUV = 0x00000001,
|
||||||
|
TLG_TUNER_VID_FORMAT_YCRCB = 0x00000002,
|
||||||
|
TLG_TUNER_VID_FORMAT_RGB_565 = 0x00000004,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum tlg_ext_audio_support {
|
||||||
|
TLG_EXT_AUDIO_NONE = 0x00,/* No external audio input supported */
|
||||||
|
TLG_EXT_AUDIO_LR = 0x01/* LR external audio inputs supported*/
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TLG_MODE_NONE = 0x00, /* No Mode specified*/
|
||||||
|
TLG_MODE_ANALOG_TV = 0x01, /* Analog Television mode*/
|
||||||
|
TLG_MODE_ANALOG_TV_UNCOMP = 0x01, /* Analog Television mode*/
|
||||||
|
TLG_MODE_ANALOG_TV_COMP = 0x02, /* Analog TV mode (compressed)*/
|
||||||
|
TLG_MODE_FM_RADIO = 0x04, /* FM Radio mode*/
|
||||||
|
TLG_MODE_DVB_T = 0x08, /* Digital TV (DVB-T)*/
|
||||||
|
};
|
||||||
|
|
||||||
|
enum tlg_signal_sources_t {
|
||||||
|
TLG_SIG_SRC_NONE = 0x00,/* Signal source not specified */
|
||||||
|
TLG_SIG_SRC_ANTENNA = 0x01,/* Signal src is: Antenna */
|
||||||
|
TLG_SIG_SRC_CABLE = 0x02,/* Signal src is: Coax Cable*/
|
||||||
|
TLG_SIG_SRC_SVIDEO = 0x04,/* Signal src is: S_VIDEO */
|
||||||
|
TLG_SIG_SRC_COMPOSITE = 0x08 /* Signal src is: Composite Video */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum tuner_analog_video_standard {
|
||||||
|
TLG_TUNE_VSTD_NONE = 0x00000000,
|
||||||
|
TLG_TUNE_VSTD_NTSC_M = 0x00000001,
|
||||||
|
TLG_TUNE_VSTD_NTSC_M_J = 0x00000002,/* Japan */
|
||||||
|
TLG_TUNE_VSTD_PAL_B = 0x00000010,
|
||||||
|
TLG_TUNE_VSTD_PAL_D = 0x00000020,
|
||||||
|
TLG_TUNE_VSTD_PAL_G = 0x00000040,
|
||||||
|
TLG_TUNE_VSTD_PAL_H = 0x00000080,
|
||||||
|
TLG_TUNE_VSTD_PAL_I = 0x00000100,
|
||||||
|
TLG_TUNE_VSTD_PAL_M = 0x00000200,
|
||||||
|
TLG_TUNE_VSTD_PAL_N = 0x00000400,
|
||||||
|
TLG_TUNE_VSTD_SECAM_B = 0x00001000,
|
||||||
|
TLG_TUNE_VSTD_SECAM_D = 0x00002000,
|
||||||
|
TLG_TUNE_VSTD_SECAM_G = 0x00004000,
|
||||||
|
TLG_TUNE_VSTD_SECAM_H = 0x00008000,
|
||||||
|
TLG_TUNE_VSTD_SECAM_K = 0x00010000,
|
||||||
|
TLG_TUNE_VSTD_SECAM_K1 = 0x00020000,
|
||||||
|
TLG_TUNE_VSTD_SECAM_L = 0x00040000,
|
||||||
|
TLG_TUNE_VSTD_SECAM_L1 = 0x00080000,
|
||||||
|
TLG_TUNE_VSTD_PAL_N_COMBO = 0x00100000
|
||||||
|
};
|
||||||
|
|
||||||
|
enum tlg_mode_caps {
|
||||||
|
TLG_MODE_CAPS_NONE = 0x00, /* No Mode specified */
|
||||||
|
TLG_MODE_CAPS_ANALOG_TV_UNCOMP = 0x01, /* Analog TV mode */
|
||||||
|
TLG_MODE_CAPS_ANALOG_TV_COMP = 0x02, /* Analog TV (compressed)*/
|
||||||
|
TLG_MODE_CAPS_FM_RADIO = 0x04, /* FM Radio mode */
|
||||||
|
TLG_MODE_CAPS_DVB_T = 0x08, /* Digital TV (DVB-T) */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum poseidon_vendor_cmds {
|
||||||
|
LAST_CMD_STAT = 0x00,
|
||||||
|
GET_CHIP_ID = 0x01,
|
||||||
|
GET_FW_ID = 0x02,
|
||||||
|
PRODUCT_CAPS = 0x03,
|
||||||
|
|
||||||
|
TUNE_MODE_CAP_ATV = 0x10,
|
||||||
|
TUNE_MODE_CAP_ATVCOMP = 0X10,
|
||||||
|
TUNE_MODE_CAP_DVBT = 0x10,
|
||||||
|
TUNE_MODE_CAP_FM = 0x10,
|
||||||
|
TUNE_MODE_SELECT = 0x11,
|
||||||
|
TUNE_FREQ_SELECT = 0x12,
|
||||||
|
SGNL_SRC_SEL = 0x13,
|
||||||
|
|
||||||
|
VIDEO_STD_SEL = 0x14,
|
||||||
|
VIDEO_STREAM_FMT_SEL = 0x15,
|
||||||
|
VIDEO_ROSOLU_AVAIL = 0x16,
|
||||||
|
VIDEO_ROSOLU_SEL = 0x17,
|
||||||
|
VIDEO_CONT_PROTECT = 0x20,
|
||||||
|
|
||||||
|
VCR_TIMING_MODSEL = 0x21,
|
||||||
|
EXT_AUDIO_CAP = 0x22,
|
||||||
|
EXT_AUDIO_SEL = 0x23,
|
||||||
|
TEST_PATTERN_SEL = 0x24,
|
||||||
|
VBI_DATA_SEL = 0x25,
|
||||||
|
AUDIO_SAMPLE_RATE_CAP = 0x28,
|
||||||
|
AUDIO_SAMPLE_RATE_SEL = 0x29,
|
||||||
|
TUNER_AUD_MODE = 0x2a,
|
||||||
|
TUNER_AUD_MODE_AVAIL = 0x2b,
|
||||||
|
TUNER_AUD_ANA_STD = 0x2c,
|
||||||
|
TUNER_CUSTOM_PARAMETER = 0x2f,
|
||||||
|
|
||||||
|
DVBT_TUNE_MODE_SEL = 0x30,
|
||||||
|
DVBT_BANDW_CAP = 0x31,
|
||||||
|
DVBT_BANDW_SEL = 0x32,
|
||||||
|
DVBT_GUARD_INTERV_CAP = 0x33,
|
||||||
|
DVBT_GUARD_INTERV_SEL = 0x34,
|
||||||
|
DVBT_MODULATION_CAP = 0x35,
|
||||||
|
DVBT_MODULATION_SEL = 0x36,
|
||||||
|
DVBT_INNER_FEC_RATE_CAP = 0x37,
|
||||||
|
DVBT_INNER_FEC_RATE_SEL = 0x38,
|
||||||
|
DVBT_TRANS_MODE_CAP = 0x39,
|
||||||
|
DVBT_TRANS_MODE_SEL = 0x3a,
|
||||||
|
DVBT_SEARCH_RANG = 0x3c,
|
||||||
|
|
||||||
|
TUNER_SETUP_ANALOG = 0x40,
|
||||||
|
TUNER_SETUP_DIGITAL = 0x41,
|
||||||
|
TUNER_SETUP_FM_RADIO = 0x42,
|
||||||
|
TAKE_REQUEST = 0x43, /* Take effect of the command */
|
||||||
|
PLAY_SERVICE = 0x44, /* Play start or Play stop */
|
||||||
|
TUNER_STATUS = 0x45,
|
||||||
|
TUNE_PROP_DVBT = 0x46,
|
||||||
|
ERR_RATE_STATS = 0x47,
|
||||||
|
TUNER_BER_RATE = 0x48,
|
||||||
|
|
||||||
|
SCAN_CAPS = 0x50,
|
||||||
|
SCAN_SETUP = 0x51,
|
||||||
|
SCAN_SERVICE = 0x52,
|
||||||
|
SCAN_STATS = 0x53,
|
||||||
|
|
||||||
|
PID_SET = 0x58,
|
||||||
|
PID_UNSET = 0x59,
|
||||||
|
PID_LIST = 0x5a,
|
||||||
|
|
||||||
|
IRD_CAP = 0x60,
|
||||||
|
IRD_MODE_SEL = 0x61,
|
||||||
|
IRD_SETUP = 0x62,
|
||||||
|
|
||||||
|
PTM_MODE_CAP = 0x70,
|
||||||
|
PTM_MODE_SEL = 0x71,
|
||||||
|
PTM_SERVICE = 0x72,
|
||||||
|
TUNER_REG_SCRIPT = 0x73,
|
||||||
|
CMD_CHIP_RST = 0x74,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum tlg_bw {
|
||||||
|
TLG_BW_5 = 5,
|
||||||
|
TLG_BW_6 = 6,
|
||||||
|
TLG_BW_7 = 7,
|
||||||
|
TLG_BW_8 = 8,
|
||||||
|
TLG_BW_12 = 12,
|
||||||
|
TLG_BW_15 = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cmd_firmware_vers_s {
|
||||||
|
uint8_t fw_rev_major;
|
||||||
|
uint8_t fw_rev_minor;
|
||||||
|
uint16_t fw_patch;
|
||||||
|
};
|
||||||
|
#endif /* VENDOR_CMD_H_ */
|
Loading…
Reference in a new issue