[media] tlg2300: remove deprecated staging driver

This driver hasn't been tested in a long, long time. The company that made
this chip has gone bust many years ago and hardware using this chip is next
to impossible to find.

This driver needs to be converted to newer media frameworks but due to the
lack of hardware that's going to be impossible. Since cheap alternatives are
easily available, there is little point in keeping this driver alive.

This driver is already deprecated, so now remove it altogether.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Cc: Huang Shijie <shijie8@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
This commit is contained in:
Hans Verkuil 2014-12-23 09:11:48 -03:00 committed by Mauro Carvalho Chehab
parent 08272db62a
commit 3f7a3f6ecf
12 changed files with 0 additions and 3935 deletions

View File

@ -8393,12 +8393,6 @@ F: kernel/time/clocksource.c
F: kernel/time/time*.c
F: kernel/time/ntp.c
TLG2300 VIDEO4LINUX-2 DRIVER
M: Huang Shijie <shijie8@gmail.com>
M: Hans Verkuil <hverkuil@xs4all.nl>
S: Odd Fixes
F: drivers/media/usb/tlg2300/
SC1200 WDT DRIVER
M: Zwane Mwaikambo <zwanem@gmail.com>
S: Maintained

View File

@ -27,8 +27,6 @@ source "drivers/staging/media/davinci_vpfe/Kconfig"
source "drivers/staging/media/dt3155v4l/Kconfig"
source "drivers/staging/media/tlg2300/Kconfig"
source "drivers/staging/media/mn88472/Kconfig"
source "drivers/staging/media/mn88473/Kconfig"

View File

@ -7,6 +7,5 @@ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
obj-$(CONFIG_DVB_MN88472) += mn88472/
obj-$(CONFIG_DVB_MN88473) += mn88473/
obj-y += parport/
obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
obj-y += vino/

View File

@ -1,21 +0,0 @@
config VIDEO_TLG2300
tristate "Telegent TLG2300 USB video capture support (Deprecated)"
depends on VIDEO_DEV && I2C && SND && DVB_CORE
depends on MEDIA_USB_SUPPORT
select VIDEO_TUNER
select VIDEO_TVEEPROM
depends on RC_CORE
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.
This driver is deprecated and will be removed soon. If you have
hardware for this and you want to work on this driver, then contact
the linux-media mailinglist.
To compile this driver as a module, choose M here: the
module will be called poseidon

View File

@ -1,9 +0,0 @@
poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o
obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o
ccflags-y += -Idrivers/media/i2c
ccflags-y += -Idrivers/media/tuners
ccflags-y += -Idrivers/media/dvb-core
ccflags-y += -Idrivers/media/dvb-frontends

View File

@ -1,337 +0,0 @@
#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/vmalloc.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
#include <linux/gfp.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_new(&p->interface->dev, -1, "Telegent",
THIS_MODULE, 0, &card);
if (ret != 0)
return ret;
ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
if (ret < 0) {
snd_card_free(card);
return ret;
}
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;
}

View File

@ -1,270 +0,0 @@
#ifndef PD_COMMON_H
#define PD_COMMON_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 <media/v4l2-ctrls.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_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 (45000000U)
#define TUNER_FREQ_MAX (862000000U)
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;
struct v4l2_ctrl_handler ctrl_handler;
/* 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;
unsigned int is_radio_streaming;
int pre_emphasis;
struct video_device fm_dev;
struct v4l2_ctrl_handler ctrl_handler;
};
#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 dtv_frontend_properties 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;
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 *);
/* 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);
/* 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);
extern int debug_mode;
#ifdef CONFIG_PM
#define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE)
#else
#define in_hibernation(pd) (0)
#endif
#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)
#endif

View File

@ -1,597 +0,0 @@
#include "pd-common.h"
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/time.h>
#include <linux/dvb/dmx.h>
#include <linux/delay.h>
#include <linux/gfp.h>
#include "vendorcmds.h"
#include <linux/sched.h>
#include <linux/atomic.h>
static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb);
static int dvb_bandwidth[][2] = {
{ TLG_BW_8, 8000000 },
{ TLG_BW_7, 7000000 },
{ TLG_BW_6, 6000000 }
};
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;
}
#ifdef CONFIG_PM
static void poseidon_fe_release(struct dvb_frontend *fe)
{
struct poseidon *pd = fe->demodulator_priv;
pd->pm_suspend = NULL;
pd->pm_resume = NULL;
}
#else
#define poseidon_fe_release 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 dtv_frontend_properties *fep = &fe->dtv_property_cache;
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->bandwidth_hz == 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);
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 dtv_frontend_properties));
return 0;
}
static int poseidon_get_fe(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
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 = {
.delsys = { SYS_DVBT },
.info = {
.name = "Poseidon DVB-T",
.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;
}

View File

@ -1,553 +0,0 @@
/*
* 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/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 "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");
#define TLG2300_FIRMWARE "tlg2300_firmware.bin"
static const char *firmware_name = TLG2300_FIRMWARE;
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;
}
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 = kmemdup(fw->data, fwlength, GFP_KERNEL);
if (!fwbuf) {
ret = -ENOMEM;
goto out;
}
max_packet_size = le16_to_cpu(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;
}
static inline struct poseidon *get_pd(struct usb_interface *intf)
{
return usb_get_intfdata(intf);
}
#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) +
!list_empty(&pd->radio_data.fm_dev.fh_list);
}
/* 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 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 "TLG2300 auto suspend\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 "TLG2300 resume\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);
}
#else /* CONFIG_PM is not enabled: */
static inline struct poseidon *find_old_poseidon(struct usb_device *udev)
{
return NULL;
}
static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
{
}
#endif
static int check_firmware(struct usb_device *udev)
{
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)
return firmware_download(udev);
return 0;
}
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 */
ret = check_firmware(udev);
if (ret)
return ret;
/* 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) {
logpm(pd);
mutex_init(&pd->lock);
/* register v4l2 device */
ret = v4l2_device_register(&interface->dev, &pd->v4l2_dev);
if (ret)
goto err_v4l2;
/* register devices in directory /dev */
ret = pd_video_init(pd);
if (ret)
goto err_video;
ret = poseidon_audio_init(pd);
if (ret)
goto err_audio;
ret = poseidon_fm_init(pd);
if (ret)
goto err_fm;
ret = pd_dvb_usb_device_init(pd);
if (ret)
goto err_dvb;
INIT_LIST_HEAD(&pd->device_list);
list_add_tail(&pd->device_list, &pd_device_list);
}
device_init_wakeup(&udev->dev, 1);
#ifdef CONFIG_PM
pm_runtime_set_autosuspend_delay(&pd->udev->dev,
1000 * PM_SUSPEND_DELAY);
usb_enable_autosuspend(pd->udev);
if (in_hibernation(pd)) {
INIT_WORK(&pd->pm_work, hibernation_resume);
schedule_work(&pd->pm_work);
}
#endif
return 0;
err_dvb:
poseidon_fm_exit(pd);
err_fm:
poseidon_audio_free(pd);
err_audio:
pd_video_exit(pd);
err_video:
v4l2_device_unregister(&pd->v4l2_dev);
err_v4l2:
usb_put_intf(pd->interface);
usb_put_dev(pd->udev);
kfree(pd);
return ret;
}
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);
pd_dvb_usb_device_exit(pd);
poseidon_fm_exit(pd);
poseidon_audio_free(pd);
pd_video_exit(pd);
usb_set_intfdata(interface, NULL);
kref_put(&pd->kref, poseidon_delete);
}
static 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");
MODULE_VERSION("0.0.2");
MODULE_FIRMWARE(TLG2300_FIRMWARE);

View File

@ -1,336 +0,0 @@
#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/mm.h>
#include <linux/mutex.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fh.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 76000000U
#define TUNER_FREQ_MAX_FM 108000000U
#define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1)
static int preemphasis[MAX_PREEMPHASIS] = {
TLG_TUNE_ASTD_NONE, /* V4L2_PREEMPHASIS_DISABLED */
TLG_TUNE_ASTD_FM_EUR, /* V4L2_PREEMPHASIS_50_uS */
TLG_TUNE_ASTD_FM_US, /* V4L2_PREEMPHASIS_75_uS */
};
static int poseidon_check_mode_radio(struct poseidon *p)
{
int ret;
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);
ret = send_set_req(p, TUNER_AUD_ANA_STD,
p->radio_data.pre_emphasis, &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 poseidon *p = video_drvdata(filp);
int ret = 0;
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;
}
ret = v4l2_fh_open(filp);
if (ret)
goto out;
usb_autopm_get_interface(p->interface);
if (0 == p->state) {
/* default pre-emphasis */
if (p->radio_data.pre_emphasis == 0)
p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR;
ret = poseidon_check_mode_radio(p);
if (ret < 0) {
usb_autopm_put_interface(p->interface);
goto out;
}
p->state |= POSEIDON_STATE_FM;
}
kref_get(&p->kref);
out:
mutex_unlock(&p->lock);
return ret;
}
static int poseidon_fm_close(struct file *filp)
{
struct poseidon *p = video_drvdata(filp);
struct radio_data *fm = &p->radio_data;
uint32_t status;
mutex_lock(&p->lock);
if (v4l2_fh_is_singular_file(filp))
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);
return v4l2_fh_release(filp);
}
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *v)
{
struct poseidon *p = video_drvdata(file);
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->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
/* Report all capabilities of the USB device */
v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS |
V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE |
V4L2_CAP_AUDIO | V4L2_CAP_STREAMING |
V4L2_CAP_READWRITE;
return 0;
}
static const struct v4l2_file_operations poseidon_fm_fops = {
.owner = THIS_MODULE,
.open = poseidon_fm_open,
.release = poseidon_fm_close,
.poll = v4l2_ctrl_poll,
.unlocked_ioctl = video_ioctl2,
};
static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *vt)
{
struct poseidon *p = video_drvdata(file);
struct tuner_fm_sig_stat_s fm_stat = {};
int ret, status, count = 5;
if (vt->index != 0)
return -EINVAL;
vt->type = V4L2_TUNER_RADIO;
vt->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
vt->rangelow = TUNER_FREQ_MIN_FM * 2 / 125;
vt->rangehigh = TUNER_FREQ_MAX_FM * 2 / 125;
vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
vt->audmode = V4L2_TUNER_MODE_STEREO;
vt->signal = 0;
vt->afc = 0;
strlcpy(vt->name, "Radio", sizeof(vt->name));
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;
}
static int fm_get_freq(struct file *file, void *priv,
struct v4l2_frequency *argp)
{
struct poseidon *p = video_drvdata(file);
if (argp->tuner)
return -EINVAL;
argp->frequency = p->radio_data.fm_freq;
return 0;
}
static int set_frequency(struct poseidon *p, __u32 frequency)
{
__u32 freq ;
int ret, status;
mutex_lock(&p->lock);
ret = send_set_req(p, TUNER_AUD_ANA_STD,
p->radio_data.pre_emphasis, &status);
freq = (frequency * 125) / 2; /* Hz */
freq = clamp(freq, TUNER_FREQ_MIN_FM, TUNER_FREQ_MAX_FM);
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 = freq * 2 / 125;
error:
mutex_unlock(&p->lock);
return ret;
}
static int fm_set_freq(struct file *file, void *priv,
const struct v4l2_frequency *argp)
{
struct poseidon *p = video_drvdata(file);
if (argp->tuner)
return -EINVAL;
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);
}
static int tlg_fm_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct poseidon *p = container_of(ctrl->handler, struct poseidon,
radio_data.ctrl_handler);
int pre_emphasis;
u32 status;
switch (ctrl->id) {
case V4L2_CID_TUNE_PREEMPHASIS:
pre_emphasis = preemphasis[ctrl->val];
send_set_req(p, TUNER_AUD_ANA_STD, pre_emphasis, &status);
p->radio_data.pre_emphasis = pre_emphasis;
return 0;
}
return -EINVAL;
}
static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *vt)
{
return vt->index > 0 ? -EINVAL : 0;
}
static const struct v4l2_ctrl_ops tlg_fm_ctrl_ops = {
.s_ctrl = tlg_fm_s_ctrl,
};
static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.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,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static struct video_device poseidon_fm_template = {
.name = "Telegent-Radio",
.fops = &poseidon_fm_fops,
.minor = -1,
.release = video_device_release_empty,
.ioctl_ops = &poseidon_fm_ioctl_ops,
};
int poseidon_fm_init(struct poseidon *p)
{
struct video_device *vfd = &p->radio_data.fm_dev;
struct v4l2_ctrl_handler *hdl = &p->radio_data.ctrl_handler;
*vfd = poseidon_fm_template;
set_frequency(p, TUNER_FREQ_MIN_FM);
v4l2_ctrl_handler_init(hdl, 1);
v4l2_ctrl_new_std_menu(hdl, &tlg_fm_ctrl_ops, V4L2_CID_TUNE_PREEMPHASIS,
V4L2_PREEMPHASIS_75_uS, 0, V4L2_PREEMPHASIS_50_uS);
if (hdl->error) {
v4l2_ctrl_handler_free(hdl);
return hdl->error;
}
vfd->v4l2_dev = &p->v4l2_dev;
vfd->ctrl_handler = hdl;
video_set_drvdata(vfd, p);
return video_register_device(vfd, VFL_TYPE_RADIO, -1);
}
int poseidon_fm_exit(struct poseidon *p)
{
video_unregister_device(&p->radio_data.fm_dev);
v4l2_ctrl_handler_free(&p->radio_data.ctrl_handler);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,243 +0,0 @@
#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_ */