📄 bttv-driver.c
字号:
static const struct v4l2_queryctrl no_ctl = {
.name = "42",
.flags = V4L2_CTRL_FLAG_DISABLED,
};
static const struct v4l2_queryctrl bttv_ctls[] = {
/* --- video --- */
{
.id = V4L2_CID_BRIGHTNESS,
.name = "Brightness",
.minimum = 0,
.maximum = 65535,
.step = 256,
.default_value = 32768,
.type = V4L2_CTRL_TYPE_INTEGER,
},{
.id = V4L2_CID_CONTRAST,
.name = "Contrast",
.minimum = 0,
.maximum = 65535,
.step = 128,
.default_value = 32768,
.type = V4L2_CTRL_TYPE_INTEGER,
},{
.id = V4L2_CID_SATURATION,
.name = "Saturation",
.minimum = 0,
.maximum = 65535,
.step = 128,
.default_value = 32768,
.type = V4L2_CTRL_TYPE_INTEGER,
},{
.id = V4L2_CID_HUE,
.name = "Hue",
.minimum = 0,
.maximum = 65535,
.step = 256,
.default_value = 32768,
.type = V4L2_CTRL_TYPE_INTEGER,
},
/* --- audio --- */
{
.id = V4L2_CID_AUDIO_MUTE,
.name = "Mute",
.minimum = 0,
.maximum = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},{
.id = V4L2_CID_AUDIO_VOLUME,
.name = "Volume",
.minimum = 0,
.maximum = 65535,
.step = 65535/100,
.default_value = 65535,
.type = V4L2_CTRL_TYPE_INTEGER,
},{
.id = V4L2_CID_AUDIO_BALANCE,
.name = "Balance",
.minimum = 0,
.maximum = 65535,
.step = 65535/100,
.default_value = 32768,
.type = V4L2_CTRL_TYPE_INTEGER,
},{
.id = V4L2_CID_AUDIO_BASS,
.name = "Bass",
.minimum = 0,
.maximum = 65535,
.step = 65535/100,
.default_value = 32768,
.type = V4L2_CTRL_TYPE_INTEGER,
},{
.id = V4L2_CID_AUDIO_TREBLE,
.name = "Treble",
.minimum = 0,
.maximum = 65535,
.step = 65535/100,
.default_value = 32768,
.type = V4L2_CTRL_TYPE_INTEGER,
},
/* --- private --- */
{
.id = V4L2_CID_PRIVATE_CHROMA_AGC,
.name = "chroma agc",
.minimum = 0,
.maximum = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},{
.id = V4L2_CID_PRIVATE_COMBFILTER,
.name = "combfilter",
.minimum = 0,
.maximum = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},{
.id = V4L2_CID_PRIVATE_AUTOMUTE,
.name = "automute",
.minimum = 0,
.maximum = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},{
.id = V4L2_CID_PRIVATE_LUMAFILTER,
.name = "luma decimation filter",
.minimum = 0,
.maximum = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},{
.id = V4L2_CID_PRIVATE_AGC_CRUSH,
.name = "agc crush",
.minimum = 0,
.maximum = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},{
.id = V4L2_CID_PRIVATE_VCR_HACK,
.name = "vcr hack",
.minimum = 0,
.maximum = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},{
.id = V4L2_CID_PRIVATE_WHITECRUSH_UPPER,
.name = "whitecrush upper",
.minimum = 0,
.maximum = 255,
.step = 1,
.default_value = 0xCF,
.type = V4L2_CTRL_TYPE_INTEGER,
},{
.id = V4L2_CID_PRIVATE_WHITECRUSH_LOWER,
.name = "whitecrush lower",
.minimum = 0,
.maximum = 255,
.step = 1,
.default_value = 0x7F,
.type = V4L2_CTRL_TYPE_INTEGER,
}
};
const int BTTV_CTLS = ARRAY_SIZE(bttv_ctls);
/* ----------------------------------------------------------------------- */
/* resource management */
static
int check_alloc_btres(struct bttv *btv, struct bttv_fh *fh, int bit)
{
if (fh->resources & bit)
/* have it already allocated */
return 1;
/* is it free? */
down(&btv->reslock);
if (btv->resources & bit) {
/* no, someone else uses it */
up(&btv->reslock);
return 0;
}
/* it's free, grab it */
fh->resources |= bit;
btv->resources |= bit;
up(&btv->reslock);
return 1;
}
static
int check_btres(struct bttv_fh *fh, int bit)
{
return (fh->resources & bit);
}
static
int locked_btres(struct bttv *btv, int bit)
{
return (btv->resources & bit);
}
static
void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits)
{
#if 1 /* DEBUG */
if ((fh->resources & bits) != bits) {
/* trying to free ressources not allocated by us ... */
printk("bttv: BUG! (btres)\n");
}
#endif
down(&btv->reslock);
fh->resources &= ~bits;
btv->resources &= ~bits;
up(&btv->reslock);
}
/* ----------------------------------------------------------------------- */
/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC */
/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C
PLL_X = Reference pre-divider (0=1, 1=2)
PLL_C = Post divider (0=6, 1=4)
PLL_I = Integer input
PLL_F = Fractional input
F_input = 28.636363 MHz:
PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
*/
static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
{
unsigned char fl, fh, fi;
/* prevent overflows */
fin/=4;
fout/=4;
fout*=12;
fi=fout/fin;
fout=(fout%fin)*256;
fh=fout/fin;
fout=(fout%fin)*256;
fl=fout/fin;
btwrite(fl, BT848_PLL_F_LO);
btwrite(fh, BT848_PLL_F_HI);
btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
}
static void set_pll(struct bttv *btv)
{
int i;
if (!btv->pll.pll_crystal)
return;
if (btv->pll.pll_ofreq == btv->pll.pll_current) {
dprintk("bttv%d: PLL: no change required\n",btv->c.nr);
return;
}
if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
/* no PLL needed */
if (btv->pll.pll_current == 0)
return;
vprintk(KERN_INFO "bttv%d: PLL can sleep, using XTAL (%d).\n",
btv->c.nr,btv->pll.pll_ifreq);
btwrite(0x00,BT848_TGCTRL);
btwrite(0x00,BT848_PLL_XCI);
btv->pll.pll_current = 0;
return;
}
vprintk(KERN_INFO "bttv%d: PLL: %d => %d ",btv->c.nr,
btv->pll.pll_ifreq, btv->pll.pll_ofreq);
set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
for (i=0; i<10; i++) {
/* Let other people run while the PLL stabilizes */
vprintk(".");
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ/10);
if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
btwrite(0,BT848_DSTATUS);
} else {
btwrite(0x08,BT848_TGCTRL);
btv->pll.pll_current = btv->pll.pll_ofreq;
vprintk(" ok\n");
return;
}
}
btv->pll.pll_current = -1;
vprintk("failed\n");
return;
}
/* used to switch between the bt848's analog/digital video capture modes */
void bt848A_set_timing(struct bttv *btv)
{
int i, len;
int table_idx = bttv_tvnorms[btv->tvnorm].sram;
int fsc = bttv_tvnorms[btv->tvnorm].Fsc;
if (UNSET == bttv_tvcards[btv->c.type].muxsel[btv->input]) {
dprintk("bttv%d: load digital timing table (table_idx=%d)\n",
btv->c.nr,table_idx);
/* timing change...reset timing generator address */
btwrite(0x00, BT848_TGCTRL);
btwrite(0x02, BT848_TGCTRL);
btwrite(0x00, BT848_TGCTRL);
len=SRAM_Table[table_idx][0];
for(i = 1; i <= len; i++)
btwrite(SRAM_Table[table_idx][i],BT848_TGLB);
btv->pll.pll_ofreq = 27000000;
set_pll(btv);
btwrite(0x11, BT848_TGCTRL);
btwrite(0x41, BT848_DVSIF);
} else {
btv->pll.pll_ofreq = fsc;
set_pll(btv);
btwrite(0x0, BT848_DVSIF);
}
}
/* ----------------------------------------------------------------------- */
static void bt848_bright(struct bttv *btv, int bright)
{
int value;
btv->bright = bright;
/* We want -128 to 127 we get 0-65535 */
value = (bright >> 8) - 128;
btwrite(value & 0xff, BT848_BRIGHT);
}
static void bt848_hue(struct bttv *btv, int hue)
{
int value;
btv->hue = hue;
/* -128 to 127 */
value = (hue >> 8) - 128;
btwrite(value & 0xff, BT848_HUE);
}
static void bt848_contrast(struct bttv *btv, int cont)
{
int value,hibit;
btv->contrast = cont;
/* 0-511 */
value = (cont >> 7);
hibit = (value >> 6) & 4;
btwrite(value & 0xff, BT848_CONTRAST_LO);
btaor(hibit, ~4, BT848_E_CONTROL);
btaor(hibit, ~4, BT848_O_CONTROL);
}
static void bt848_sat(struct bttv *btv, int color)
{
int val_u,val_v,hibits;
btv->saturation = color;
/* 0-511 for the color */
val_u = color >> 7;
val_v = ((color>>7)*180L)/254;
hibits = (val_u >> 7) & 2;
hibits |= (val_v >> 8) & 1;
btwrite(val_u & 0xff, BT848_SAT_U_LO);
btwrite(val_v & 0xff, BT848_SAT_V_LO);
btaor(hibits, ~3, BT848_E_CONTROL);
btaor(hibits, ~3, BT848_O_CONTROL);
}
/* ----------------------------------------------------------------------- */
static int
video_mux(struct bttv *btv, unsigned int input)
{
int mux,mask2;
if (input >= bttv_tvcards[btv->c.type].video_inputs)
return -EINVAL;
/* needed by RemoteVideo MX */
mask2 = bttv_tvcards[btv->c.type].gpiomask2;
if (mask2)
gpio_inout(mask2,mask2);
if (input == btv->svhs) {
btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
} else {
btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
}
mux = bttv_tvcards[btv->c.type].muxsel[input] & 3;
btaor(mux<<5, ~(3<<5), BT848_IFORM);
dprintk(KERN_DEBUG "bttv%d: video mux: input=%d mux=%d\n",
btv->c.nr,input,mux);
/* card specific hook */
if(bttv_tvcards[btv->c.type].muxsel_hook)
bttv_tvcards[btv->c.type].muxsel_hook (btv, input);
return 0;
}
static char *audio_modes[] = {
"audio: tuner", "audio: radio", "audio: extern",
"audio: intern", "audio: off"
};
static int
audio_mux(struct bttv *btv, int mode)
{
int val,mux,i2c_mux,signal;
gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
bttv_tvcards[btv->c.type].gpiomask);
signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
switch (mode) {
case AUDIO_MUTE:
btv->audio |= AUDIO_MUTE;
break;
case AUDIO_UNMUTE:
btv->audio &= ~AUDIO_MUTE;
break;
case AUDIO_TUNER:
case AUDIO_RADIO:
case AUDIO_EXTERN:
case AUDIO_INTERN:
btv->audio &= AUDIO_MUTE;
btv->audio |= mode;
}
i2c_mux = mux = (btv->audio & AUDIO_MUTE) ? AUDIO_OFF : btv->audio;
if (btv->opt_automute && !signal && !btv->radio_user)
mux = AUDIO_OFF;
#if 0
printk("bttv%d: amux: mode=%d audio=%d signal=%s mux=%d/%d irq=%s\n",
btv->c.nr, mode, btv->audio, signal ? "yes" : "no",
mux, i2c_mux, in_interrupt() ? "yes" : "no");
#endif
val = bttv_tvcards[btv->c.type].audiomux[mux];
gpio_bits(bttv_tvcards[btv->c.type].gpiomask,val);
if (bttv_gpio)
bttv_gpio_tracking(btv,audio_modes[mux]);
if (!in_interrupt())
bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(i2c_mux));
return 0;
}
static void
i2c_vidiocschan(struct bttv *btv)
{
struct video_channel c;
memset(&c,0,sizeof(c));
c.norm = btv->tvnorm;
c.channel = btv->input;
bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c);
if (btv->c.type == BTTV_VOODOOTV_FM)
bttv_tda9880_setnorm(btv,c.norm);
}
static int
set_tvnorm(struct bttv *btv, unsigned int norm)
{
const struct bttv_tvnorm *tvnorm;
if (norm < 0 || norm >= BTTV_TVNORMS)
return -EINVAL;
btv->tvnorm = norm;
tvnorm = &bttv_tvnorms[norm];
btwrite(tvnorm->adelay, BT848_ADELAY);
btwrite(tvnorm->bdelay, BT848_BDELAY);
btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH),
BT848_IFORM);
btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE);
btwrite(1, BT848_VBI_PACK_DEL);
bt848A_set_timing(btv);
switch (btv->c.type) {
case BTTV_VOODOOTV_FM:
bttv_tda9880_setnorm(btv,norm);
break;
#if 0
case BTTV_OSPREY540:
osprey_540_set_norm(btv,norm);
break;
#endif
}
return 0;
}
static void
set_input(struct bttv *btv, unsigned int input)
{
unsigned long flags;
btv->input = input;
if (irq_iswitch) {
spin_lock_irqsave(&btv->s_lock,flags);
if (btv->curr.irqflags) {
/* active capture -> delayed input switch */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -