mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 11:41:38 -04:00
media updates for v3.19-rc1
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUksm/AAoJEAhfPr2O5OEV7GIP/1nm2rcf16+6Y7nhjWLgPmlU uG9fNUMm7qvFHryCr4RwmTBnCv63Nn/5BFRcXdpAA2gHOtADcHCDNJbtSX+GRN5F I0C5gbLbQVd7l/GFsiGTsBScInAQB+ZN0WNyPbF0OCGLuzzRlexadAYTZufnNiNN nYCINIBub0jUPJaIrbm/qDAbazS7fxTO9HAdMcJGtexPCO/Gj92+4s8NXI+N3hxk Cj87EPsk6i6GA1BM0oVmDWK0dQjgFm+ONMBc8vhFOyVzCXb7pjHg53n1zZ0e0TDt jm/h/oIJuWyH/isEuwcNPTBxf2E9EOv8g66V9mDN1i7zsTEf9Bs08fPigalfJ84x 5S5GBaHEGIu8d2PgqaqxmFO1COyk+xoj9O0Iwx8uabYtyRtgUVaEtsfyHWU2ZiAh 3wqmtHSK5mt835h9wzjxs18apnRhTwEhfQWzFdiCFxkDPz3vPmfWbm+8WHChtYJh j0oSJwzK9uAcYwG8WZi643WduIyrXiDqaglWyBg+7c2k3NhP76W5CpLhjZ+8PrYt I6Jf+gZL/Joq5bMQx6itajgE5IIhAfxmdyaYIBqmlXuQ/88zrw7JBwu6q22jSVbq +QaURaSJ6qOLYj6S/ip93KPVsUXmfdE1JttuLp4ie7r0Q8zOahr4dPQ/xAwATtY/ Jeo+d7AI4tLWQcIDKIWi =1rPM -----END PGP SIGNATURE----- Merge tag 'media/v3.19-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media Pull second set of media updates from Mauro Carvalho Chehab: - Move drivers for really old legacy hardware to staging. Those are using obsolete media kAPIs and are for hardware that nobody uses for years. Simply not worth porting them to the new kAPIs. Of course, if anyone pops up to fix, we can move them back from there - While not too late, do some API fixups at the new colorspace API, added for v3.19 - Some improvements for rcar_vin driver - Some fixups at cx88 and vivid drivers - Some Documentation fixups * tag 'media/v3.19-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: [media] bq/c-qcam, w9966, pms: move to staging in preparation for removal [media] tlg2300: move to staging in preparation for removal [media] vino/saa7191: move to staging in preparation for removal [media] MAINTAINERS: vivi -> vivid [media] cx88: remove leftover start_video_dma() call [media] cx88: add missing alloc_ctx support [media] v4l2-ioctl: WARN_ON if querycap didn't fill device_caps [media] vivid: fix CROP_BOUNDS typo for video output [media] DocBook media: update version number and document changes [media] vivid.txt: document new controls [media] DocBook media: add missing ycbcr_enc and quantization fields [media] v4l2-mediabus.h: use two __u16 instead of two __u32 [media] rcar_vin: Fix interrupt enable in progressive [media] rcar_vin: Enable VSYNC field toggle mode [media] rcar_vin: Add scaling support [media] rcar_vin: Add DT support for r8a7793 and r8a7794 SoCs [media] rcar_vin: Add YUYV capture format support
This commit is contained in:
commit
0ec28c37c2
50 changed files with 640 additions and 78 deletions
|
|
@ -160,7 +160,6 @@ source "drivers/media/usb/Kconfig"
|
|||
source "drivers/media/pci/Kconfig"
|
||||
source "drivers/media/platform/Kconfig"
|
||||
source "drivers/media/mmc/Kconfig"
|
||||
source "drivers/media/parport/Kconfig"
|
||||
source "drivers/media/radio/Kconfig"
|
||||
|
||||
comment "Supported FireWire (IEEE 1394) Adapters"
|
||||
|
|
|
|||
|
|
@ -28,6 +28,6 @@ obj-y += rc/
|
|||
# Finally, merge the drivers that require the core
|
||||
#
|
||||
|
||||
obj-y += common/ platform/ pci/ usb/ mmc/ firewire/ parport/
|
||||
obj-y += common/ platform/ pci/ usb/ mmc/ firewire/
|
||||
obj-$(CONFIG_VIDEO_DEV) += radio/
|
||||
|
||||
|
|
|
|||
|
|
@ -284,15 +284,6 @@ config VIDEO_SAA711X
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called saa7115.
|
||||
|
||||
config VIDEO_SAA7191
|
||||
tristate "Philips SAA7191 video decoder"
|
||||
depends on VIDEO_V4L2 && I2C
|
||||
---help---
|
||||
Support for the Philips SAA7191 video decoder.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called saa7191.
|
||||
|
||||
config VIDEO_TVP514X
|
||||
tristate "Texas Instruments TVP514x video decoder"
|
||||
depends on VIDEO_V4L2 && I2C
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o
|
|||
obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o
|
||||
obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
|
||||
obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
|
||||
obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
|
||||
obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o
|
||||
obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
|
||||
obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
|
||||
|
|
|
|||
|
|
@ -1,649 +0,0 @@
|
|||
/*
|
||||
* saa7191.c - Philips SAA7191 video decoder driver
|
||||
*
|
||||
* Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
|
||||
* Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <media/v4l2-device.h>
|
||||
|
||||
#include "saa7191.h"
|
||||
|
||||
#define SAA7191_MODULE_VERSION "0.0.5"
|
||||
|
||||
MODULE_DESCRIPTION("Philips SAA7191 video decoder driver");
|
||||
MODULE_VERSION(SAA7191_MODULE_VERSION);
|
||||
MODULE_AUTHOR("Mikael Nousiainen <[email protected]>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
// #define SAA7191_DEBUG
|
||||
|
||||
#ifdef SAA7191_DEBUG
|
||||
#define dprintk(x...) printk("SAA7191: " x);
|
||||
#else
|
||||
#define dprintk(x...)
|
||||
#endif
|
||||
|
||||
#define SAA7191_SYNC_COUNT 30
|
||||
#define SAA7191_SYNC_DELAY 100 /* milliseconds */
|
||||
|
||||
struct saa7191 {
|
||||
struct v4l2_subdev sd;
|
||||
|
||||
/* the register values are stored here as the actual
|
||||
* I2C-registers are write-only */
|
||||
u8 reg[25];
|
||||
|
||||
int input;
|
||||
v4l2_std_id norm;
|
||||
};
|
||||
|
||||
static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd)
|
||||
{
|
||||
return container_of(sd, struct saa7191, sd);
|
||||
}
|
||||
|
||||
static const u8 initseq[] = {
|
||||
0, /* Subaddress */
|
||||
|
||||
0x50, /* (0x50) SAA7191_REG_IDEL */
|
||||
|
||||
/* 50 Hz signal timing */
|
||||
0x30, /* (0x30) SAA7191_REG_HSYB */
|
||||
0x00, /* (0x00) SAA7191_REG_HSYS */
|
||||
0xe8, /* (0xe8) SAA7191_REG_HCLB */
|
||||
0xb6, /* (0xb6) SAA7191_REG_HCLS */
|
||||
0xf4, /* (0xf4) SAA7191_REG_HPHI */
|
||||
|
||||
/* control */
|
||||
SAA7191_LUMA_APER_1, /* (0x01) SAA7191_REG_LUMA - CVBS mode */
|
||||
0x00, /* (0x00) SAA7191_REG_HUEC */
|
||||
0xf8, /* (0xf8) SAA7191_REG_CKTQ */
|
||||
0xf8, /* (0xf8) SAA7191_REG_CKTS */
|
||||
0x90, /* (0x90) SAA7191_REG_PLSE */
|
||||
0x90, /* (0x90) SAA7191_REG_SESE */
|
||||
0x00, /* (0x00) SAA7191_REG_GAIN */
|
||||
SAA7191_STDC_NFEN | SAA7191_STDC_HRMV, /* (0x0c) SAA7191_REG_STDC
|
||||
* - not SECAM,
|
||||
* slow time constant */
|
||||
SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS
|
||||
| SAA7191_IOCK_OEDY, /* (0x78) SAA7191_REG_IOCK
|
||||
* - chroma from CVBS, GPSW1 & 2 off */
|
||||
SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS
|
||||
| SAA7191_CTL3_YDEL0, /* (0x99) SAA7191_REG_CTL3
|
||||
* - automatic field detection */
|
||||
0x00, /* (0x00) SAA7191_REG_CTL4 */
|
||||
0x2c, /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */
|
||||
0x00, /* unused */
|
||||
0x00, /* unused */
|
||||
|
||||
/* 60 Hz signal timing */
|
||||
0x34, /* (0x34) SAA7191_REG_HS6B */
|
||||
0x0a, /* (0x0a) SAA7191_REG_HS6S */
|
||||
0xf4, /* (0xf4) SAA7191_REG_HC6B */
|
||||
0xce, /* (0xce) SAA7191_REG_HC6S */
|
||||
0xf4, /* (0xf4) SAA7191_REG_HP6I */
|
||||
};
|
||||
|
||||
/* SAA7191 register handling */
|
||||
|
||||
static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg)
|
||||
{
|
||||
return to_saa7191(sd)->reg[reg];
|
||||
}
|
||||
|
||||
static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_recv(client, value, 1);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
|
||||
to_saa7191(sd)->reg[reg] = value;
|
||||
return i2c_smbus_write_byte_data(client, reg, value);
|
||||
}
|
||||
|
||||
/* the first byte of data must be the first subaddress number (register) */
|
||||
static int saa7191_write_block(struct v4l2_subdev *sd,
|
||||
u8 length, const u8 *data)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct saa7191 *decoder = to_saa7191(sd);
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < (length - 1); i++) {
|
||||
decoder->reg[data[0] + i] = data[i + 1];
|
||||
}
|
||||
|
||||
ret = i2c_master_send(client, data, length);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "SAA7191: saa7191_write_block(): "
|
||||
"write failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper functions */
|
||||
|
||||
static int saa7191_s_routing(struct v4l2_subdev *sd,
|
||||
u32 input, u32 output, u32 config)
|
||||
{
|
||||
struct saa7191 *decoder = to_saa7191(sd);
|
||||
u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA);
|
||||
u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK);
|
||||
int err;
|
||||
|
||||
switch (input) {
|
||||
case SAA7191_INPUT_COMPOSITE: /* Set Composite input */
|
||||
iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1
|
||||
| SAA7191_IOCK_GPSW2);
|
||||
/* Chrominance trap active */
|
||||
luma &= ~SAA7191_LUMA_BYPS;
|
||||
break;
|
||||
case SAA7191_INPUT_SVIDEO: /* Set S-Video input */
|
||||
iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2;
|
||||
/* Chrominance trap bypassed */
|
||||
luma |= SAA7191_LUMA_BYPS;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma);
|
||||
if (err)
|
||||
return -EIO;
|
||||
err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock);
|
||||
if (err)
|
||||
return -EIO;
|
||||
|
||||
decoder->input = input;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
|
||||
{
|
||||
struct saa7191 *decoder = to_saa7191(sd);
|
||||
u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC);
|
||||
u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3);
|
||||
u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV);
|
||||
int err;
|
||||
|
||||
if (norm & V4L2_STD_PAL) {
|
||||
stdc &= ~SAA7191_STDC_SECS;
|
||||
ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
|
||||
chcv = SAA7191_CHCV_PAL;
|
||||
} else if (norm & V4L2_STD_NTSC) {
|
||||
stdc &= ~SAA7191_STDC_SECS;
|
||||
ctl3 &= ~SAA7191_CTL3_AUFD;
|
||||
ctl3 |= SAA7191_CTL3_FSEL;
|
||||
chcv = SAA7191_CHCV_NTSC;
|
||||
} else if (norm & V4L2_STD_SECAM) {
|
||||
stdc |= SAA7191_STDC_SECS;
|
||||
ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
|
||||
chcv = SAA7191_CHCV_PAL;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
|
||||
if (err)
|
||||
return -EIO;
|
||||
err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc);
|
||||
if (err)
|
||||
return -EIO;
|
||||
err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv);
|
||||
if (err)
|
||||
return -EIO;
|
||||
|
||||
decoder->norm = norm;
|
||||
|
||||
dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3,
|
||||
stdc, chcv);
|
||||
dprintk("norm: %llx\n", norm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
dprintk("Checking for signal...\n");
|
||||
|
||||
for (i = 0; i < SAA7191_SYNC_COUNT; i++) {
|
||||
if (saa7191_read_status(sd, status))
|
||||
return -EIO;
|
||||
|
||||
if (((*status) & SAA7191_STATUS_HLCK) == 0) {
|
||||
dprintk("Signal found\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
msleep(SAA7191_SYNC_DELAY);
|
||||
}
|
||||
|
||||
dprintk("No signal\n");
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm)
|
||||
{
|
||||
struct saa7191 *decoder = to_saa7191(sd);
|
||||
u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC);
|
||||
u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3);
|
||||
u8 status;
|
||||
v4l2_std_id old_norm = decoder->norm;
|
||||
int err = 0;
|
||||
|
||||
dprintk("SAA7191 extended signal auto-detection...\n");
|
||||
|
||||
*norm &= V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
|
||||
stdc &= ~SAA7191_STDC_SECS;
|
||||
ctl3 &= ~(SAA7191_CTL3_FSEL);
|
||||
|
||||
err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc);
|
||||
if (err) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
|
||||
if (err) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctl3 |= SAA7191_CTL3_AUFD;
|
||||
err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
|
||||
if (err) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
msleep(SAA7191_SYNC_DELAY);
|
||||
|
||||
err = saa7191_wait_for_signal(sd, &status);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (status & SAA7191_STATUS_FIDT) {
|
||||
/* 60Hz signal -> NTSC */
|
||||
dprintk("60Hz signal: NTSC\n");
|
||||
*norm &= V4L2_STD_NTSC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 50Hz signal */
|
||||
dprintk("50Hz signal: Trying PAL...\n");
|
||||
|
||||
/* try PAL first */
|
||||
err = saa7191_s_std(sd, V4L2_STD_PAL);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
msleep(SAA7191_SYNC_DELAY);
|
||||
|
||||
err = saa7191_wait_for_signal(sd, &status);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* not 50Hz ? */
|
||||
if (status & SAA7191_STATUS_FIDT) {
|
||||
dprintk("No 50Hz signal\n");
|
||||
saa7191_s_std(sd, old_norm);
|
||||
*norm = V4L2_STD_UNKNOWN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (status & SAA7191_STATUS_CODE) {
|
||||
dprintk("PAL\n");
|
||||
*norm &= V4L2_STD_PAL;
|
||||
return saa7191_s_std(sd, old_norm);
|
||||
}
|
||||
|
||||
dprintk("No color detected with PAL - Trying SECAM...\n");
|
||||
|
||||
/* no color detected ? -> try SECAM */
|
||||
err = saa7191_s_std(sd, V4L2_STD_SECAM);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
msleep(SAA7191_SYNC_DELAY);
|
||||
|
||||
err = saa7191_wait_for_signal(sd, &status);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* not 50Hz ? */
|
||||
if (status & SAA7191_STATUS_FIDT) {
|
||||
dprintk("No 50Hz signal\n");
|
||||
*norm = V4L2_STD_UNKNOWN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (status & SAA7191_STATUS_CODE) {
|
||||
/* Color detected -> SECAM */
|
||||
dprintk("SECAM\n");
|
||||
*norm &= V4L2_STD_SECAM;
|
||||
return saa7191_s_std(sd, old_norm);
|
||||
}
|
||||
|
||||
dprintk("No color detected with SECAM - Going back to PAL.\n");
|
||||
*norm = V4L2_STD_UNKNOWN;
|
||||
|
||||
out:
|
||||
return saa7191_s_std(sd, old_norm);
|
||||
}
|
||||
|
||||
static int saa7191_autodetect_norm(struct v4l2_subdev *sd)
|
||||
{
|
||||
u8 status;
|
||||
|
||||
dprintk("SAA7191 signal auto-detection...\n");
|
||||
|
||||
dprintk("Reading status...\n");
|
||||
|
||||
if (saa7191_read_status(sd, &status))
|
||||
return -EIO;
|
||||
|
||||
dprintk("Checking for signal...\n");
|
||||
|
||||
/* no signal ? */
|
||||
if (status & SAA7191_STATUS_HLCK) {
|
||||
dprintk("No signal\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dprintk("Signal found\n");
|
||||
|
||||
if (status & SAA7191_STATUS_FIDT) {
|
||||
/* 60hz signal -> NTSC */
|
||||
dprintk("NTSC\n");
|
||||
return saa7191_s_std(sd, V4L2_STD_NTSC);
|
||||
} else {
|
||||
/* 50hz signal -> PAL */
|
||||
dprintk("PAL\n");
|
||||
return saa7191_s_std(sd, V4L2_STD_PAL);
|
||||
}
|
||||
}
|
||||
|
||||
static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
{
|
||||
u8 reg;
|
||||
int ret = 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case SAA7191_CONTROL_BANDPASS:
|
||||
case SAA7191_CONTROL_BANDPASS_WEIGHT:
|
||||
case SAA7191_CONTROL_CORING:
|
||||
reg = saa7191_read_reg(sd, SAA7191_REG_LUMA);
|
||||
switch (ctrl->id) {
|
||||
case SAA7191_CONTROL_BANDPASS:
|
||||
ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK)
|
||||
>> SAA7191_LUMA_BPSS_SHIFT;
|
||||
break;
|
||||
case SAA7191_CONTROL_BANDPASS_WEIGHT:
|
||||
ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK)
|
||||
>> SAA7191_LUMA_APER_SHIFT;
|
||||
break;
|
||||
case SAA7191_CONTROL_CORING:
|
||||
ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK)
|
||||
>> SAA7191_LUMA_CORI_SHIFT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SAA7191_CONTROL_FORCE_COLOUR:
|
||||
case SAA7191_CONTROL_CHROMA_GAIN:
|
||||
reg = saa7191_read_reg(sd, SAA7191_REG_GAIN);
|
||||
if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR)
|
||||
ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0;
|
||||
else
|
||||
ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK)
|
||||
>> SAA7191_GAIN_LFIS_SHIFT;
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
reg = saa7191_read_reg(sd, SAA7191_REG_HUEC);
|
||||
if (reg < 0x80)
|
||||
reg += 0x80;
|
||||
else
|
||||
reg -= 0x80;
|
||||
ctrl->value = (s32)reg;
|
||||
break;
|
||||
case SAA7191_CONTROL_VTRC:
|
||||
reg = saa7191_read_reg(sd, SAA7191_REG_STDC);
|
||||
ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0;
|
||||
break;
|
||||
case SAA7191_CONTROL_LUMA_DELAY:
|
||||
reg = saa7191_read_reg(sd, SAA7191_REG_CTL3);
|
||||
ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK)
|
||||
>> SAA7191_CTL3_YDEL_SHIFT;
|
||||
if (ctrl->value >= 4)
|
||||
ctrl->value -= 8;
|
||||
break;
|
||||
case SAA7191_CONTROL_VNR:
|
||||
reg = saa7191_read_reg(sd, SAA7191_REG_CTL4);
|
||||
ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK)
|
||||
>> SAA7191_CTL4_VNOI_SHIFT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
{
|
||||
u8 reg;
|
||||
int ret = 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case SAA7191_CONTROL_BANDPASS:
|
||||
case SAA7191_CONTROL_BANDPASS_WEIGHT:
|
||||
case SAA7191_CONTROL_CORING:
|
||||
reg = saa7191_read_reg(sd, SAA7191_REG_LUMA);
|
||||
switch (ctrl->id) {
|
||||
case SAA7191_CONTROL_BANDPASS:
|
||||
reg &= ~SAA7191_LUMA_BPSS_MASK;
|
||||
reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT)
|
||||
& SAA7191_LUMA_BPSS_MASK;
|
||||
break;
|
||||
case SAA7191_CONTROL_BANDPASS_WEIGHT:
|
||||
reg &= ~SAA7191_LUMA_APER_MASK;
|
||||
reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT)
|
||||
& SAA7191_LUMA_APER_MASK;
|
||||
break;
|
||||
case SAA7191_CONTROL_CORING:
|
||||
reg &= ~SAA7191_LUMA_CORI_MASK;
|
||||
reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT)
|
||||
& SAA7191_LUMA_CORI_MASK;
|
||||
break;
|
||||
}
|
||||
ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg);
|
||||
break;
|
||||
case SAA7191_CONTROL_FORCE_COLOUR:
|
||||
case SAA7191_CONTROL_CHROMA_GAIN:
|
||||
reg = saa7191_read_reg(sd, SAA7191_REG_GAIN);
|
||||
if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) {
|
||||
if (ctrl->value)
|
||||
reg |= SAA7191_GAIN_COLO;
|
||||
else
|
||||
reg &= ~SAA7191_GAIN_COLO;
|
||||
} else {
|
||||
reg &= ~SAA7191_GAIN_LFIS_MASK;
|
||||
reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT)
|
||||
& SAA7191_GAIN_LFIS_MASK;
|
||||
}
|
||||
ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg);
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
reg = ctrl->value & 0xff;
|
||||
if (reg < 0x80)
|
||||
reg += 0x80;
|
||||
else
|
||||
reg -= 0x80;
|
||||
ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg);
|
||||
break;
|
||||
case SAA7191_CONTROL_VTRC:
|
||||
reg = saa7191_read_reg(sd, SAA7191_REG_STDC);
|
||||
if (ctrl->value)
|
||||
reg |= SAA7191_STDC_VTRC;
|
||||
else
|
||||
reg &= ~SAA7191_STDC_VTRC;
|
||||
ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg);
|
||||
break;
|
||||
case SAA7191_CONTROL_LUMA_DELAY: {
|
||||
s32 value = ctrl->value;
|
||||
if (value < 0)
|
||||
value += 8;
|
||||
reg = saa7191_read_reg(sd, SAA7191_REG_CTL3);
|
||||
reg &= ~SAA7191_CTL3_YDEL_MASK;
|
||||
reg |= (value << SAA7191_CTL3_YDEL_SHIFT)
|
||||
& SAA7191_CTL3_YDEL_MASK;
|
||||
ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg);
|
||||
break;
|
||||
}
|
||||
case SAA7191_CONTROL_VNR:
|
||||
reg = saa7191_read_reg(sd, SAA7191_REG_CTL4);
|
||||
reg &= ~SAA7191_CTL4_VNOI_MASK;
|
||||
reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT)
|
||||
& SAA7191_CTL4_VNOI_MASK;
|
||||
ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* I2C-interface */
|
||||
|
||||
static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status)
|
||||
{
|
||||
u8 status_reg;
|
||||
int res = V4L2_IN_ST_NO_SIGNAL;
|
||||
|
||||
if (saa7191_read_status(sd, &status_reg))
|
||||
return -EIO;
|
||||
if ((status_reg & SAA7191_STATUS_HLCK) == 0)
|
||||
res = 0;
|
||||
if (!(status_reg & SAA7191_STATUS_CODE))
|
||||
res |= V4L2_IN_ST_NO_COLOR;
|
||||
*status = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static const struct v4l2_subdev_core_ops saa7191_core_ops = {
|
||||
.g_ctrl = saa7191_g_ctrl,
|
||||
.s_ctrl = saa7191_s_ctrl,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_video_ops saa7191_video_ops = {
|
||||
.s_std = saa7191_s_std,
|
||||
.s_routing = saa7191_s_routing,
|
||||
.querystd = saa7191_querystd,
|
||||
.g_input_status = saa7191_g_input_status,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops saa7191_ops = {
|
||||
.core = &saa7191_core_ops,
|
||||
.video = &saa7191_video_ops,
|
||||
};
|
||||
|
||||
static int saa7191_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int err = 0;
|
||||
struct saa7191 *decoder;
|
||||
struct v4l2_subdev *sd;
|
||||
|
||||
v4l_info(client, "chip found @ 0x%x (%s)\n",
|
||||
client->addr << 1, client->adapter->name);
|
||||
|
||||
decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
|
||||
if (!decoder)
|
||||
return -ENOMEM;
|
||||
|
||||
sd = &decoder->sd;
|
||||
v4l2_i2c_subdev_init(sd, client, &saa7191_ops);
|
||||
|
||||
err = saa7191_write_block(sd, sizeof(initseq), initseq);
|
||||
if (err) {
|
||||
printk(KERN_ERR "SAA7191 initialization failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "SAA7191 initialized\n");
|
||||
|
||||
decoder->input = SAA7191_INPUT_COMPOSITE;
|
||||
decoder->norm = V4L2_STD_PAL;
|
||||
|
||||
err = saa7191_autodetect_norm(sd);
|
||||
if (err && (err != -EBUSY))
|
||||
printk(KERN_ERR "SAA7191: Signal auto-detection failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int saa7191_remove(struct i2c_client *client)
|
||||
{
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
|
||||
v4l2_device_unregister_subdev(sd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id saa7191_id[] = {
|
||||
{ "saa7191", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, saa7191_id);
|
||||
|
||||
static struct i2c_driver saa7191_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "saa7191",
|
||||
},
|
||||
.probe = saa7191_probe,
|
||||
.remove = saa7191_remove,
|
||||
.id_table = saa7191_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(saa7191_driver);
|
||||
|
|
@ -1,245 +0,0 @@
|
|||
/*
|
||||
* saa7191.h - Philips SAA7191 video decoder driver
|
||||
*
|
||||
* Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
|
||||
* Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _SAA7191_H_
|
||||
#define _SAA7191_H_
|
||||
|
||||
/* Philips SAA7191 DMSD I2C bus address */
|
||||
#define SAA7191_ADDR 0x8a
|
||||
|
||||
/* Register subaddresses. */
|
||||
#define SAA7191_REG_IDEL 0x00
|
||||
#define SAA7191_REG_HSYB 0x01
|
||||
#define SAA7191_REG_HSYS 0x02
|
||||
#define SAA7191_REG_HCLB 0x03
|
||||
#define SAA7191_REG_HCLS 0x04
|
||||
#define SAA7191_REG_HPHI 0x05
|
||||
#define SAA7191_REG_LUMA 0x06
|
||||
#define SAA7191_REG_HUEC 0x07
|
||||
#define SAA7191_REG_CKTQ 0x08 /* bits 3-7 */
|
||||
#define SAA7191_REG_CKTS 0x09 /* bits 3-7 */
|
||||
#define SAA7191_REG_PLSE 0x0a
|
||||
#define SAA7191_REG_SESE 0x0b
|
||||
#define SAA7191_REG_GAIN 0x0c
|
||||
#define SAA7191_REG_STDC 0x0d
|
||||
#define SAA7191_REG_IOCK 0x0e
|
||||
#define SAA7191_REG_CTL3 0x0f
|
||||
#define SAA7191_REG_CTL4 0x10
|
||||
#define SAA7191_REG_CHCV 0x11
|
||||
#define SAA7191_REG_HS6B 0x14
|
||||
#define SAA7191_REG_HS6S 0x15
|
||||
#define SAA7191_REG_HC6B 0x16
|
||||
#define SAA7191_REG_HC6S 0x17
|
||||
#define SAA7191_REG_HP6I 0x18
|
||||
#define SAA7191_REG_STATUS 0xff /* not really a subaddress */
|
||||
|
||||
/* Status Register definitions */
|
||||
#define SAA7191_STATUS_CODE 0x01 /* color detected flag */
|
||||
#define SAA7191_STATUS_FIDT 0x20 /* signal type 50/60 Hz */
|
||||
#define SAA7191_STATUS_HLCK 0x40 /* PLL unlocked(1)/locked(0) */
|
||||
#define SAA7191_STATUS_STTC 0x80 /* tv/vtr time constant */
|
||||
|
||||
/* Luminance Control Register definitions */
|
||||
/* input mode select bit:
|
||||
* 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */
|
||||
#define SAA7191_LUMA_BYPS 0x80
|
||||
/* pre-filter (only when chrominance trap is active) */
|
||||
#define SAA7191_LUMA_PREF 0x40
|
||||
/* aperture bandpass to select different characteristics with maximums
|
||||
* (bits 4-5) */
|
||||
#define SAA7191_LUMA_BPSS_MASK 0x30
|
||||
#define SAA7191_LUMA_BPSS_SHIFT 4
|
||||
#define SAA7191_LUMA_BPSS_3 0x30
|
||||
#define SAA7191_LUMA_BPSS_2 0x20
|
||||
#define SAA7191_LUMA_BPSS_1 0x10
|
||||
#define SAA7191_LUMA_BPSS_0 0x00
|
||||
/* coring range for high frequency components according to 8-bit luminance
|
||||
* (bits 2-3)
|
||||
* 0=coring off, n= (+-)n LSB */
|
||||
#define SAA7191_LUMA_CORI_MASK 0x0c
|
||||
#define SAA7191_LUMA_CORI_SHIFT 2
|
||||
#define SAA7191_LUMA_CORI_3 0x0c
|
||||
#define SAA7191_LUMA_CORI_2 0x08
|
||||
#define SAA7191_LUMA_CORI_1 0x04
|
||||
#define SAA7191_LUMA_CORI_0 0x00
|
||||
/* aperture bandpass filter weights high frequency components of luminance
|
||||
* signal (bits 0-1)
|
||||
* 0=factor 0, 1=0.25, 2=0.5, 3=1 */
|
||||
#define SAA7191_LUMA_APER_MASK 0x03
|
||||
#define SAA7191_LUMA_APER_SHIFT 0
|
||||
#define SAA7191_LUMA_APER_3 0x03
|
||||
#define SAA7191_LUMA_APER_2 0x02
|
||||
#define SAA7191_LUMA_APER_1 0x01
|
||||
#define SAA7191_LUMA_APER_0 0x00
|
||||
|
||||
/* Chrominance Gain Control Settings Register definitions */
|
||||
/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */
|
||||
#define SAA7191_GAIN_COLO 0x80
|
||||
/* chrominance gain control (AGC filter)
|
||||
* 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */
|
||||
#define SAA7191_GAIN_LFIS_MASK 0x60
|
||||
#define SAA7191_GAIN_LFIS_SHIFT 5
|
||||
#define SAA7191_GAIN_LFIS_3 0x60
|
||||
#define SAA7191_GAIN_LFIS_2 0x40
|
||||
#define SAA7191_GAIN_LFIS_1 0x20
|
||||
#define SAA7191_GAIN_LFIS_0 0x00
|
||||
|
||||
/* Standard/Mode Control Register definitions */
|
||||
/* tv/vtr mode bit: 0=TV mode (slow time constant),
|
||||
* 1=VTR mode (fast time constant) */
|
||||
#define SAA7191_STDC_VTRC 0x80
|
||||
/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs)
|
||||
* 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */
|
||||
#define SAA7191_STDC_NFEN 0x08
|
||||
/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */
|
||||
#define SAA7191_STDC_HRMV 0x04
|
||||
/* general purpose switch 0
|
||||
* (not used with VINO afaik) */
|
||||
#define SAA7191_STDC_GPSW0 0x02
|
||||
/* SECAM mode bit: 0=other standards, 1=SECAM */
|
||||
#define SAA7191_STDC_SECS 0x01
|
||||
|
||||
/* I/O and Clock Control Register definitions */
|
||||
/* horizontal clock PLL: 0=PLL closed,
|
||||
* 1=PLL circuit open and horizontal freq fixed */
|
||||
#define SAA7191_IOCK_HPLL 0x80
|
||||
/* colour-difference output enable (outputs UV0-UV7) */
|
||||
#define SAA7191_IOCK_OEDC 0x40
|
||||
/* H-sync output enable */
|
||||
#define SAA7191_IOCK_OEHS 0x20
|
||||
/* V-sync output enable */
|
||||
#define SAA7191_IOCK_OEVS 0x10
|
||||
/* luminance output enable (outputs Y0-Y7) */
|
||||
#define SAA7191_IOCK_OEDY 0x08
|
||||
/* S-VHS bit (chrominance from CVBS or from chrominance input):
|
||||
* 0=controlled by BYPS-bit, 1=from chrominance input */
|
||||
#define SAA7191_IOCK_CHRS 0x04
|
||||
/* general purpose switch 2
|
||||
* VINO-specific: 0=used with CVBS, 1=used with S-Video */
|
||||
#define SAA7191_IOCK_GPSW2 0x02
|
||||
/* general purpose switch 1 */
|
||||
/* VINO-specific: 0=always, 1=not used!*/
|
||||
#define SAA7191_IOCK_GPSW1 0x01
|
||||
|
||||
/* Miscellaneous Control #1 Register definitions */
|
||||
/* automatic field detection (50/60Hz standard) */
|
||||
#define SAA7191_CTL3_AUFD 0x80
|
||||
/* field select: (if AUFD=0)
|
||||
* 0=50Hz (625 lines), 1=60Hz (525 lines) */
|
||||
#define SAA7191_CTL3_FSEL 0x40
|
||||
/* SECAM cross-colour reduction enable */
|
||||
#define SAA7191_CTL3_SXCR 0x20
|
||||
/* sync and clamping pulse enable (HCL and HSY outputs) */
|
||||
#define SAA7191_CTL3_SCEN 0x10
|
||||
/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */
|
||||
#define SAA7191_CTL3_OFTS 0x08
|
||||
/* luminance delay compensation
|
||||
* 0=0*2/LLC, 1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC,
|
||||
* 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC
|
||||
* step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */
|
||||
#define SAA7191_CTL3_YDEL_MASK 0x07
|
||||
#define SAA7191_CTL3_YDEL_SHIFT 0
|
||||
#define SAA7191_CTL3_YDEL2 0x04
|
||||
#define SAA7191_CTL3_YDEL1 0x02
|
||||
#define SAA7191_CTL3_YDEL0 0x01
|
||||
|
||||
/* Miscellaneous Control #2 Register definitions */
|
||||
/* select HREF position
|
||||
* 0=normal, HREF is matched to YUV output port,
|
||||
* 1=HREF is matched to CVBS input port */
|
||||
#define SAA7191_CTL4_HRFS 0x04
|
||||
/* vertical noise reduction
|
||||
* 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */
|
||||
#define SAA7191_CTL4_VNOI_MASK 0x03
|
||||
#define SAA7191_CTL4_VNOI_SHIFT 0
|
||||
#define SAA7191_CTL4_VNOI_3 0x03
|
||||
#define SAA7191_CTL4_VNOI_2 0x02
|
||||
#define SAA7191_CTL4_VNOI_1 0x01
|
||||
#define SAA7191_CTL4_VNOI_0 0x00
|
||||
|
||||
/* Chrominance Gain Control Register definitions
|
||||
* - for QAM-modulated input signals, effects output amplitude
|
||||
* (SECAM gain fixed)
|
||||
* (nominal values for UV CCIR level) */
|
||||
#define SAA7191_CHCV_NTSC 0x2c
|
||||
#define SAA7191_CHCV_PAL 0x59
|
||||
|
||||
/* Driver interface definitions */
|
||||
#define SAA7191_INPUT_COMPOSITE 0
|
||||
#define SAA7191_INPUT_SVIDEO 1
|
||||
|
||||
#define SAA7191_NORM_PAL 1
|
||||
#define SAA7191_NORM_NTSC 2
|
||||
#define SAA7191_NORM_SECAM 3
|
||||
|
||||
struct saa7191_status {
|
||||
/* 0=no signal, 1=signal detected */
|
||||
int signal;
|
||||
/* 0=50hz (pal) signal, 1=60hz (ntsc) signal */
|
||||
int signal_60hz;
|
||||
/* 0=no color detected, 1=color detected */
|
||||
int color;
|
||||
|
||||
/* current SAA7191_INPUT_ */
|
||||
int input;
|
||||
/* current SAA7191_NORM_ */
|
||||
int norm;
|
||||
};
|
||||
|
||||
#define SAA7191_BANDPASS_MIN 0x00
|
||||
#define SAA7191_BANDPASS_MAX 0x03
|
||||
#define SAA7191_BANDPASS_DEFAULT 0x00
|
||||
|
||||
#define SAA7191_BANDPASS_WEIGHT_MIN 0x00
|
||||
#define SAA7191_BANDPASS_WEIGHT_MAX 0x03
|
||||
#define SAA7191_BANDPASS_WEIGHT_DEFAULT 0x01
|
||||
|
||||
#define SAA7191_CORING_MIN 0x00
|
||||
#define SAA7191_CORING_MAX 0x03
|
||||
#define SAA7191_CORING_DEFAULT 0x00
|
||||
|
||||
#define SAA7191_HUE_MIN 0x00
|
||||
#define SAA7191_HUE_MAX 0xff
|
||||
#define SAA7191_HUE_DEFAULT 0x80
|
||||
|
||||
#define SAA7191_VTRC_MIN 0x00
|
||||
#define SAA7191_VTRC_MAX 0x01
|
||||
#define SAA7191_VTRC_DEFAULT 0x00
|
||||
|
||||
#define SAA7191_FORCE_COLOUR_MIN 0x00
|
||||
#define SAA7191_FORCE_COLOUR_MAX 0x01
|
||||
#define SAA7191_FORCE_COLOUR_DEFAULT 0x00
|
||||
|
||||
#define SAA7191_CHROMA_GAIN_MIN 0x00
|
||||
#define SAA7191_CHROMA_GAIN_MAX 0x03
|
||||
#define SAA7191_CHROMA_GAIN_DEFAULT 0x00
|
||||
|
||||
#define SAA7191_LUMA_DELAY_MIN -0x04
|
||||
#define SAA7191_LUMA_DELAY_MAX 0x03
|
||||
#define SAA7191_LUMA_DELAY_DEFAULT 0x01
|
||||
|
||||
#define SAA7191_VNR_MIN 0x00
|
||||
#define SAA7191_VNR_MAX 0x03
|
||||
#define SAA7191_VNR_DEFAULT 0x00
|
||||
|
||||
#define SAA7191_CONTROL_BANDPASS (V4L2_CID_PRIVATE_BASE + 0)
|
||||
#define SAA7191_CONTROL_BANDPASS_WEIGHT (V4L2_CID_PRIVATE_BASE + 1)
|
||||
#define SAA7191_CONTROL_CORING (V4L2_CID_PRIVATE_BASE + 2)
|
||||
#define SAA7191_CONTROL_FORCE_COLOUR (V4L2_CID_PRIVATE_BASE + 3)
|
||||
#define SAA7191_CONTROL_CHROMA_GAIN (V4L2_CID_PRIVATE_BASE + 4)
|
||||
#define SAA7191_CONTROL_VTRC (V4L2_CID_PRIVATE_BASE + 5)
|
||||
#define SAA7191_CONTROL_LUMA_DELAY (V4L2_CID_PRIVATE_BASE + 6)
|
||||
#define SAA7191_CONTROL_VNR (V4L2_CID_PRIVATE_BASE + 7)
|
||||
|
||||
#define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status)
|
||||
#define DECODER_SAA7191_SET_NORM _IOW('d', 196, int)
|
||||
|
||||
#endif
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
menuconfig MEDIA_PARPORT_SUPPORT
|
||||
bool "ISA and parallel port devices"
|
||||
depends on (ISA || PARPORT) && MEDIA_CAMERA_SUPPORT
|
||||
help
|
||||
Enables drivers for ISA and parallel port bus. If you
|
||||
need media drivers using those legacy buses, say Y.
|
||||
|
||||
if MEDIA_PARPORT_SUPPORT
|
||||
config VIDEO_BWQCAM
|
||||
tristate "Quickcam BW Video For Linux"
|
||||
depends on PARPORT && VIDEO_V4L2
|
||||
select VIDEOBUF2_VMALLOC
|
||||
help
|
||||
Say Y have if you the black and white version of the QuickCam
|
||||
camera. See the next option for the color version.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bw-qcam.
|
||||
|
||||
config VIDEO_CQCAM
|
||||
tristate "QuickCam Colour Video For Linux"
|
||||
depends on PARPORT && VIDEO_V4L2
|
||||
help
|
||||
This is the video4linux driver for the colour version of the
|
||||
Connectix QuickCam. If you have one of these cameras, say Y here,
|
||||
otherwise say N. This driver does not work with the original
|
||||
monochrome QuickCam, QuickCam VC or QuickClip. It is also available
|
||||
as a module (c-qcam).
|
||||
Read <file:Documentation/video4linux/CQcam.txt> for more information.
|
||||
|
||||
config VIDEO_PMS
|
||||
tristate "Mediavision Pro Movie Studio Video For Linux"
|
||||
depends on ISA && VIDEO_V4L2
|
||||
help
|
||||
Say Y if you have the ISA Mediavision Pro Movie Studio
|
||||
capture card.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pms.
|
||||
|
||||
config VIDEO_W9966
|
||||
tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux"
|
||||
depends on PARPORT_1284 && PARPORT && VIDEO_V4L2
|
||||
help
|
||||
Video4linux driver for Winbond's w9966 based Webcams.
|
||||
Currently tested with the LifeView FlyCam Supra.
|
||||
If you have one of these cameras, say Y here
|
||||
otherwise say N.
|
||||
This driver is also available as a module (w9966).
|
||||
|
||||
Check out <file:Documentation/video4linux/w9966.txt> for more
|
||||
information.
|
||||
endif
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o
|
||||
obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o
|
||||
obj-$(CONFIG_VIDEO_W9966) += w9966.o
|
||||
obj-$(CONFIG_VIDEO_PMS) += pms.o
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,882 +0,0 @@
|
|||
/*
|
||||
* Video4Linux Colour QuickCam driver
|
||||
* Copyright 1997-2000 Philip Blundell <philb@gnu.org>
|
||||
*
|
||||
* Module parameters:
|
||||
*
|
||||
* parport=auto -- probe all parports (default)
|
||||
* parport=0 -- parport0 becomes qcam1
|
||||
* parport=2,0,1 -- parports 2,0,1 are tried in that order
|
||||
*
|
||||
* probe=0 -- do no probing, assume camera is present
|
||||
* probe=1 -- use IEEE-1284 autoprobe data only (default)
|
||||
* probe=2 -- probe aggressively for cameras
|
||||
*
|
||||
* force_rgb=1 -- force data format to RGB (default is BGR)
|
||||
*
|
||||
* The parport parameter controls which parports will be scanned.
|
||||
* Scanning all parports causes some printers to print a garbage page.
|
||||
* -- March 14, 1999 Billy Donahue <billy@escape.com>
|
||||
*
|
||||
* Fixed data format to BGR, added force_rgb parameter. Added missing
|
||||
* parport_unregister_driver() on module removal.
|
||||
* -- May 28, 2000 Claudio Matsuoka <claudio@conectiva.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-fh.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-event.h>
|
||||
|
||||
struct qcam {
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct video_device vdev;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
struct pardevice *pdev;
|
||||
struct parport *pport;
|
||||
int width, height;
|
||||
int ccd_width, ccd_height;
|
||||
int mode;
|
||||
int contrast, brightness, whitebal;
|
||||
int top, left;
|
||||
unsigned int bidirectional;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
/* cameras maximum */
|
||||
#define MAX_CAMS 4
|
||||
|
||||
/* The three possible QuickCam modes */
|
||||
#define QC_MILLIONS 0x18
|
||||
#define QC_BILLIONS 0x10
|
||||
#define QC_THOUSANDS 0x08 /* with VIDEC compression (not supported) */
|
||||
|
||||
/* The three possible decimations */
|
||||
#define QC_DECIMATION_1 0
|
||||
#define QC_DECIMATION_2 2
|
||||
#define QC_DECIMATION_4 4
|
||||
|
||||
#define BANNER "Colour QuickCam for Video4Linux v0.06"
|
||||
|
||||
static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 };
|
||||
static int probe = 2;
|
||||
static bool force_rgb;
|
||||
static int video_nr = -1;
|
||||
|
||||
/* FIXME: parport=auto would never have worked, surely? --RR */
|
||||
MODULE_PARM_DESC(parport, "parport=<auto|n[,n]...> for port detection method\n"
|
||||
"probe=<0|1|2> for camera detection method\n"
|
||||
"force_rgb=<0|1> for RGB data format (default BGR)");
|
||||
module_param_array(parport, int, NULL, 0);
|
||||
module_param(probe, int, 0);
|
||||
module_param(force_rgb, bool, 0);
|
||||
module_param(video_nr, int, 0);
|
||||
|
||||
static struct qcam *qcams[MAX_CAMS];
|
||||
static unsigned int num_cams;
|
||||
|
||||
static inline void qcam_set_ack(struct qcam *qcam, unsigned int i)
|
||||
{
|
||||
/* note: the QC specs refer to the PCAck pin by voltage, not
|
||||
software level. PC ports have builtin inverters. */
|
||||
parport_frob_control(qcam->pport, 8, i ? 8 : 0);
|
||||
}
|
||||
|
||||
static inline unsigned int qcam_ready1(struct qcam *qcam)
|
||||
{
|
||||
return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0;
|
||||
}
|
||||
|
||||
static inline unsigned int qcam_ready2(struct qcam *qcam)
|
||||
{
|
||||
return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0;
|
||||
}
|
||||
|
||||
static unsigned int qcam_await_ready1(struct qcam *qcam, int value)
|
||||
{
|
||||
struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
|
||||
unsigned long oldjiffies = jiffies;
|
||||
unsigned int i;
|
||||
|
||||
for (oldjiffies = jiffies;
|
||||
time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
|
||||
if (qcam_ready1(qcam) == value)
|
||||
return 0;
|
||||
|
||||
/* If the camera didn't respond within 1/25 second, poll slowly
|
||||
for a while. */
|
||||
for (i = 0; i < 50; i++) {
|
||||
if (qcam_ready1(qcam) == value)
|
||||
return 0;
|
||||
msleep_interruptible(100);
|
||||
}
|
||||
|
||||
/* Probably somebody pulled the plug out. Not much we can do. */
|
||||
v4l2_err(v4l2_dev, "ready1 timeout (%d) %x %x\n", value,
|
||||
parport_read_status(qcam->pport),
|
||||
parport_read_control(qcam->pport));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned int qcam_await_ready2(struct qcam *qcam, int value)
|
||||
{
|
||||
struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
|
||||
unsigned long oldjiffies = jiffies;
|
||||
unsigned int i;
|
||||
|
||||
for (oldjiffies = jiffies;
|
||||
time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
|
||||
if (qcam_ready2(qcam) == value)
|
||||
return 0;
|
||||
|
||||
/* If the camera didn't respond within 1/25 second, poll slowly
|
||||
for a while. */
|
||||
for (i = 0; i < 50; i++) {
|
||||
if (qcam_ready2(qcam) == value)
|
||||
return 0;
|
||||
msleep_interruptible(100);
|
||||
}
|
||||
|
||||
/* Probably somebody pulled the plug out. Not much we can do. */
|
||||
v4l2_err(v4l2_dev, "ready2 timeout (%d) %x %x %x\n", value,
|
||||
parport_read_status(qcam->pport),
|
||||
parport_read_control(qcam->pport),
|
||||
parport_read_data(qcam->pport));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int qcam_read_data(struct qcam *qcam)
|
||||
{
|
||||
unsigned int idata;
|
||||
|
||||
qcam_set_ack(qcam, 0);
|
||||
if (qcam_await_ready1(qcam, 1))
|
||||
return -1;
|
||||
idata = parport_read_status(qcam->pport) & 0xf0;
|
||||
qcam_set_ack(qcam, 1);
|
||||
if (qcam_await_ready1(qcam, 0))
|
||||
return -1;
|
||||
idata |= parport_read_status(qcam->pport) >> 4;
|
||||
return idata;
|
||||
}
|
||||
|
||||
static int qcam_write_data(struct qcam *qcam, unsigned int data)
|
||||
{
|
||||
struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
|
||||
unsigned int idata;
|
||||
|
||||
parport_write_data(qcam->pport, data);
|
||||
idata = qcam_read_data(qcam);
|
||||
if (data != idata) {
|
||||
v4l2_warn(v4l2_dev, "sent %x but received %x\n", data,
|
||||
idata);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int qcam_set(struct qcam *qcam, unsigned int cmd, unsigned int data)
|
||||
{
|
||||
if (qcam_write_data(qcam, cmd))
|
||||
return -1;
|
||||
if (qcam_write_data(qcam, data))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int qcam_get(struct qcam *qcam, unsigned int cmd)
|
||||
{
|
||||
if (qcam_write_data(qcam, cmd))
|
||||
return -1;
|
||||
return qcam_read_data(qcam);
|
||||
}
|
||||
|
||||
static int qc_detect(struct qcam *qcam)
|
||||
{
|
||||
unsigned int stat, ostat, i, count = 0;
|
||||
|
||||
/* The probe routine below is not very reliable. The IEEE-1284
|
||||
probe takes precedence. */
|
||||
/* XXX Currently parport provides no way to distinguish between
|
||||
"the IEEE probe was not done" and "the probe was done, but
|
||||
no device was found". Fix this one day. */
|
||||
if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA
|
||||
&& qcam->pport->probe_info[0].model
|
||||
&& !strcmp(qcam->pdev->port->probe_info[0].model,
|
||||
"Color QuickCam 2.0")) {
|
||||
printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (probe < 2)
|
||||
return 0;
|
||||
|
||||
parport_write_control(qcam->pport, 0xc);
|
||||
|
||||
/* look for a heartbeat */
|
||||
ostat = stat = parport_read_status(qcam->pport);
|
||||
for (i = 0; i < 250; i++) {
|
||||
mdelay(1);
|
||||
stat = parport_read_status(qcam->pport);
|
||||
if (ostat != stat) {
|
||||
if (++count >= 3)
|
||||
return 1;
|
||||
ostat = stat;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the camera and try again */
|
||||
parport_write_control(qcam->pport, 0xc);
|
||||
parport_write_control(qcam->pport, 0x8);
|
||||
mdelay(1);
|
||||
parport_write_control(qcam->pport, 0xc);
|
||||
mdelay(1);
|
||||
count = 0;
|
||||
|
||||
ostat = stat = parport_read_status(qcam->pport);
|
||||
for (i = 0; i < 250; i++) {
|
||||
mdelay(1);
|
||||
stat = parport_read_status(qcam->pport);
|
||||
if (ostat != stat) {
|
||||
if (++count >= 3)
|
||||
return 1;
|
||||
ostat = stat;
|
||||
}
|
||||
}
|
||||
|
||||
/* no (or flatline) camera, give up */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qc_reset(struct qcam *qcam)
|
||||
{
|
||||
parport_write_control(qcam->pport, 0xc);
|
||||
parport_write_control(qcam->pport, 0x8);
|
||||
mdelay(1);
|
||||
parport_write_control(qcam->pport, 0xc);
|
||||
mdelay(1);
|
||||
}
|
||||
|
||||
/* Reset the QuickCam and program for brightness, contrast,
|
||||
* white-balance, and resolution. */
|
||||
|
||||
static void qc_setup(struct qcam *qcam)
|
||||
{
|
||||
qc_reset(qcam);
|
||||
|
||||
/* Set the brightness. */
|
||||
qcam_set(qcam, 11, qcam->brightness);
|
||||
|
||||
/* Set the height and width. These refer to the actual
|
||||
CCD area *before* applying the selected decimation. */
|
||||
qcam_set(qcam, 17, qcam->ccd_height);
|
||||
qcam_set(qcam, 19, qcam->ccd_width / 2);
|
||||
|
||||
/* Set top and left. */
|
||||
qcam_set(qcam, 0xd, qcam->top);
|
||||
qcam_set(qcam, 0xf, qcam->left);
|
||||
|
||||
/* Set contrast and white balance. */
|
||||
qcam_set(qcam, 0x19, qcam->contrast);
|
||||
qcam_set(qcam, 0x1f, qcam->whitebal);
|
||||
|
||||
/* Set the speed. */
|
||||
qcam_set(qcam, 45, 2);
|
||||
}
|
||||
|
||||
/* Read some bytes from the camera and put them in the buffer.
|
||||
nbytes should be a multiple of 3, because bidirectional mode gives
|
||||
us three bytes at a time. */
|
||||
|
||||
static unsigned int qcam_read_bytes(struct qcam *qcam, unsigned char *buf, unsigned int nbytes)
|
||||
{
|
||||
unsigned int bytes = 0;
|
||||
|
||||
qcam_set_ack(qcam, 0);
|
||||
if (qcam->bidirectional) {
|
||||
/* It's a bidirectional port */
|
||||
while (bytes < nbytes) {
|
||||
unsigned int lo1, hi1, lo2, hi2;
|
||||
unsigned char r, g, b;
|
||||
|
||||
if (qcam_await_ready2(qcam, 1))
|
||||
return bytes;
|
||||
lo1 = parport_read_data(qcam->pport) >> 1;
|
||||
hi1 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
|
||||
qcam_set_ack(qcam, 1);
|
||||
if (qcam_await_ready2(qcam, 0))
|
||||
return bytes;
|
||||
lo2 = parport_read_data(qcam->pport) >> 1;
|
||||
hi2 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
|
||||
qcam_set_ack(qcam, 0);
|
||||
r = lo1 | ((hi1 & 1) << 7);
|
||||
g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1);
|
||||
b = lo2 | ((hi2 & 1) << 7);
|
||||
if (force_rgb) {
|
||||
buf[bytes++] = r;
|
||||
buf[bytes++] = g;
|
||||
buf[bytes++] = b;
|
||||
} else {
|
||||
buf[bytes++] = b;
|
||||
buf[bytes++] = g;
|
||||
buf[bytes++] = r;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* It's a unidirectional port */
|
||||
int i = 0, n = bytes;
|
||||
unsigned char rgb[3];
|
||||
|
||||
while (bytes < nbytes) {
|
||||
unsigned int hi, lo;
|
||||
|
||||
if (qcam_await_ready1(qcam, 1))
|
||||
return bytes;
|
||||
hi = (parport_read_status(qcam->pport) & 0xf0);
|
||||
qcam_set_ack(qcam, 1);
|
||||
if (qcam_await_ready1(qcam, 0))
|
||||
return bytes;
|
||||
lo = (parport_read_status(qcam->pport) & 0xf0);
|
||||
qcam_set_ack(qcam, 0);
|
||||
/* flip some bits */
|
||||
rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88;
|
||||
if (i >= 2) {
|
||||
get_fragment:
|
||||
if (force_rgb) {
|
||||
buf[n++] = rgb[0];
|
||||
buf[n++] = rgb[1];
|
||||
buf[n++] = rgb[2];
|
||||
} else {
|
||||
buf[n++] = rgb[2];
|
||||
buf[n++] = rgb[1];
|
||||
buf[n++] = rgb[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i) {
|
||||
i = 0;
|
||||
goto get_fragment;
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#define BUFSZ 150
|
||||
|
||||
static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len)
|
||||
{
|
||||
struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
|
||||
unsigned lines, pixelsperline;
|
||||
unsigned int is_bi_dir = qcam->bidirectional;
|
||||
size_t wantlen, outptr = 0;
|
||||
char tmpbuf[BUFSZ];
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, buf, len))
|
||||
return -EFAULT;
|
||||
|
||||
/* Wait for camera to become ready */
|
||||
for (;;) {
|
||||
int i = qcam_get(qcam, 41);
|
||||
|
||||
if (i == -1) {
|
||||
qc_setup(qcam);
|
||||
return -EIO;
|
||||
}
|
||||
if ((i & 0x80) == 0)
|
||||
break;
|
||||
schedule();
|
||||
}
|
||||
|
||||
if (qcam_set(qcam, 7, (qcam->mode | (is_bi_dir ? 1 : 0)) + 1))
|
||||
return -EIO;
|
||||
|
||||
lines = qcam->height;
|
||||
pixelsperline = qcam->width;
|
||||
|
||||
if (is_bi_dir) {
|
||||
/* Turn the port around */
|
||||
parport_data_reverse(qcam->pport);
|
||||
mdelay(3);
|
||||
qcam_set_ack(qcam, 0);
|
||||
if (qcam_await_ready1(qcam, 1)) {
|
||||
qc_setup(qcam);
|
||||
return -EIO;
|
||||
}
|
||||
qcam_set_ack(qcam, 1);
|
||||
if (qcam_await_ready1(qcam, 0)) {
|
||||
qc_setup(qcam);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
wantlen = lines * pixelsperline * 24 / 8;
|
||||
|
||||
while (wantlen) {
|
||||
size_t t, s;
|
||||
|
||||
s = (wantlen > BUFSZ) ? BUFSZ : wantlen;
|
||||
t = qcam_read_bytes(qcam, tmpbuf, s);
|
||||
if (outptr < len) {
|
||||
size_t sz = len - outptr;
|
||||
|
||||
if (sz > t)
|
||||
sz = t;
|
||||
if (__copy_to_user(buf + outptr, tmpbuf, sz))
|
||||
break;
|
||||
outptr += sz;
|
||||
}
|
||||
wantlen -= t;
|
||||
if (t < s)
|
||||
break;
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
len = outptr;
|
||||
|
||||
if (wantlen) {
|
||||
v4l2_err(v4l2_dev, "short read.\n");
|
||||
if (is_bi_dir)
|
||||
parport_data_forward(qcam->pport);
|
||||
qc_setup(qcam);
|
||||
return len;
|
||||
}
|
||||
|
||||
if (is_bi_dir) {
|
||||
int l;
|
||||
|
||||
do {
|
||||
l = qcam_read_bytes(qcam, tmpbuf, 3);
|
||||
cond_resched();
|
||||
} while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e));
|
||||
if (force_rgb) {
|
||||
if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
|
||||
v4l2_err(v4l2_dev, "bad EOF\n");
|
||||
} else {
|
||||
if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
|
||||
v4l2_err(v4l2_dev, "bad EOF\n");
|
||||
}
|
||||
qcam_set_ack(qcam, 0);
|
||||
if (qcam_await_ready1(qcam, 1)) {
|
||||
v4l2_err(v4l2_dev, "no ack after EOF\n");
|
||||
parport_data_forward(qcam->pport);
|
||||
qc_setup(qcam);
|
||||
return len;
|
||||
}
|
||||
parport_data_forward(qcam->pport);
|
||||
mdelay(3);
|
||||
qcam_set_ack(qcam, 1);
|
||||
if (qcam_await_ready1(qcam, 0)) {
|
||||
v4l2_err(v4l2_dev, "no ack to port turnaround\n");
|
||||
qc_setup(qcam);
|
||||
return len;
|
||||
}
|
||||
} else {
|
||||
int l;
|
||||
|
||||
do {
|
||||
l = qcam_read_bytes(qcam, tmpbuf, 1);
|
||||
cond_resched();
|
||||
} while (l && tmpbuf[0] == 0x7e);
|
||||
l = qcam_read_bytes(qcam, tmpbuf + 1, 2);
|
||||
if (force_rgb) {
|
||||
if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
|
||||
v4l2_err(v4l2_dev, "bad EOF\n");
|
||||
} else {
|
||||
if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
|
||||
v4l2_err(v4l2_dev, "bad EOF\n");
|
||||
}
|
||||
}
|
||||
|
||||
qcam_write_data(qcam, 0);
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Video4linux interfacing
|
||||
*/
|
||||
|
||||
static int qcam_querycap(struct file *file, void *priv,
|
||||
struct v4l2_capability *vcap)
|
||||
{
|
||||
struct qcam *qcam = video_drvdata(file);
|
||||
|
||||
strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
|
||||
strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card));
|
||||
strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
|
||||
vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
|
||||
vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
|
||||
{
|
||||
if (vin->index > 0)
|
||||
return -EINVAL;
|
||||
strlcpy(vin->name, "Camera", sizeof(vin->name));
|
||||
vin->type = V4L2_INPUT_TYPE_CAMERA;
|
||||
vin->audioset = 0;
|
||||
vin->tuner = 0;
|
||||
vin->std = 0;
|
||||
vin->status = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcam_g_input(struct file *file, void *fh, unsigned int *inp)
|
||||
{
|
||||
*inp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
|
||||
{
|
||||
return (inp > 0) ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
{
|
||||
struct qcam *qcam = video_drvdata(file);
|
||||
struct v4l2_pix_format *pix = &fmt->fmt.pix;
|
||||
|
||||
pix->width = qcam->width;
|
||||
pix->height = qcam->height;
|
||||
pix->pixelformat = V4L2_PIX_FMT_RGB24;
|
||||
pix->field = V4L2_FIELD_NONE;
|
||||
pix->bytesperline = 3 * qcam->width;
|
||||
pix->sizeimage = 3 * qcam->width * qcam->height;
|
||||
/* Just a guess */
|
||||
pix->colorspace = V4L2_COLORSPACE_SRGB;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
{
|
||||
struct v4l2_pix_format *pix = &fmt->fmt.pix;
|
||||
|
||||
if (pix->height < 60 || pix->width < 80) {
|
||||
pix->height = 60;
|
||||
pix->width = 80;
|
||||
} else if (pix->height < 120 || pix->width < 160) {
|
||||
pix->height = 120;
|
||||
pix->width = 160;
|
||||
} else {
|
||||
pix->height = 240;
|
||||
pix->width = 320;
|
||||
}
|
||||
pix->pixelformat = V4L2_PIX_FMT_RGB24;
|
||||
pix->field = V4L2_FIELD_NONE;
|
||||
pix->bytesperline = 3 * pix->width;
|
||||
pix->sizeimage = 3 * pix->width * pix->height;
|
||||
/* Just a guess */
|
||||
pix->colorspace = V4L2_COLORSPACE_SRGB;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
{
|
||||
struct qcam *qcam = video_drvdata(file);
|
||||
struct v4l2_pix_format *pix = &fmt->fmt.pix;
|
||||
int ret = qcam_try_fmt_vid_cap(file, fh, fmt);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
switch (pix->height) {
|
||||
case 60:
|
||||
qcam->mode = QC_DECIMATION_4;
|
||||
break;
|
||||
case 120:
|
||||
qcam->mode = QC_DECIMATION_2;
|
||||
break;
|
||||
default:
|
||||
qcam->mode = QC_DECIMATION_1;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_lock(&qcam->lock);
|
||||
qcam->mode |= QC_MILLIONS;
|
||||
qcam->height = pix->height;
|
||||
qcam->width = pix->width;
|
||||
parport_claim_or_block(qcam->pdev);
|
||||
qc_setup(qcam);
|
||||
parport_release(qcam->pdev);
|
||||
mutex_unlock(&qcam->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
|
||||
{
|
||||
static struct v4l2_fmtdesc formats[] = {
|
||||
{ 0, 0, 0,
|
||||
"RGB 8:8:8", V4L2_PIX_FMT_RGB24,
|
||||
{ 0, 0, 0, 0 }
|
||||
},
|
||||
};
|
||||
enum v4l2_buf_type type = fmt->type;
|
||||
|
||||
if (fmt->index > 0)
|
||||
return -EINVAL;
|
||||
|
||||
*fmt = formats[fmt->index];
|
||||
fmt->type = type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t qcam_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct qcam *qcam = video_drvdata(file);
|
||||
int len;
|
||||
|
||||
mutex_lock(&qcam->lock);
|
||||
parport_claim_or_block(qcam->pdev);
|
||||
/* Probably should have a semaphore against multiple users */
|
||||
len = qc_capture(qcam, buf, count);
|
||||
parport_release(qcam->pdev);
|
||||
mutex_unlock(&qcam->lock);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int qcam_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct qcam *qcam =
|
||||
container_of(ctrl->handler, struct qcam, hdl);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&qcam->lock);
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
qcam->brightness = ctrl->val;
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
qcam->contrast = ctrl->val;
|
||||
break;
|
||||
case V4L2_CID_GAMMA:
|
||||
qcam->whitebal = ctrl->val;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (ret == 0) {
|
||||
parport_claim_or_block(qcam->pdev);
|
||||
qc_setup(qcam);
|
||||
parport_release(qcam->pdev);
|
||||
}
|
||||
mutex_unlock(&qcam->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct v4l2_file_operations qcam_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = v4l2_fh_open,
|
||||
.release = v4l2_fh_release,
|
||||
.poll = v4l2_ctrl_poll,
|
||||
.unlocked_ioctl = video_ioctl2,
|
||||
.read = qcam_read,
|
||||
};
|
||||
|
||||
static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
|
||||
.vidioc_querycap = qcam_querycap,
|
||||
.vidioc_g_input = qcam_g_input,
|
||||
.vidioc_s_input = qcam_s_input,
|
||||
.vidioc_enum_input = qcam_enum_input,
|
||||
.vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap,
|
||||
.vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap,
|
||||
.vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap,
|
||||
.vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap,
|
||||
.vidioc_log_status = v4l2_ctrl_log_status,
|
||||
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
||||
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
||||
};
|
||||
|
||||
static const struct v4l2_ctrl_ops qcam_ctrl_ops = {
|
||||
.s_ctrl = qcam_s_ctrl,
|
||||
};
|
||||
|
||||
/* Initialize the QuickCam driver control structure. */
|
||||
|
||||
static struct qcam *qcam_init(struct parport *port)
|
||||
{
|
||||
struct qcam *qcam;
|
||||
struct v4l2_device *v4l2_dev;
|
||||
|
||||
qcam = kzalloc(sizeof(*qcam), GFP_KERNEL);
|
||||
if (qcam == NULL)
|
||||
return NULL;
|
||||
|
||||
v4l2_dev = &qcam->v4l2_dev;
|
||||
strlcpy(v4l2_dev->name, "c-qcam", sizeof(v4l2_dev->name));
|
||||
|
||||
if (v4l2_device_register(NULL, v4l2_dev) < 0) {
|
||||
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
|
||||
kfree(qcam);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
v4l2_ctrl_handler_init(&qcam->hdl, 3);
|
||||
v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 255, 1, 240);
|
||||
v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 255, 1, 192);
|
||||
v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
|
||||
V4L2_CID_GAMMA, 0, 255, 1, 128);
|
||||
if (qcam->hdl.error) {
|
||||
v4l2_err(v4l2_dev, "couldn't register controls\n");
|
||||
v4l2_ctrl_handler_free(&qcam->hdl);
|
||||
kfree(qcam);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qcam->pport = port;
|
||||
qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
|
||||
NULL, 0, NULL);
|
||||
|
||||
qcam->bidirectional = (qcam->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0;
|
||||
|
||||
if (qcam->pdev == NULL) {
|
||||
v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
|
||||
v4l2_ctrl_handler_free(&qcam->hdl);
|
||||
kfree(qcam);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strlcpy(qcam->vdev.name, "Colour QuickCam", sizeof(qcam->vdev.name));
|
||||
qcam->vdev.v4l2_dev = v4l2_dev;
|
||||
qcam->vdev.fops = &qcam_fops;
|
||||
qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
|
||||
qcam->vdev.release = video_device_release_empty;
|
||||
qcam->vdev.ctrl_handler = &qcam->hdl;
|
||||
video_set_drvdata(&qcam->vdev, qcam);
|
||||
|
||||
mutex_init(&qcam->lock);
|
||||
qcam->width = qcam->ccd_width = 320;
|
||||
qcam->height = qcam->ccd_height = 240;
|
||||
qcam->mode = QC_MILLIONS | QC_DECIMATION_1;
|
||||
qcam->contrast = 192;
|
||||
qcam->brightness = 240;
|
||||
qcam->whitebal = 128;
|
||||
qcam->top = 1;
|
||||
qcam->left = 14;
|
||||
return qcam;
|
||||
}
|
||||
|
||||
static int init_cqcam(struct parport *port)
|
||||
{
|
||||
struct qcam *qcam;
|
||||
struct v4l2_device *v4l2_dev;
|
||||
|
||||
if (parport[0] != -1) {
|
||||
/* The user gave specific instructions */
|
||||
int i, found = 0;
|
||||
|
||||
for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) {
|
||||
if (parport[0] == port->number)
|
||||
found = 1;
|
||||
}
|
||||
if (!found)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (num_cams == MAX_CAMS)
|
||||
return -ENOSPC;
|
||||
|
||||
qcam = qcam_init(port);
|
||||
if (qcam == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
v4l2_dev = &qcam->v4l2_dev;
|
||||
|
||||
parport_claim_or_block(qcam->pdev);
|
||||
|
||||
qc_reset(qcam);
|
||||
|
||||
if (probe && qc_detect(qcam) == 0) {
|
||||
parport_release(qcam->pdev);
|
||||
parport_unregister_device(qcam->pdev);
|
||||
kfree(qcam);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
qc_setup(qcam);
|
||||
|
||||
parport_release(qcam->pdev);
|
||||
|
||||
if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
|
||||
v4l2_err(v4l2_dev, "Unable to register Colour QuickCam on %s\n",
|
||||
qcam->pport->name);
|
||||
parport_unregister_device(qcam->pdev);
|
||||
kfree(qcam);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
v4l2_info(v4l2_dev, "%s: Colour QuickCam found on %s\n",
|
||||
video_device_node_name(&qcam->vdev), qcam->pport->name);
|
||||
|
||||
qcams[num_cams++] = qcam;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void close_cqcam(struct qcam *qcam)
|
||||
{
|
||||
video_unregister_device(&qcam->vdev);
|
||||
v4l2_ctrl_handler_free(&qcam->hdl);
|
||||
parport_unregister_device(qcam->pdev);
|
||||
kfree(qcam);
|
||||
}
|
||||
|
||||
static void cq_attach(struct parport *port)
|
||||
{
|
||||
init_cqcam(port);
|
||||
}
|
||||
|
||||
static void cq_detach(struct parport *port)
|
||||
{
|
||||
/* Write this some day. */
|
||||
}
|
||||
|
||||
static struct parport_driver cqcam_driver = {
|
||||
.name = "cqcam",
|
||||
.attach = cq_attach,
|
||||
.detach = cq_detach,
|
||||
};
|
||||
|
||||
static int __init cqcam_init(void)
|
||||
{
|
||||
printk(KERN_INFO BANNER "\n");
|
||||
|
||||
return parport_register_driver(&cqcam_driver);
|
||||
}
|
||||
|
||||
static void __exit cqcam_cleanup(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < num_cams; i++)
|
||||
close_cqcam(qcams[i]);
|
||||
|
||||
parport_unregister_driver(&cqcam_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Philip Blundell <[email protected]>");
|
||||
MODULE_DESCRIPTION(BANNER);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.0.4");
|
||||
|
||||
module_init(cqcam_init);
|
||||
module_exit(cqcam_cleanup);
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,980 +0,0 @@
|
|||
/*
|
||||
Winbond w9966cf Webcam parport driver.
|
||||
|
||||
Version 0.33
|
||||
|
||||
Copyright (C) 2001 Jakob Kemi <jakob.kemi@post.utfors.se>
|
||||
|
||||
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.
|
||||
*/
|
||||
/*
|
||||
Supported devices:
|
||||
*Lifeview FlyCam Supra (using the Philips saa7111a chip)
|
||||
|
||||
Does any other model using the w9966 interface chip exist ?
|
||||
|
||||
Todo:
|
||||
|
||||
*Add a working EPP mode, since DMA ECP read isn't implemented
|
||||
in the parport drivers. (That's why it's so sloow)
|
||||
|
||||
*Add support for other ccd-control chips than the saa7111
|
||||
please send me feedback on what kind of chips you have.
|
||||
|
||||
*Add proper probing. I don't know what's wrong with the IEEE1284
|
||||
parport drivers but (IEEE1284_MODE_NIBBLE|IEEE1284_DEVICE_ID)
|
||||
and nibble read seems to be broken for some peripherals.
|
||||
|
||||
*Add probing for onboard SRAM, port directions etc. (if possible)
|
||||
|
||||
*Add support for the hardware compressed modes (maybe using v4l2)
|
||||
|
||||
*Fix better support for the capture window (no skewed images, v4l
|
||||
interface to capt. window)
|
||||
|
||||
*Probably some bugs that I don't know of
|
||||
|
||||
Please support me by sending feedback!
|
||||
|
||||
Changes:
|
||||
|
||||
Alan Cox: Removed RGB mode for kernel merge, added THIS_MODULE
|
||||
and owner support for newer module locks
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/slab.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-fh.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-event.h>
|
||||
#include <linux/parport.h>
|
||||
|
||||
/*#define DEBUG*/ /* Undef me for production */
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __func__ , ##a)
|
||||
#else
|
||||
#define DPRINTF(x...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Defines, simple typedefs etc.
|
||||
*/
|
||||
|
||||
#define W9966_DRIVERNAME "W9966CF Webcam"
|
||||
#define W9966_MAXCAMS 4 /* Maximum number of cameras */
|
||||
#define W9966_RBUFFER 2048 /* Read buffer (must be an even number) */
|
||||
#define W9966_SRAMSIZE 131072 /* 128kb */
|
||||
#define W9966_SRAMID 0x02 /* check w9966cf.pdf */
|
||||
|
||||
/* Empirically determined window limits */
|
||||
#define W9966_WND_MIN_X 16
|
||||
#define W9966_WND_MIN_Y 14
|
||||
#define W9966_WND_MAX_X 705
|
||||
#define W9966_WND_MAX_Y 253
|
||||
#define W9966_WND_MAX_W (W9966_WND_MAX_X - W9966_WND_MIN_X)
|
||||
#define W9966_WND_MAX_H (W9966_WND_MAX_Y - W9966_WND_MIN_Y)
|
||||
|
||||
/* Keep track of our current state */
|
||||
#define W9966_STATE_PDEV 0x01
|
||||
#define W9966_STATE_CLAIMED 0x02
|
||||
#define W9966_STATE_VDEV 0x04
|
||||
|
||||
#define W9966_I2C_W_ID 0x48
|
||||
#define W9966_I2C_R_ID 0x49
|
||||
#define W9966_I2C_R_DATA 0x08
|
||||
#define W9966_I2C_R_CLOCK 0x04
|
||||
#define W9966_I2C_W_DATA 0x02
|
||||
#define W9966_I2C_W_CLOCK 0x01
|
||||
|
||||
struct w9966 {
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
unsigned char dev_state;
|
||||
unsigned char i2c_state;
|
||||
unsigned short ppmode;
|
||||
struct parport *pport;
|
||||
struct pardevice *pdev;
|
||||
struct video_device vdev;
|
||||
unsigned short width;
|
||||
unsigned short height;
|
||||
unsigned char brightness;
|
||||
signed char contrast;
|
||||
signed char color;
|
||||
signed char hue;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
/*
|
||||
* Module specific properties
|
||||
*/
|
||||
|
||||
MODULE_AUTHOR("Jakob Kemi <[email protected]>");
|
||||
MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.33.1");
|
||||
|
||||
#ifdef MODULE
|
||||
static char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
|
||||
#else
|
||||
static char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"};
|
||||
#endif
|
||||
module_param_array(pardev, charp, NULL, 0);
|
||||
MODULE_PARM_DESC(pardev, "pardev: where to search for\n"
|
||||
"\teach camera. 'aggressive' means brute-force search.\n"
|
||||
"\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n"
|
||||
"\tcam 1 to parport3 and search every parport for cam 2 etc...");
|
||||
|
||||
static int parmode;
|
||||
module_param(parmode, int, 0);
|
||||
MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp");
|
||||
|
||||
static int video_nr = -1;
|
||||
module_param(video_nr, int, 0);
|
||||
|
||||
static struct w9966 w9966_cams[W9966_MAXCAMS];
|
||||
|
||||
/*
|
||||
* Private function defines
|
||||
*/
|
||||
|
||||
|
||||
/* Set camera phase flags, so we know what to uninit when terminating */
|
||||
static inline void w9966_set_state(struct w9966 *cam, int mask, int val)
|
||||
{
|
||||
cam->dev_state = (cam->dev_state & ~mask) ^ val;
|
||||
}
|
||||
|
||||
/* Get camera phase flags */
|
||||
static inline int w9966_get_state(struct w9966 *cam, int mask, int val)
|
||||
{
|
||||
return ((cam->dev_state & mask) == val);
|
||||
}
|
||||
|
||||
/* Claim parport for ourself */
|
||||
static void w9966_pdev_claim(struct w9966 *cam)
|
||||
{
|
||||
if (w9966_get_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED))
|
||||
return;
|
||||
parport_claim_or_block(cam->pdev);
|
||||
w9966_set_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED);
|
||||
}
|
||||
|
||||
/* Release parport for others to use */
|
||||
static void w9966_pdev_release(struct w9966 *cam)
|
||||
{
|
||||
if (w9966_get_state(cam, W9966_STATE_CLAIMED, 0))
|
||||
return;
|
||||
parport_release(cam->pdev);
|
||||
w9966_set_state(cam, W9966_STATE_CLAIMED, 0);
|
||||
}
|
||||
|
||||
/* Read register from W9966 interface-chip
|
||||
Expects a claimed pdev
|
||||
-1 on error, else register data (byte) */
|
||||
static int w9966_read_reg(struct w9966 *cam, int reg)
|
||||
{
|
||||
/* ECP, read, regtransfer, REG, REG, REG, REG, REG */
|
||||
const unsigned char addr = 0x80 | (reg & 0x1f);
|
||||
unsigned char val;
|
||||
|
||||
if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
|
||||
return -1;
|
||||
if (parport_write(cam->pport, &addr, 1) != 1)
|
||||
return -1;
|
||||
if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
|
||||
return -1;
|
||||
if (parport_read(cam->pport, &val, 1) != 1)
|
||||
return -1;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Write register to W9966 interface-chip
|
||||
Expects a claimed pdev
|
||||
-1 on error */
|
||||
static int w9966_write_reg(struct w9966 *cam, int reg, int data)
|
||||
{
|
||||
/* ECP, write, regtransfer, REG, REG, REG, REG, REG */
|
||||
const unsigned char addr = 0xc0 | (reg & 0x1f);
|
||||
const unsigned char val = data;
|
||||
|
||||
if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
|
||||
return -1;
|
||||
if (parport_write(cam->pport, &addr, 1) != 1)
|
||||
return -1;
|
||||
if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
|
||||
return -1;
|
||||
if (parport_write(cam->pport, &val, 1) != 1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ugly and primitive i2c protocol functions
|
||||
*/
|
||||
|
||||
/* Sets the data line on the i2c bus.
|
||||
Expects a claimed pdev. */
|
||||
static void w9966_i2c_setsda(struct w9966 *cam, int state)
|
||||
{
|
||||
if (state)
|
||||
cam->i2c_state |= W9966_I2C_W_DATA;
|
||||
else
|
||||
cam->i2c_state &= ~W9966_I2C_W_DATA;
|
||||
|
||||
w9966_write_reg(cam, 0x18, cam->i2c_state);
|
||||
udelay(5);
|
||||
}
|
||||
|
||||
/* Get peripheral clock line
|
||||
Expects a claimed pdev. */
|
||||
static int w9966_i2c_getscl(struct w9966 *cam)
|
||||
{
|
||||
const unsigned char state = w9966_read_reg(cam, 0x18);
|
||||
return ((state & W9966_I2C_R_CLOCK) > 0);
|
||||
}
|
||||
|
||||
/* Sets the clock line on the i2c bus.
|
||||
Expects a claimed pdev. -1 on error */
|
||||
static int w9966_i2c_setscl(struct w9966 *cam, int state)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
if (state)
|
||||
cam->i2c_state |= W9966_I2C_W_CLOCK;
|
||||
else
|
||||
cam->i2c_state &= ~W9966_I2C_W_CLOCK;
|
||||
|
||||
w9966_write_reg(cam, 0x18, cam->i2c_state);
|
||||
udelay(5);
|
||||
|
||||
/* we go to high, we also expect the peripheral to ack. */
|
||||
if (state) {
|
||||
timeout = jiffies + 100;
|
||||
while (!w9966_i2c_getscl(cam)) {
|
||||
if (time_after(jiffies, timeout))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Get peripheral data line
|
||||
Expects a claimed pdev. */
|
||||
static int w9966_i2c_getsda(struct w9966 *cam)
|
||||
{
|
||||
const unsigned char state = w9966_read_reg(cam, 0x18);
|
||||
return ((state & W9966_I2C_R_DATA) > 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Write a byte with ack to the i2c bus.
|
||||
Expects a claimed pdev. -1 on error */
|
||||
static int w9966_i2c_wbyte(struct w9966 *cam, int data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 7; i >= 0; i--) {
|
||||
w9966_i2c_setsda(cam, (data >> i) & 0x01);
|
||||
|
||||
if (w9966_i2c_setscl(cam, 1) == -1)
|
||||
return -1;
|
||||
w9966_i2c_setscl(cam, 0);
|
||||
}
|
||||
|
||||
w9966_i2c_setsda(cam, 1);
|
||||
|
||||
if (w9966_i2c_setscl(cam, 1) == -1)
|
||||
return -1;
|
||||
w9966_i2c_setscl(cam, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read a data byte with ack from the i2c-bus
|
||||
Expects a claimed pdev. -1 on error */
|
||||
#if 0
|
||||
static int w9966_i2c_rbyte(struct w9966 *cam)
|
||||
{
|
||||
unsigned char data = 0x00;
|
||||
int i;
|
||||
|
||||
w9966_i2c_setsda(cam, 1);
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (w9966_i2c_setscl(cam, 1) == -1)
|
||||
return -1;
|
||||
data = data << 1;
|
||||
if (w9966_i2c_getsda(cam))
|
||||
data |= 0x01;
|
||||
|
||||
w9966_i2c_setscl(cam, 0);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Read a register from the i2c device.
|
||||
Expects claimed pdev. -1 on error */
|
||||
#if 0
|
||||
static int w9966_read_reg_i2c(struct w9966 *cam, int reg)
|
||||
{
|
||||
int data;
|
||||
|
||||
w9966_i2c_setsda(cam, 0);
|
||||
w9966_i2c_setscl(cam, 0);
|
||||
|
||||
if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
|
||||
w9966_i2c_wbyte(cam, reg) == -1)
|
||||
return -1;
|
||||
|
||||
w9966_i2c_setsda(cam, 1);
|
||||
if (w9966_i2c_setscl(cam, 1) == -1)
|
||||
return -1;
|
||||
w9966_i2c_setsda(cam, 0);
|
||||
w9966_i2c_setscl(cam, 0);
|
||||
|
||||
if (w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1)
|
||||
return -1;
|
||||
data = w9966_i2c_rbyte(cam);
|
||||
if (data == -1)
|
||||
return -1;
|
||||
|
||||
w9966_i2c_setsda(cam, 0);
|
||||
|
||||
if (w9966_i2c_setscl(cam, 1) == -1)
|
||||
return -1;
|
||||
w9966_i2c_setsda(cam, 1);
|
||||
|
||||
return data;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Write a register to the i2c device.
|
||||
Expects claimed pdev. -1 on error */
|
||||
static int w9966_write_reg_i2c(struct w9966 *cam, int reg, int data)
|
||||
{
|
||||
w9966_i2c_setsda(cam, 0);
|
||||
w9966_i2c_setscl(cam, 0);
|
||||
|
||||
if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
|
||||
w9966_i2c_wbyte(cam, reg) == -1 ||
|
||||
w9966_i2c_wbyte(cam, data) == -1)
|
||||
return -1;
|
||||
|
||||
w9966_i2c_setsda(cam, 0);
|
||||
if (w9966_i2c_setscl(cam, 1) == -1)
|
||||
return -1;
|
||||
|
||||
w9966_i2c_setsda(cam, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find a good length for capture window (used both for W and H)
|
||||
A bit ugly but pretty functional. The capture length
|
||||
have to match the downscale */
|
||||
static int w9966_findlen(int near, int size, int maxlen)
|
||||
{
|
||||
int bestlen = size;
|
||||
int besterr = abs(near - bestlen);
|
||||
int len;
|
||||
|
||||
for (len = size + 1; len < maxlen; len++) {
|
||||
int err;
|
||||
if (((64 * size) % len) != 0)
|
||||
continue;
|
||||
|
||||
err = abs(near - len);
|
||||
|
||||
/* Only continue as long as we keep getting better values */
|
||||
if (err > besterr)
|
||||
break;
|
||||
|
||||
besterr = err;
|
||||
bestlen = len;
|
||||
}
|
||||
|
||||
return bestlen;
|
||||
}
|
||||
|
||||
/* Modify capture window (if necessary)
|
||||
and calculate downscaling
|
||||
Return -1 on error */
|
||||
static int w9966_calcscale(int size, int min, int max, int *beg, int *end, unsigned char *factor)
|
||||
{
|
||||
int maxlen = max - min;
|
||||
int len = *end - *beg + 1;
|
||||
int newlen = w9966_findlen(len, size, maxlen);
|
||||
int err = newlen - len;
|
||||
|
||||
/* Check for bad format */
|
||||
if (newlen > maxlen || newlen < size)
|
||||
return -1;
|
||||
|
||||
/* Set factor (6 bit fixed) */
|
||||
*factor = (64 * size) / newlen;
|
||||
if (*factor == 64)
|
||||
*factor = 0x00; /* downscale is disabled */
|
||||
else
|
||||
*factor |= 0x80; /* set downscale-enable bit */
|
||||
|
||||
/* Modify old beginning and end */
|
||||
*beg -= err / 2;
|
||||
*end += err - (err / 2);
|
||||
|
||||
/* Move window if outside borders */
|
||||
if (*beg < min) {
|
||||
*end += min - *beg;
|
||||
*beg += min - *beg;
|
||||
}
|
||||
if (*end > max) {
|
||||
*beg -= *end - max;
|
||||
*end -= *end - max;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Setup the cameras capture window etc.
|
||||
Expects a claimed pdev
|
||||
return -1 on error */
|
||||
static int w9966_setup(struct w9966 *cam, int x1, int y1, int x2, int y2, int w, int h)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int enh_s, enh_e;
|
||||
unsigned char scale_x, scale_y;
|
||||
unsigned char regs[0x1c];
|
||||
unsigned char saa7111_regs[] = {
|
||||
0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00,
|
||||
0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00,
|
||||
0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0
|
||||
};
|
||||
|
||||
|
||||
if (w * h * 2 > W9966_SRAMSIZE) {
|
||||
DPRINTF("capture window exceeds SRAM size!.\n");
|
||||
w = 200; h = 160; /* Pick default values */
|
||||
}
|
||||
|
||||
w &= ~0x1;
|
||||
if (w < 2)
|
||||
w = 2;
|
||||
if (h < 1)
|
||||
h = 1;
|
||||
if (w > W9966_WND_MAX_W)
|
||||
w = W9966_WND_MAX_W;
|
||||
if (h > W9966_WND_MAX_H)
|
||||
h = W9966_WND_MAX_H;
|
||||
|
||||
cam->width = w;
|
||||
cam->height = h;
|
||||
|
||||
enh_s = 0;
|
||||
enh_e = w * h * 2;
|
||||
|
||||
/* Modify capture window if necessary and calculate downscaling */
|
||||
if (w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 ||
|
||||
w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0)
|
||||
return -1;
|
||||
|
||||
DPRINTF("%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n",
|
||||
w, h, x1, x2, y1, y2, scale_x & ~0x80, scale_y & ~0x80);
|
||||
|
||||
/* Setup registers */
|
||||
regs[0x00] = 0x00; /* Set normal operation */
|
||||
regs[0x01] = 0x18; /* Capture mode */
|
||||
regs[0x02] = scale_y; /* V-scaling */
|
||||
regs[0x03] = scale_x; /* H-scaling */
|
||||
|
||||
/* Capture window */
|
||||
regs[0x04] = (x1 & 0x0ff); /* X-start (8 low bits) */
|
||||
regs[0x05] = (x1 & 0x300)>>8; /* X-start (2 high bits) */
|
||||
regs[0x06] = (y1 & 0x0ff); /* Y-start (8 low bits) */
|
||||
regs[0x07] = (y1 & 0x300)>>8; /* Y-start (2 high bits) */
|
||||
regs[0x08] = (x2 & 0x0ff); /* X-end (8 low bits) */
|
||||
regs[0x09] = (x2 & 0x300)>>8; /* X-end (2 high bits) */
|
||||
regs[0x0a] = (y2 & 0x0ff); /* Y-end (8 low bits) */
|
||||
|
||||
regs[0x0c] = W9966_SRAMID; /* SRAM-banks (1x 128kb) */
|
||||
|
||||
/* Enhancement layer */
|
||||
regs[0x0d] = (enh_s & 0x000ff); /* Enh. start (0-7) */
|
||||
regs[0x0e] = (enh_s & 0x0ff00) >> 8; /* Enh. start (8-15) */
|
||||
regs[0x0f] = (enh_s & 0x70000) >> 16; /* Enh. start (16-17/18??) */
|
||||
regs[0x10] = (enh_e & 0x000ff); /* Enh. end (0-7) */
|
||||
regs[0x11] = (enh_e & 0x0ff00) >> 8; /* Enh. end (8-15) */
|
||||
regs[0x12] = (enh_e & 0x70000) >> 16; /* Enh. end (16-17/18??) */
|
||||
|
||||
/* Misc */
|
||||
regs[0x13] = 0x40; /* VEE control (raw 4:2:2) */
|
||||
regs[0x17] = 0x00; /* ??? */
|
||||
regs[0x18] = cam->i2c_state = 0x00; /* Serial bus */
|
||||
regs[0x19] = 0xff; /* I/O port direction control */
|
||||
regs[0x1a] = 0xff; /* I/O port data register */
|
||||
regs[0x1b] = 0x10; /* ??? */
|
||||
|
||||
/* SAA7111 chip settings */
|
||||
saa7111_regs[0x0a] = cam->brightness;
|
||||
saa7111_regs[0x0b] = cam->contrast;
|
||||
saa7111_regs[0x0c] = cam->color;
|
||||
saa7111_regs[0x0d] = cam->hue;
|
||||
|
||||
/* Reset (ECP-fifo & serial-bus) */
|
||||
if (w9966_write_reg(cam, 0x00, 0x03) == -1)
|
||||
return -1;
|
||||
|
||||
/* Write regs to w9966cf chip */
|
||||
for (i = 0; i < 0x1c; i++)
|
||||
if (w9966_write_reg(cam, i, regs[i]) == -1)
|
||||
return -1;
|
||||
|
||||
/* Write regs to saa7111 chip */
|
||||
for (i = 0; i < 0x20; i++)
|
||||
if (w9966_write_reg_i2c(cam, i, saa7111_regs[i]) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Video4linux interfacing
|
||||
*/
|
||||
|
||||
static int cam_querycap(struct file *file, void *priv,
|
||||
struct v4l2_capability *vcap)
|
||||
{
|
||||
struct w9966 *cam = video_drvdata(file);
|
||||
|
||||
strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver));
|
||||
strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card));
|
||||
strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
|
||||
vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
|
||||
vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
|
||||
{
|
||||
if (vin->index > 0)
|
||||
return -EINVAL;
|
||||
strlcpy(vin->name, "Camera", sizeof(vin->name));
|
||||
vin->type = V4L2_INPUT_TYPE_CAMERA;
|
||||
vin->audioset = 0;
|
||||
vin->tuner = 0;
|
||||
vin->std = 0;
|
||||
vin->status = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cam_g_input(struct file *file, void *fh, unsigned int *inp)
|
||||
{
|
||||
*inp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cam_s_input(struct file *file, void *fh, unsigned int inp)
|
||||
{
|
||||
return (inp > 0) ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
static int cam_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct w9966 *cam =
|
||||
container_of(ctrl->handler, struct w9966, hdl);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&cam->lock);
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
cam->brightness = ctrl->val;
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
cam->contrast = ctrl->val;
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
cam->color = ctrl->val;
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
cam->hue = ctrl->val;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
w9966_pdev_claim(cam);
|
||||
|
||||
if (w9966_write_reg_i2c(cam, 0x0a, cam->brightness) == -1 ||
|
||||
w9966_write_reg_i2c(cam, 0x0b, cam->contrast) == -1 ||
|
||||
w9966_write_reg_i2c(cam, 0x0c, cam->color) == -1 ||
|
||||
w9966_write_reg_i2c(cam, 0x0d, cam->hue) == -1) {
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
w9966_pdev_release(cam);
|
||||
}
|
||||
mutex_unlock(&cam->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
{
|
||||
struct w9966 *cam = video_drvdata(file);
|
||||
struct v4l2_pix_format *pix = &fmt->fmt.pix;
|
||||
|
||||
pix->width = cam->width;
|
||||
pix->height = cam->height;
|
||||
pix->pixelformat = V4L2_PIX_FMT_YUYV;
|
||||
pix->field = V4L2_FIELD_NONE;
|
||||
pix->bytesperline = 2 * cam->width;
|
||||
pix->sizeimage = 2 * cam->width * cam->height;
|
||||
/* Just a guess */
|
||||
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
{
|
||||
struct v4l2_pix_format *pix = &fmt->fmt.pix;
|
||||
|
||||
if (pix->width < 2)
|
||||
pix->width = 2;
|
||||
if (pix->height < 1)
|
||||
pix->height = 1;
|
||||
if (pix->width > W9966_WND_MAX_W)
|
||||
pix->width = W9966_WND_MAX_W;
|
||||
if (pix->height > W9966_WND_MAX_H)
|
||||
pix->height = W9966_WND_MAX_H;
|
||||
pix->pixelformat = V4L2_PIX_FMT_YUYV;
|
||||
pix->field = V4L2_FIELD_NONE;
|
||||
pix->bytesperline = 2 * pix->width;
|
||||
pix->sizeimage = 2 * pix->width * pix->height;
|
||||
/* Just a guess */
|
||||
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
{
|
||||
struct w9966 *cam = video_drvdata(file);
|
||||
struct v4l2_pix_format *pix = &fmt->fmt.pix;
|
||||
int ret = cam_try_fmt_vid_cap(file, fh, fmt);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&cam->lock);
|
||||
/* Update camera regs */
|
||||
w9966_pdev_claim(cam);
|
||||
ret = w9966_setup(cam, 0, 0, 1023, 1023, pix->width, pix->height);
|
||||
w9966_pdev_release(cam);
|
||||
mutex_unlock(&cam->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
|
||||
{
|
||||
static struct v4l2_fmtdesc formats[] = {
|
||||
{ 0, 0, 0,
|
||||
"YUV 4:2:2", V4L2_PIX_FMT_YUYV,
|
||||
{ 0, 0, 0, 0 }
|
||||
},
|
||||
};
|
||||
enum v4l2_buf_type type = fmt->type;
|
||||
|
||||
if (fmt->index > 0)
|
||||
return -EINVAL;
|
||||
|
||||
*fmt = formats[fmt->index];
|
||||
fmt->type = type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Capture data */
|
||||
static ssize_t w9966_v4l_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct w9966 *cam = video_drvdata(file);
|
||||
unsigned char addr = 0xa0; /* ECP, read, CCD-transfer, 00000 */
|
||||
unsigned char __user *dest = (unsigned char __user *)buf;
|
||||
unsigned long dleft = count;
|
||||
unsigned char *tbuf;
|
||||
|
||||
/* Why would anyone want more than this?? */
|
||||
if (count > cam->width * cam->height * 2)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&cam->lock);
|
||||
w9966_pdev_claim(cam);
|
||||
w9966_write_reg(cam, 0x00, 0x02); /* Reset ECP-FIFO buffer */
|
||||
w9966_write_reg(cam, 0x00, 0x00); /* Return to normal operation */
|
||||
w9966_write_reg(cam, 0x01, 0x98); /* Enable capture */
|
||||
|
||||
/* write special capture-addr and negotiate into data transfer */
|
||||
if ((parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0) ||
|
||||
(parport_write(cam->pport, &addr, 1) != 1) ||
|
||||
(parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0)) {
|
||||
w9966_pdev_release(cam);
|
||||
mutex_unlock(&cam->lock);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL);
|
||||
if (tbuf == NULL) {
|
||||
count = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (dleft > 0) {
|
||||
unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft;
|
||||
|
||||
if (parport_read(cam->pport, tbuf, tsize) < tsize) {
|
||||
count = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
if (copy_to_user(dest, tbuf, tsize) != 0) {
|
||||
count = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
dest += tsize;
|
||||
dleft -= tsize;
|
||||
}
|
||||
|
||||
w9966_write_reg(cam, 0x01, 0x18); /* Disable capture */
|
||||
|
||||
out:
|
||||
kfree(tbuf);
|
||||
w9966_pdev_release(cam);
|
||||
mutex_unlock(&cam->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct v4l2_file_operations w9966_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = v4l2_fh_open,
|
||||
.release = v4l2_fh_release,
|
||||
.poll = v4l2_ctrl_poll,
|
||||
.unlocked_ioctl = video_ioctl2,
|
||||
.read = w9966_v4l_read,
|
||||
};
|
||||
|
||||
static const struct v4l2_ioctl_ops w9966_ioctl_ops = {
|
||||
.vidioc_querycap = cam_querycap,
|
||||
.vidioc_g_input = cam_g_input,
|
||||
.vidioc_s_input = cam_s_input,
|
||||
.vidioc_enum_input = cam_enum_input,
|
||||
.vidioc_enum_fmt_vid_cap = cam_enum_fmt_vid_cap,
|
||||
.vidioc_g_fmt_vid_cap = cam_g_fmt_vid_cap,
|
||||
.vidioc_s_fmt_vid_cap = cam_s_fmt_vid_cap,
|
||||
.vidioc_try_fmt_vid_cap = cam_try_fmt_vid_cap,
|
||||
.vidioc_log_status = v4l2_ctrl_log_status,
|
||||
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
||||
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
||||
};
|
||||
|
||||
static const struct v4l2_ctrl_ops cam_ctrl_ops = {
|
||||
.s_ctrl = cam_s_ctrl,
|
||||
};
|
||||
|
||||
|
||||
/* Initialize camera device. Setup all internal flags, set a
|
||||
default video mode, setup ccd-chip, register v4l device etc..
|
||||
Also used for 'probing' of hardware.
|
||||
-1 on error */
|
||||
static int w9966_init(struct w9966 *cam, struct parport *port)
|
||||
{
|
||||
struct v4l2_device *v4l2_dev = &cam->v4l2_dev;
|
||||
|
||||
if (cam->dev_state != 0)
|
||||
return -1;
|
||||
|
||||
strlcpy(v4l2_dev->name, "w9966", sizeof(v4l2_dev->name));
|
||||
|
||||
if (v4l2_device_register(NULL, v4l2_dev) < 0) {
|
||||
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
v4l2_ctrl_handler_init(&cam->hdl, 4);
|
||||
v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
|
||||
v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, -64, 64, 1, 64);
|
||||
v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
|
||||
V4L2_CID_SATURATION, -64, 64, 1, 64);
|
||||
v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
|
||||
V4L2_CID_HUE, -128, 127, 1, 0);
|
||||
if (cam->hdl.error) {
|
||||
v4l2_err(v4l2_dev, "couldn't register controls\n");
|
||||
return -1;
|
||||
}
|
||||
cam->pport = port;
|
||||
cam->brightness = 128;
|
||||
cam->contrast = 64;
|
||||
cam->color = 64;
|
||||
cam->hue = 0;
|
||||
|
||||
/* Select requested transfer mode */
|
||||
switch (parmode) {
|
||||
default: /* Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) */
|
||||
case 0:
|
||||
if (port->modes & PARPORT_MODE_ECP)
|
||||
cam->ppmode = IEEE1284_MODE_ECP;
|
||||
else if (port->modes & PARPORT_MODE_EPP)
|
||||
cam->ppmode = IEEE1284_MODE_EPP;
|
||||
else
|
||||
cam->ppmode = IEEE1284_MODE_ECP;
|
||||
break;
|
||||
case 1: /* hw- or sw-ecp */
|
||||
cam->ppmode = IEEE1284_MODE_ECP;
|
||||
break;
|
||||
case 2: /* hw- or sw-epp */
|
||||
cam->ppmode = IEEE1284_MODE_EPP;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Tell the parport driver that we exists */
|
||||
cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL);
|
||||
if (cam->pdev == NULL) {
|
||||
DPRINTF("parport_register_device() failed\n");
|
||||
return -1;
|
||||
}
|
||||
w9966_set_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV);
|
||||
|
||||
w9966_pdev_claim(cam);
|
||||
|
||||
/* Setup a default capture mode */
|
||||
if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) {
|
||||
DPRINTF("w9966_setup() failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
w9966_pdev_release(cam);
|
||||
|
||||
/* Fill in the video_device struct and register us to v4l */
|
||||
strlcpy(cam->vdev.name, W9966_DRIVERNAME, sizeof(cam->vdev.name));
|
||||
cam->vdev.v4l2_dev = v4l2_dev;
|
||||
cam->vdev.fops = &w9966_fops;
|
||||
cam->vdev.ioctl_ops = &w9966_ioctl_ops;
|
||||
cam->vdev.release = video_device_release_empty;
|
||||
cam->vdev.ctrl_handler = &cam->hdl;
|
||||
video_set_drvdata(&cam->vdev, cam);
|
||||
|
||||
mutex_init(&cam->lock);
|
||||
|
||||
if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
|
||||
return -1;
|
||||
|
||||
w9966_set_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV);
|
||||
|
||||
/* All ok */
|
||||
v4l2_info(v4l2_dev, "Found and initialized a webcam on %s.\n",
|
||||
cam->pport->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Terminate everything gracefully */
|
||||
static void w9966_term(struct w9966 *cam)
|
||||
{
|
||||
/* Unregister from v4l */
|
||||
if (w9966_get_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) {
|
||||
video_unregister_device(&cam->vdev);
|
||||
w9966_set_state(cam, W9966_STATE_VDEV, 0);
|
||||
}
|
||||
|
||||
v4l2_ctrl_handler_free(&cam->hdl);
|
||||
|
||||
/* Terminate from IEEE1284 mode and release pdev block */
|
||||
if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
|
||||
w9966_pdev_claim(cam);
|
||||
parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT);
|
||||
w9966_pdev_release(cam);
|
||||
}
|
||||
|
||||
/* Unregister from parport */
|
||||
if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
|
||||
parport_unregister_device(cam->pdev);
|
||||
w9966_set_state(cam, W9966_STATE_PDEV, 0);
|
||||
}
|
||||
memset(cam, 0, sizeof(*cam));
|
||||
}
|
||||
|
||||
|
||||
/* Called once for every parport on init */
|
||||
static void w9966_attach(struct parport *port)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < W9966_MAXCAMS; i++) {
|
||||
if (w9966_cams[i].dev_state != 0) /* Cam is already assigned */
|
||||
continue;
|
||||
if (strcmp(pardev[i], "aggressive") == 0 || strcmp(pardev[i], port->name) == 0) {
|
||||
if (w9966_init(&w9966_cams[i], port) != 0)
|
||||
w9966_term(&w9966_cams[i]);
|
||||
break; /* return */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Called once for every parport on termination */
|
||||
static void w9966_detach(struct parport *port)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < W9966_MAXCAMS; i++)
|
||||
if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port)
|
||||
w9966_term(&w9966_cams[i]);
|
||||
}
|
||||
|
||||
|
||||
static struct parport_driver w9966_ppd = {
|
||||
.name = W9966_DRIVERNAME,
|
||||
.attach = w9966_attach,
|
||||
.detach = w9966_detach,
|
||||
};
|
||||
|
||||
/* Module entry point */
|
||||
static int __init w9966_mod_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < W9966_MAXCAMS; i++)
|
||||
w9966_cams[i].dev_state = 0;
|
||||
|
||||
return parport_register_driver(&w9966_ppd);
|
||||
}
|
||||
|
||||
/* Module cleanup */
|
||||
static void __exit w9966_mod_term(void)
|
||||
{
|
||||
parport_unregister_driver(&w9966_ppd);
|
||||
}
|
||||
|
||||
module_init(w9966_mod_init);
|
||||
module_exit(w9966_mod_term);
|
||||
|
|
@ -647,6 +647,7 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
|
|||
dev->ts_packet_size = 188 * 4;
|
||||
dev->ts_packet_count = 32;
|
||||
sizes[0] = dev->ts_packet_size * dev->ts_packet_count;
|
||||
alloc_ctxs[0] = dev->alloc_ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -662,14 +663,11 @@ static void buffer_finish(struct vb2_buffer *vb)
|
|||
{
|
||||
struct cx8802_dev *dev = vb->vb2_queue->drv_priv;
|
||||
struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
|
||||
struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
|
||||
struct cx88_riscmem *risc = &buf->risc;
|
||||
|
||||
if (risc->cpu)
|
||||
pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
|
||||
memset(risc, 0, sizeof(*risc));
|
||||
|
||||
dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
static void buffer_queue(struct vb2_buffer *vb)
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
|
|||
dev->ts_packet_size = 188 * 4;
|
||||
dev->ts_packet_count = dvb_buf_tscnt;
|
||||
sizes[0] = dev->ts_packet_size * dev->ts_packet_count;
|
||||
alloc_ctxs[0] = dev->alloc_ctx;
|
||||
*num_buffers = dvb_buf_tscnt;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -108,14 +109,11 @@ static void buffer_finish(struct vb2_buffer *vb)
|
|||
{
|
||||
struct cx8802_dev *dev = vb->vb2_queue->drv_priv;
|
||||
struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
|
||||
struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
|
||||
struct cx88_riscmem *risc = &buf->risc;
|
||||
|
||||
if (risc->cpu)
|
||||
pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
|
||||
memset(risc, 0, sizeof(*risc));
|
||||
|
||||
dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
static void buffer_queue(struct vb2_buffer *vb)
|
||||
|
|
|
|||
|
|
@ -235,10 +235,6 @@ int cx8802_buf_prepare(struct vb2_queue *q, struct cx8802_dev *dev,
|
|||
return -EINVAL;
|
||||
vb2_set_plane_payload(&buf->vb, 0, size);
|
||||
|
||||
rc = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
|
||||
if (!rc)
|
||||
return -EIO;
|
||||
|
||||
rc = cx88_risc_databuffer(dev->pci, risc, sgt->sgl,
|
||||
dev->ts_packet_size, dev->ts_packet_count, 0);
|
||||
if (rc) {
|
||||
|
|
@ -733,6 +729,11 @@ static int cx8802_probe(struct pci_dev *pci_dev,
|
|||
if (NULL == dev)
|
||||
goto fail_core;
|
||||
dev->pci = pci_dev;
|
||||
dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
|
||||
if (IS_ERR(dev->alloc_ctx)) {
|
||||
err = PTR_ERR(dev->alloc_ctx);
|
||||
goto fail_core;
|
||||
}
|
||||
dev->core = core;
|
||||
|
||||
/* Maintain a reference so cx88-video can query the 8802 device. */
|
||||
|
|
@ -752,6 +753,7 @@ static int cx8802_probe(struct pci_dev *pci_dev,
|
|||
return 0;
|
||||
|
||||
fail_free:
|
||||
vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
|
||||
kfree(dev);
|
||||
fail_core:
|
||||
core->dvbdev = NULL;
|
||||
|
|
@ -798,6 +800,7 @@ static void cx8802_remove(struct pci_dev *pci_dev)
|
|||
/* common */
|
||||
cx8802_fini_common(dev);
|
||||
cx88_core_put(dev->core,dev->pci);
|
||||
vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
|
|||
sizes[0] = VBI_LINE_NTSC_COUNT * VBI_LINE_LENGTH * 2;
|
||||
else
|
||||
sizes[0] = VBI_LINE_PAL_COUNT * VBI_LINE_LENGTH * 2;
|
||||
alloc_ctxs[0] = dev->alloc_ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -131,7 +132,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
|
|||
struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
|
||||
unsigned int lines;
|
||||
unsigned int size;
|
||||
int rc;
|
||||
|
||||
if (dev->core->tvnorm & V4L2_STD_525_60)
|
||||
lines = VBI_LINE_NTSC_COUNT;
|
||||
|
|
@ -142,10 +142,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
|
|||
return -EINVAL;
|
||||
vb2_set_plane_payload(vb, 0, size);
|
||||
|
||||
rc = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
|
||||
if (!rc)
|
||||
return -EIO;
|
||||
|
||||
cx88_risc_buffer(dev->pci, &buf->risc, sgt->sgl,
|
||||
0, VBI_LINE_LENGTH * lines,
|
||||
VBI_LINE_LENGTH, 0,
|
||||
|
|
@ -157,14 +153,11 @@ static void buffer_finish(struct vb2_buffer *vb)
|
|||
{
|
||||
struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
|
||||
struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
|
||||
struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
|
||||
struct cx88_riscmem *risc = &buf->risc;
|
||||
|
||||
if (risc->cpu)
|
||||
pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
|
||||
memset(risc, 0, sizeof(*risc));
|
||||
|
||||
dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
static void buffer_queue(struct vb2_buffer *vb)
|
||||
|
|
|
|||
|
|
@ -440,6 +440,7 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
|
|||
|
||||
*num_planes = 1;
|
||||
sizes[0] = (dev->fmt->depth * core->width * core->height) >> 3;
|
||||
alloc_ctxs[0] = dev->alloc_ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -449,7 +450,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
|
|||
struct cx88_core *core = dev->core;
|
||||
struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
|
||||
struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
|
||||
int rc;
|
||||
|
||||
buf->bpl = core->width * dev->fmt->depth >> 3;
|
||||
|
||||
|
|
@ -457,10 +457,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
|
|||
return -EINVAL;
|
||||
vb2_set_plane_payload(vb, 0, core->height * buf->bpl);
|
||||
|
||||
rc = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
|
||||
if (!rc)
|
||||
return -EIO;
|
||||
|
||||
switch (core->field) {
|
||||
case V4L2_FIELD_TOP:
|
||||
cx88_risc_buffer(dev->pci, &buf->risc,
|
||||
|
|
@ -505,14 +501,11 @@ static void buffer_finish(struct vb2_buffer *vb)
|
|||
{
|
||||
struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
|
||||
struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
|
||||
struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
|
||||
struct cx88_riscmem *risc = &buf->risc;
|
||||
|
||||
if (risc->cpu)
|
||||
pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
|
||||
memset(risc, 0, sizeof(*risc));
|
||||
|
||||
dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
static void buffer_queue(struct vb2_buffer *vb)
|
||||
|
|
@ -530,7 +523,6 @@ static void buffer_queue(struct vb2_buffer *vb)
|
|||
|
||||
if (list_empty(&q->active)) {
|
||||
list_add_tail(&buf->list, &q->active);
|
||||
start_video_dma(dev, q, buf);
|
||||
buf->count = q->count++;
|
||||
dprintk(2,"[%p/%d] buffer_queue - first active\n",
|
||||
buf, buf->vb.v4l2_buf.index);
|
||||
|
|
@ -1345,6 +1337,12 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
|
|||
err = -EIO;
|
||||
goto fail_core;
|
||||
}
|
||||
dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
|
||||
if (IS_ERR(dev->alloc_ctx)) {
|
||||
err = PTR_ERR(dev->alloc_ctx);
|
||||
goto fail_core;
|
||||
}
|
||||
|
||||
|
||||
/* initialize driver struct */
|
||||
spin_lock_init(&dev->slock);
|
||||
|
|
@ -1549,6 +1547,7 @@ fail_unreg:
|
|||
free_irq(pci_dev->irq, dev);
|
||||
mutex_unlock(&core->lock);
|
||||
fail_core:
|
||||
vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
|
||||
core->v4ldev = NULL;
|
||||
cx88_core_put(core,dev->pci);
|
||||
fail_free:
|
||||
|
|
@ -1582,6 +1581,7 @@ static void cx8800_finidev(struct pci_dev *pci_dev)
|
|||
|
||||
/* free memory */
|
||||
cx88_core_put(core,dev->pci);
|
||||
vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -485,6 +485,7 @@ struct cx8800_dev {
|
|||
/* pci i/o */
|
||||
struct pci_dev *pci;
|
||||
unsigned char pci_rev,pci_lat;
|
||||
void *alloc_ctx;
|
||||
|
||||
const struct cx8800_fmt *fmt;
|
||||
|
||||
|
|
@ -548,6 +549,7 @@ struct cx8802_dev {
|
|||
/* pci i/o */
|
||||
struct pci_dev *pci;
|
||||
unsigned char pci_rev,pci_lat;
|
||||
void *alloc_ctx;
|
||||
|
||||
/* dma queues */
|
||||
struct cx88_dmaqueue mpegq;
|
||||
|
|
|
|||
|
|
@ -65,14 +65,6 @@ config VIDEO_TIMBERDALE
|
|||
---help---
|
||||
Add support for the Video In peripherial of the timberdale FPGA.
|
||||
|
||||
config VIDEO_VINO
|
||||
tristate "SGI Vino Video For Linux"
|
||||
depends on I2C && SGI_IP22 && VIDEO_V4L2
|
||||
select VIDEO_SAA7191 if MEDIA_SUBDRV_AUTOSELECT
|
||||
help
|
||||
Say Y here to build in support for the Vino video input system found
|
||||
on SGI Indy machines.
|
||||
|
||||
config VIDEO_M32R_AR
|
||||
tristate "AR devices"
|
||||
depends on VIDEO_V4L2
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@
|
|||
# Makefile for the video capture/playback device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_VIDEO_VINO) += indycam.o
|
||||
obj-$(CONFIG_VIDEO_VINO) += vino.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o
|
||||
obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
|
||||
|
||||
|
|
|
|||
|
|
@ -1,378 +0,0 @@
|
|||
/*
|
||||
* indycam.c - Silicon Graphics IndyCam digital camera driver
|
||||
*
|
||||
* Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
|
||||
* Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* IndyCam decodes stream of photons into digital image representation ;-) */
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <media/v4l2-device.h>
|
||||
|
||||
#include "indycam.h"
|
||||
|
||||
#define INDYCAM_MODULE_VERSION "0.0.5"
|
||||
|
||||
MODULE_DESCRIPTION("SGI IndyCam driver");
|
||||
MODULE_VERSION(INDYCAM_MODULE_VERSION);
|
||||
MODULE_AUTHOR("Mikael Nousiainen <[email protected]>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
// #define INDYCAM_DEBUG
|
||||
|
||||
#ifdef INDYCAM_DEBUG
|
||||
#define dprintk(x...) printk("IndyCam: " x);
|
||||
#define indycam_regdump(client) indycam_regdump_debug(client)
|
||||
#else
|
||||
#define dprintk(x...)
|
||||
#define indycam_regdump(client)
|
||||
#endif
|
||||
|
||||
struct indycam {
|
||||
struct v4l2_subdev sd;
|
||||
u8 version;
|
||||
};
|
||||
|
||||
static inline struct indycam *to_indycam(struct v4l2_subdev *sd)
|
||||
{
|
||||
return container_of(sd, struct indycam, sd);
|
||||
}
|
||||
|
||||
static const u8 initseq[] = {
|
||||
INDYCAM_CONTROL_AGCENA, /* INDYCAM_CONTROL */
|
||||
INDYCAM_SHUTTER_60, /* INDYCAM_SHUTTER */
|
||||
INDYCAM_GAIN_DEFAULT, /* INDYCAM_GAIN */
|
||||
0x00, /* INDYCAM_BRIGHTNESS (read-only) */
|
||||
INDYCAM_RED_BALANCE_DEFAULT, /* INDYCAM_RED_BALANCE */
|
||||
INDYCAM_BLUE_BALANCE_DEFAULT, /* INDYCAM_BLUE_BALANCE */
|
||||
INDYCAM_RED_SATURATION_DEFAULT, /* INDYCAM_RED_SATURATION */
|
||||
INDYCAM_BLUE_SATURATION_DEFAULT,/* INDYCAM_BLUE_SATURATION */
|
||||
};
|
||||
|
||||
/* IndyCam register handling */
|
||||
|
||||
static int indycam_read_reg(struct v4l2_subdev *sd, u8 reg, u8 *value)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
int ret;
|
||||
|
||||
if (reg == INDYCAM_REG_RESET) {
|
||||
dprintk("indycam_read_reg(): "
|
||||
"skipping write-only register %d\n", reg);
|
||||
*value = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, reg);
|
||||
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "IndyCam: indycam_read_reg(): read failed, "
|
||||
"register = 0x%02x\n", reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*value = (u8)ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int indycam_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
int err;
|
||||
|
||||
if (reg == INDYCAM_REG_BRIGHTNESS || reg == INDYCAM_REG_VERSION) {
|
||||
dprintk("indycam_write_reg(): "
|
||||
"skipping read-only register %d\n", reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dprintk("Writing Reg %d = 0x%02x\n", reg, value);
|
||||
err = i2c_smbus_write_byte_data(client, reg, value);
|
||||
|
||||
if (err) {
|
||||
printk(KERN_ERR "IndyCam: indycam_write_reg(): write failed, "
|
||||
"register = 0x%02x, value = 0x%02x\n", reg, value);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int indycam_write_block(struct v4l2_subdev *sd, u8 reg,
|
||||
u8 length, u8 *data)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
err = indycam_write_reg(sd, reg + i, data[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper functions */
|
||||
|
||||
#ifdef INDYCAM_DEBUG
|
||||
static void indycam_regdump_debug(struct v4l2_subdev *sd)
|
||||
{
|
||||
int i;
|
||||
u8 val;
|
||||
|
||||
for (i = 0; i < 9; i++) {
|
||||
indycam_read_reg(sd, i, &val);
|
||||
dprintk("Reg %d = 0x%02x\n", i, val);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int indycam_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
{
|
||||
struct indycam *camera = to_indycam(sd);
|
||||
u8 reg;
|
||||
int ret = 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
case V4L2_CID_AUTO_WHITE_BALANCE:
|
||||
ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®);
|
||||
if (ret)
|
||||
return -EIO;
|
||||
if (ctrl->id == V4L2_CID_AUTOGAIN)
|
||||
ctrl->value = (reg & INDYCAM_CONTROL_AGCENA)
|
||||
? 1 : 0;
|
||||
else
|
||||
ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL)
|
||||
? 1 : 0;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
ret = indycam_read_reg(sd, INDYCAM_REG_SHUTTER, ®);
|
||||
if (ret)
|
||||
return -EIO;
|
||||
ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1);
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
ret = indycam_read_reg(sd, INDYCAM_REG_GAIN, ®);
|
||||
if (ret)
|
||||
return -EIO;
|
||||
ctrl->value = (s32)reg;
|
||||
break;
|
||||
case V4L2_CID_RED_BALANCE:
|
||||
ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, ®);
|
||||
if (ret)
|
||||
return -EIO;
|
||||
ctrl->value = (s32)reg;
|
||||
break;
|
||||
case V4L2_CID_BLUE_BALANCE:
|
||||
ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, ®);
|
||||
if (ret)
|
||||
return -EIO;
|
||||
ctrl->value = (s32)reg;
|
||||
break;
|
||||
case INDYCAM_CONTROL_RED_SATURATION:
|
||||
ret = indycam_read_reg(sd,
|
||||
INDYCAM_REG_RED_SATURATION, ®);
|
||||
if (ret)
|
||||
return -EIO;
|
||||
ctrl->value = (s32)reg;
|
||||
break;
|
||||
case INDYCAM_CONTROL_BLUE_SATURATION:
|
||||
ret = indycam_read_reg(sd,
|
||||
INDYCAM_REG_BLUE_SATURATION, ®);
|
||||
if (ret)
|
||||
return -EIO;
|
||||
ctrl->value = (s32)reg;
|
||||
break;
|
||||
case V4L2_CID_GAMMA:
|
||||
if (camera->version == CAMERA_VERSION_MOOSE) {
|
||||
ret = indycam_read_reg(sd,
|
||||
INDYCAM_REG_GAMMA, ®);
|
||||
if (ret)
|
||||
return -EIO;
|
||||
ctrl->value = (s32)reg;
|
||||
} else {
|
||||
ctrl->value = INDYCAM_GAMMA_DEFAULT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
{
|
||||
struct indycam *camera = to_indycam(sd);
|
||||
u8 reg;
|
||||
int ret = 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
case V4L2_CID_AUTO_WHITE_BALANCE:
|
||||
ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (ctrl->id == V4L2_CID_AUTOGAIN) {
|
||||
if (ctrl->value)
|
||||
reg |= INDYCAM_CONTROL_AGCENA;
|
||||
else
|
||||
reg &= ~INDYCAM_CONTROL_AGCENA;
|
||||
} else {
|
||||
if (ctrl->value)
|
||||
reg |= INDYCAM_CONTROL_AWBCTL;
|
||||
else
|
||||
reg &= ~INDYCAM_CONTROL_AWBCTL;
|
||||
}
|
||||
|
||||
ret = indycam_write_reg(sd, INDYCAM_REG_CONTROL, reg);
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1);
|
||||
ret = indycam_write_reg(sd, INDYCAM_REG_SHUTTER, reg);
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
ret = indycam_write_reg(sd, INDYCAM_REG_GAIN, ctrl->value);
|
||||
break;
|
||||
case V4L2_CID_RED_BALANCE:
|
||||
ret = indycam_write_reg(sd, INDYCAM_REG_RED_BALANCE,
|
||||
ctrl->value);
|
||||
break;
|
||||
case V4L2_CID_BLUE_BALANCE:
|
||||
ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_BALANCE,
|
||||
ctrl->value);
|
||||
break;
|
||||
case INDYCAM_CONTROL_RED_SATURATION:
|
||||
ret = indycam_write_reg(sd, INDYCAM_REG_RED_SATURATION,
|
||||
ctrl->value);
|
||||
break;
|
||||
case INDYCAM_CONTROL_BLUE_SATURATION:
|
||||
ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_SATURATION,
|
||||
ctrl->value);
|
||||
break;
|
||||
case V4L2_CID_GAMMA:
|
||||
if (camera->version == CAMERA_VERSION_MOOSE) {
|
||||
ret = indycam_write_reg(sd, INDYCAM_REG_GAMMA,
|
||||
ctrl->value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* I2C-interface */
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static const struct v4l2_subdev_core_ops indycam_core_ops = {
|
||||
.g_ctrl = indycam_g_ctrl,
|
||||
.s_ctrl = indycam_s_ctrl,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops indycam_ops = {
|
||||
.core = &indycam_core_ops,
|
||||
};
|
||||
|
||||
static int indycam_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int err = 0;
|
||||
struct indycam *camera;
|
||||
struct v4l2_subdev *sd;
|
||||
|
||||
v4l_info(client, "chip found @ 0x%x (%s)\n",
|
||||
client->addr << 1, client->adapter->name);
|
||||
|
||||
camera = kzalloc(sizeof(struct indycam), GFP_KERNEL);
|
||||
if (!camera)
|
||||
return -ENOMEM;
|
||||
|
||||
sd = &camera->sd;
|
||||
v4l2_i2c_subdev_init(sd, client, &indycam_ops);
|
||||
|
||||
camera->version = i2c_smbus_read_byte_data(client,
|
||||
INDYCAM_REG_VERSION);
|
||||
if (camera->version != CAMERA_VERSION_INDY &&
|
||||
camera->version != CAMERA_VERSION_MOOSE) {
|
||||
kfree(camera);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "IndyCam v%d.%d detected\n",
|
||||
INDYCAM_VERSION_MAJOR(camera->version),
|
||||
INDYCAM_VERSION_MINOR(camera->version));
|
||||
|
||||
indycam_regdump(sd);
|
||||
|
||||
// initialize
|
||||
err = indycam_write_block(sd, 0, sizeof(initseq), (u8 *)&initseq);
|
||||
if (err) {
|
||||
printk(KERN_ERR "IndyCam initialization failed\n");
|
||||
kfree(camera);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
indycam_regdump(sd);
|
||||
|
||||
// white balance
|
||||
err = indycam_write_reg(sd, INDYCAM_REG_CONTROL,
|
||||
INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL);
|
||||
if (err) {
|
||||
printk(KERN_ERR "IndyCam: White balancing camera failed\n");
|
||||
kfree(camera);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
indycam_regdump(sd);
|
||||
|
||||
printk(KERN_INFO "IndyCam initialized\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int indycam_remove(struct i2c_client *client)
|
||||
{
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
|
||||
v4l2_device_unregister_subdev(sd);
|
||||
kfree(to_indycam(sd));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id indycam_id[] = {
|
||||
{ "indycam", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, indycam_id);
|
||||
|
||||
static struct i2c_driver indycam_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "indycam",
|
||||
},
|
||||
.probe = indycam_probe,
|
||||
.remove = indycam_remove,
|
||||
.id_table = indycam_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(indycam_driver);
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* indycam.h - Silicon Graphics IndyCam digital camera driver
|
||||
*
|
||||
* Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
|
||||
* Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _INDYCAM_H_
|
||||
#define _INDYCAM_H_
|
||||
|
||||
/* I2C address for the Guinness Camera */
|
||||
#define INDYCAM_ADDR 0x56
|
||||
|
||||
/* Camera version */
|
||||
#define CAMERA_VERSION_INDY 0x10 /* v1.0 */
|
||||
#define CAMERA_VERSION_MOOSE 0x12 /* v1.2 */
|
||||
#define INDYCAM_VERSION_MAJOR(x) (((x) & 0xf0) >> 4)
|
||||
#define INDYCAM_VERSION_MINOR(x) ((x) & 0x0f)
|
||||
|
||||
/* Register bus addresses */
|
||||
#define INDYCAM_REG_CONTROL 0x00
|
||||
#define INDYCAM_REG_SHUTTER 0x01
|
||||
#define INDYCAM_REG_GAIN 0x02
|
||||
#define INDYCAM_REG_BRIGHTNESS 0x03 /* read-only */
|
||||
#define INDYCAM_REG_RED_BALANCE 0x04
|
||||
#define INDYCAM_REG_BLUE_BALANCE 0x05
|
||||
#define INDYCAM_REG_RED_SATURATION 0x06
|
||||
#define INDYCAM_REG_BLUE_SATURATION 0x07
|
||||
#define INDYCAM_REG_GAMMA 0x08
|
||||
#define INDYCAM_REG_VERSION 0x0e /* read-only */
|
||||
#define INDYCAM_REG_RESET 0x0f /* write-only */
|
||||
|
||||
#define INDYCAM_REG_LED 0x46
|
||||
#define INDYCAM_REG_ORIENTATION 0x47
|
||||
#define INDYCAM_REG_BUTTON 0x48
|
||||
|
||||
/* Field definitions of registers */
|
||||
#define INDYCAM_CONTROL_AGCENA (1<<0) /* automatic gain control */
|
||||
#define INDYCAM_CONTROL_AWBCTL (1<<1) /* automatic white balance */
|
||||
/* 2-3 are reserved */
|
||||
#define INDYCAM_CONTROL_EVNFLD (1<<4) /* read-only */
|
||||
|
||||
#define INDYCAM_SHUTTER_10000 0x02 /* 1/10000 second */
|
||||
#define INDYCAM_SHUTTER_4000 0x04 /* 1/4000 second */
|
||||
#define INDYCAM_SHUTTER_2000 0x08 /* 1/2000 second */
|
||||
#define INDYCAM_SHUTTER_1000 0x10 /* 1/1000 second */
|
||||
#define INDYCAM_SHUTTER_500 0x20 /* 1/500 second */
|
||||
#define INDYCAM_SHUTTER_250 0x3f /* 1/250 second */
|
||||
#define INDYCAM_SHUTTER_125 0x7e /* 1/125 second */
|
||||
#define INDYCAM_SHUTTER_100 0x9e /* 1/100 second */
|
||||
#define INDYCAM_SHUTTER_60 0x00 /* 1/60 second */
|
||||
|
||||
#define INDYCAM_LED_ACTIVE 0x10
|
||||
#define INDYCAM_LED_INACTIVE 0x30
|
||||
#define INDYCAM_ORIENTATION_BOTTOM_TO_TOP 0x40
|
||||
#define INDYCAM_BUTTON_RELEASED 0x10
|
||||
|
||||
/* Values for controls */
|
||||
#define INDYCAM_SHUTTER_MIN 0x00
|
||||
#define INDYCAM_SHUTTER_MAX 0xff
|
||||
#define INDYCAM_GAIN_MIN 0x00
|
||||
#define INDYCAM_GAIN_MAX 0xff
|
||||
#define INDYCAM_RED_BALANCE_MIN 0x00
|
||||
#define INDYCAM_RED_BALANCE_MAX 0xff
|
||||
#define INDYCAM_BLUE_BALANCE_MIN 0x00
|
||||
#define INDYCAM_BLUE_BALANCE_MAX 0xff
|
||||
#define INDYCAM_RED_SATURATION_MIN 0x00
|
||||
#define INDYCAM_RED_SATURATION_MAX 0xff
|
||||
#define INDYCAM_BLUE_SATURATION_MIN 0x00
|
||||
#define INDYCAM_BLUE_SATURATION_MAX 0xff
|
||||
#define INDYCAM_GAMMA_MIN 0x00
|
||||
#define INDYCAM_GAMMA_MAX 0xff
|
||||
|
||||
#define INDYCAM_AGC_DEFAULT 1
|
||||
#define INDYCAM_AWB_DEFAULT 0
|
||||
#define INDYCAM_SHUTTER_DEFAULT 0xff
|
||||
#define INDYCAM_GAIN_DEFAULT 0x80
|
||||
#define INDYCAM_RED_BALANCE_DEFAULT 0x18
|
||||
#define INDYCAM_BLUE_BALANCE_DEFAULT 0xa4
|
||||
#define INDYCAM_RED_SATURATION_DEFAULT 0x80
|
||||
#define INDYCAM_BLUE_SATURATION_DEFAULT 0xc0
|
||||
#define INDYCAM_GAMMA_DEFAULT 0x80
|
||||
|
||||
/* Driver interface definitions */
|
||||
|
||||
#define INDYCAM_CONTROL_RED_SATURATION (V4L2_CID_PRIVATE_BASE + 0)
|
||||
#define INDYCAM_CONTROL_BLUE_SATURATION (V4L2_CID_PRIVATE_BASE + 1)
|
||||
|
||||
#endif
|
||||
|
|
@ -64,6 +64,30 @@
|
|||
#define VNDMR_REG 0x58 /* Video n Data Mode Register */
|
||||
#define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */
|
||||
#define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */
|
||||
#define VNC1A_REG 0x80 /* Video n Coefficient Set C1A Register */
|
||||
#define VNC1B_REG 0x84 /* Video n Coefficient Set C1B Register */
|
||||
#define VNC1C_REG 0x88 /* Video n Coefficient Set C1C Register */
|
||||
#define VNC2A_REG 0x90 /* Video n Coefficient Set C2A Register */
|
||||
#define VNC2B_REG 0x94 /* Video n Coefficient Set C2B Register */
|
||||
#define VNC2C_REG 0x98 /* Video n Coefficient Set C2C Register */
|
||||
#define VNC3A_REG 0xA0 /* Video n Coefficient Set C3A Register */
|
||||
#define VNC3B_REG 0xA4 /* Video n Coefficient Set C3B Register */
|
||||
#define VNC3C_REG 0xA8 /* Video n Coefficient Set C3C Register */
|
||||
#define VNC4A_REG 0xB0 /* Video n Coefficient Set C4A Register */
|
||||
#define VNC4B_REG 0xB4 /* Video n Coefficient Set C4B Register */
|
||||
#define VNC4C_REG 0xB8 /* Video n Coefficient Set C4C Register */
|
||||
#define VNC5A_REG 0xC0 /* Video n Coefficient Set C5A Register */
|
||||
#define VNC5B_REG 0xC4 /* Video n Coefficient Set C5B Register */
|
||||
#define VNC5C_REG 0xC8 /* Video n Coefficient Set C5C Register */
|
||||
#define VNC6A_REG 0xD0 /* Video n Coefficient Set C6A Register */
|
||||
#define VNC6B_REG 0xD4 /* Video n Coefficient Set C6B Register */
|
||||
#define VNC6C_REG 0xD8 /* Video n Coefficient Set C6C Register */
|
||||
#define VNC7A_REG 0xE0 /* Video n Coefficient Set C7A Register */
|
||||
#define VNC7B_REG 0xE4 /* Video n Coefficient Set C7B Register */
|
||||
#define VNC7C_REG 0xE8 /* Video n Coefficient Set C7C Register */
|
||||
#define VNC8A_REG 0xF0 /* Video n Coefficient Set C8A Register */
|
||||
#define VNC8B_REG 0xF4 /* Video n Coefficient Set C8B Register */
|
||||
#define VNC8C_REG 0xF8 /* Video n Coefficient Set C8C Register */
|
||||
|
||||
/* Register bit fields for R-Car VIN */
|
||||
/* Video n Main Control Register bits */
|
||||
|
|
@ -106,6 +130,7 @@
|
|||
#define VNDMR2_VPS (1 << 30)
|
||||
#define VNDMR2_HPS (1 << 29)
|
||||
#define VNDMR2_FTEV (1 << 17)
|
||||
#define VNDMR2_VLV(n) ((n & 0xf) << 12)
|
||||
|
||||
#define VIN_MAX_WIDTH 2048
|
||||
#define VIN_MAX_HEIGHT 2048
|
||||
|
|
@ -117,6 +142,324 @@ enum chip_id {
|
|||
RCAR_E1,
|
||||
};
|
||||
|
||||
struct vin_coeff {
|
||||
unsigned short xs_value;
|
||||
u32 coeff_set[24];
|
||||
};
|
||||
|
||||
static const struct vin_coeff vin_coeff_set[] = {
|
||||
{ 0x0000, {
|
||||
0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000 },
|
||||
},
|
||||
{ 0x1000, {
|
||||
0x000fa400, 0x000fa400, 0x09625902,
|
||||
0x000003f8, 0x00000403, 0x3de0d9f0,
|
||||
0x001fffed, 0x00000804, 0x3cc1f9c3,
|
||||
0x001003de, 0x00000c01, 0x3cb34d7f,
|
||||
0x002003d2, 0x00000c00, 0x3d24a92d,
|
||||
0x00200bca, 0x00000bff, 0x3df600d2,
|
||||
0x002013cc, 0x000007ff, 0x3ed70c7e,
|
||||
0x00100fde, 0x00000000, 0x3f87c036 },
|
||||
},
|
||||
{ 0x1200, {
|
||||
0x002ffff1, 0x002ffff1, 0x02a0a9c8,
|
||||
0x002003e7, 0x001ffffa, 0x000185bc,
|
||||
0x002007dc, 0x000003ff, 0x3e52859c,
|
||||
0x00200bd4, 0x00000002, 0x3d53996b,
|
||||
0x00100fd0, 0x00000403, 0x3d04ad2d,
|
||||
0x00000bd5, 0x00000403, 0x3d35ace7,
|
||||
0x3ff003e4, 0x00000801, 0x3dc674a1,
|
||||
0x3fffe800, 0x00000800, 0x3e76f461 },
|
||||
},
|
||||
{ 0x1400, {
|
||||
0x00100be3, 0x00100be3, 0x04d1359a,
|
||||
0x00000fdb, 0x002003ed, 0x0211fd93,
|
||||
0x00000fd6, 0x002003f4, 0x0002d97b,
|
||||
0x000007d6, 0x002ffffb, 0x3e93b956,
|
||||
0x3ff003da, 0x001003ff, 0x3db49926,
|
||||
0x3fffefe9, 0x00100001, 0x3d655cee,
|
||||
0x3fffd400, 0x00000003, 0x3d65f4b6,
|
||||
0x000fb421, 0x00000402, 0x3dc6547e },
|
||||
},
|
||||
{ 0x1600, {
|
||||
0x00000bdd, 0x00000bdd, 0x06519578,
|
||||
0x3ff007da, 0x00000be3, 0x03c24973,
|
||||
0x3ff003d9, 0x00000be9, 0x01b30d5f,
|
||||
0x3ffff7df, 0x001003f1, 0x0003c542,
|
||||
0x000fdfec, 0x001003f7, 0x3ec4711d,
|
||||
0x000fc400, 0x002ffffd, 0x3df504f1,
|
||||
0x001fa81a, 0x002ffc00, 0x3d957cc2,
|
||||
0x002f8c3c, 0x00100000, 0x3db5c891 },
|
||||
},
|
||||
{ 0x1800, {
|
||||
0x3ff003dc, 0x3ff003dc, 0x0791e558,
|
||||
0x000ff7dd, 0x3ff007de, 0x05328554,
|
||||
0x000fe7e3, 0x3ff00be2, 0x03232546,
|
||||
0x000fd7ee, 0x000007e9, 0x0143bd30,
|
||||
0x001fb800, 0x000007ee, 0x00044511,
|
||||
0x002fa015, 0x000007f4, 0x3ef4bcee,
|
||||
0x002f8832, 0x001003f9, 0x3e4514c7,
|
||||
0x001f7853, 0x001003fd, 0x3de54c9f },
|
||||
},
|
||||
{ 0x1a00, {
|
||||
0x000fefe0, 0x000fefe0, 0x08721d3c,
|
||||
0x001fdbe7, 0x000ffbde, 0x0652a139,
|
||||
0x001fcbf0, 0x000003df, 0x0463292e,
|
||||
0x002fb3ff, 0x3ff007e3, 0x0293a91d,
|
||||
0x002f9c12, 0x3ff00be7, 0x01241905,
|
||||
0x001f8c29, 0x000007ed, 0x3fe470eb,
|
||||
0x000f7c46, 0x000007f2, 0x3f04b8ca,
|
||||
0x3fef7865, 0x000007f6, 0x3e74e4a8 },
|
||||
},
|
||||
{ 0x1c00, {
|
||||
0x001fd3e9, 0x001fd3e9, 0x08f23d26,
|
||||
0x002fbff3, 0x001fe3e4, 0x0712ad23,
|
||||
0x002fa800, 0x000ff3e0, 0x05631d1b,
|
||||
0x001f9810, 0x000ffbe1, 0x03b3890d,
|
||||
0x000f8c23, 0x000003e3, 0x0233e8fa,
|
||||
0x3fef843b, 0x000003e7, 0x00f430e4,
|
||||
0x3fbf8456, 0x3ff00bea, 0x00046cc8,
|
||||
0x3f8f8c72, 0x3ff00bef, 0x3f3490ac },
|
||||
},
|
||||
{ 0x1e00, {
|
||||
0x001fbbf4, 0x001fbbf4, 0x09425112,
|
||||
0x001fa800, 0x002fc7ed, 0x0792b110,
|
||||
0x000f980e, 0x001fdbe6, 0x0613110a,
|
||||
0x3fff8c20, 0x001fe7e3, 0x04a368fd,
|
||||
0x3fcf8c33, 0x000ff7e2, 0x0343b8ed,
|
||||
0x3f9f8c4a, 0x000fffe3, 0x0203f8da,
|
||||
0x3f5f9c61, 0x000003e6, 0x00e428c5,
|
||||
0x3f1fb07b, 0x000003eb, 0x3fe440af },
|
||||
},
|
||||
{ 0x2000, {
|
||||
0x000fa400, 0x000fa400, 0x09625902,
|
||||
0x3fff980c, 0x001fb7f5, 0x0812b0ff,
|
||||
0x3fdf901c, 0x001fc7ed, 0x06b2fcfa,
|
||||
0x3faf902d, 0x001fd3e8, 0x055348f1,
|
||||
0x3f7f983f, 0x001fe3e5, 0x04038ce3,
|
||||
0x3f3fa454, 0x001fefe3, 0x02e3c8d1,
|
||||
0x3f0fb86a, 0x001ff7e4, 0x01c3e8c0,
|
||||
0x3ecfd880, 0x000fffe6, 0x00c404ac },
|
||||
},
|
||||
{ 0x2200, {
|
||||
0x3fdf9c0b, 0x3fdf9c0b, 0x09725cf4,
|
||||
0x3fbf9818, 0x3fffa400, 0x0842a8f1,
|
||||
0x3f8f9827, 0x000fb3f7, 0x0702f0ec,
|
||||
0x3f5fa037, 0x000fc3ef, 0x05d330e4,
|
||||
0x3f2fac49, 0x001fcfea, 0x04a364d9,
|
||||
0x3effc05c, 0x001fdbe7, 0x038394ca,
|
||||
0x3ecfdc6f, 0x001fe7e6, 0x0273b0bb,
|
||||
0x3ea00083, 0x001fefe6, 0x0183c0a9 },
|
||||
},
|
||||
{ 0x2400, {
|
||||
0x3f9fa014, 0x3f9fa014, 0x098260e6,
|
||||
0x3f7f9c23, 0x3fcf9c0a, 0x08629ce5,
|
||||
0x3f4fa431, 0x3fefa400, 0x0742d8e1,
|
||||
0x3f1fb440, 0x3fffb3f8, 0x062310d9,
|
||||
0x3eefc850, 0x000fbbf2, 0x050340d0,
|
||||
0x3ecfe062, 0x000fcbec, 0x041364c2,
|
||||
0x3ea00073, 0x001fd3ea, 0x03037cb5,
|
||||
0x3e902086, 0x001fdfe8, 0x022388a5 },
|
||||
},
|
||||
{ 0x2600, {
|
||||
0x3f5fa81e, 0x3f5fa81e, 0x096258da,
|
||||
0x3f3fac2b, 0x3f8fa412, 0x088290d8,
|
||||
0x3f0fbc38, 0x3fafa408, 0x0772c8d5,
|
||||
0x3eefcc47, 0x3fcfa800, 0x0672f4ce,
|
||||
0x3ecfe456, 0x3fefaffa, 0x05531cc6,
|
||||
0x3eb00066, 0x3fffbbf3, 0x047334bb,
|
||||
0x3ea01c77, 0x000fc7ee, 0x039348ae,
|
||||
0x3ea04486, 0x000fd3eb, 0x02b350a1 },
|
||||
},
|
||||
{ 0x2800, {
|
||||
0x3f2fb426, 0x3f2fb426, 0x094250ce,
|
||||
0x3f0fc032, 0x3f4fac1b, 0x086284cd,
|
||||
0x3eefd040, 0x3f7fa811, 0x0782acc9,
|
||||
0x3ecfe84c, 0x3f9fa807, 0x06a2d8c4,
|
||||
0x3eb0005b, 0x3fbfac00, 0x05b2f4bc,
|
||||
0x3eb0186a, 0x3fdfb3fa, 0x04c308b4,
|
||||
0x3eb04077, 0x3fefbbf4, 0x03f31ca8,
|
||||
0x3ec06884, 0x000fbff2, 0x03031c9e },
|
||||
},
|
||||
{ 0x2a00, {
|
||||
0x3f0fc42d, 0x3f0fc42d, 0x090240c4,
|
||||
0x3eefd439, 0x3f2fb822, 0x08526cc2,
|
||||
0x3edfe845, 0x3f4fb018, 0x078294bf,
|
||||
0x3ec00051, 0x3f6fac0f, 0x06b2b4bb,
|
||||
0x3ec0185f, 0x3f8fac07, 0x05e2ccb4,
|
||||
0x3ec0386b, 0x3fafac00, 0x0502e8ac,
|
||||
0x3ed05c77, 0x3fcfb3fb, 0x0432f0a3,
|
||||
0x3ef08482, 0x3fdfbbf6, 0x0372f898 },
|
||||
},
|
||||
{ 0x2c00, {
|
||||
0x3eefdc31, 0x3eefdc31, 0x08e238b8,
|
||||
0x3edfec3d, 0x3f0fc828, 0x082258b9,
|
||||
0x3ed00049, 0x3f1fc01e, 0x077278b6,
|
||||
0x3ed01455, 0x3f3fb815, 0x06c294b2,
|
||||
0x3ed03460, 0x3f5fb40d, 0x0602acac,
|
||||
0x3ef0506c, 0x3f7fb006, 0x0542c0a4,
|
||||
0x3f107476, 0x3f9fb400, 0x0472c89d,
|
||||
0x3f309c80, 0x3fbfb7fc, 0x03b2cc94 },
|
||||
},
|
||||
{ 0x2e00, {
|
||||
0x3eefec37, 0x3eefec37, 0x088220b0,
|
||||
0x3ee00041, 0x3effdc2d, 0x07f244ae,
|
||||
0x3ee0144c, 0x3f0fd023, 0x07625cad,
|
||||
0x3ef02c57, 0x3f1fc81a, 0x06c274a9,
|
||||
0x3f004861, 0x3f3fbc13, 0x060288a6,
|
||||
0x3f20686b, 0x3f5fb80c, 0x05529c9e,
|
||||
0x3f408c74, 0x3f6fb805, 0x04b2ac96,
|
||||
0x3f80ac7e, 0x3f8fb800, 0x0402ac8e },
|
||||
},
|
||||
{ 0x3000, {
|
||||
0x3ef0003a, 0x3ef0003a, 0x084210a6,
|
||||
0x3ef01045, 0x3effec32, 0x07b228a7,
|
||||
0x3f00284e, 0x3f0fdc29, 0x073244a4,
|
||||
0x3f104058, 0x3f0fd420, 0x06a258a2,
|
||||
0x3f305c62, 0x3f2fc818, 0x0612689d,
|
||||
0x3f508069, 0x3f3fc011, 0x05728496,
|
||||
0x3f80a072, 0x3f4fc00a, 0x04d28c90,
|
||||
0x3fc0c07b, 0x3f6fbc04, 0x04429088 },
|
||||
},
|
||||
{ 0x3200, {
|
||||
0x3f00103e, 0x3f00103e, 0x07f1fc9e,
|
||||
0x3f102447, 0x3f000035, 0x0782149d,
|
||||
0x3f203c4f, 0x3f0ff02c, 0x07122c9c,
|
||||
0x3f405458, 0x3f0fe424, 0x06924099,
|
||||
0x3f607061, 0x3f1fd41d, 0x06024c97,
|
||||
0x3f909068, 0x3f2fcc16, 0x05726490,
|
||||
0x3fc0b070, 0x3f3fc80f, 0x04f26c8a,
|
||||
0x0000d077, 0x3f4fc409, 0x04627484 },
|
||||
},
|
||||
{ 0x3400, {
|
||||
0x3f202040, 0x3f202040, 0x07a1e898,
|
||||
0x3f303449, 0x3f100c38, 0x0741fc98,
|
||||
0x3f504c50, 0x3f10002f, 0x06e21495,
|
||||
0x3f706459, 0x3f1ff028, 0x06722492,
|
||||
0x3fa08060, 0x3f1fe421, 0x05f2348f,
|
||||
0x3fd09c67, 0x3f1fdc19, 0x05824c89,
|
||||
0x0000bc6e, 0x3f2fd014, 0x04f25086,
|
||||
0x0040dc74, 0x3f3fcc0d, 0x04825c7f },
|
||||
},
|
||||
{ 0x3600, {
|
||||
0x3f403042, 0x3f403042, 0x0761d890,
|
||||
0x3f504848, 0x3f301c3b, 0x0701f090,
|
||||
0x3f805c50, 0x3f200c33, 0x06a2008f,
|
||||
0x3fa07458, 0x3f10002b, 0x06520c8d,
|
||||
0x3fd0905e, 0x3f1ff424, 0x05e22089,
|
||||
0x0000ac65, 0x3f1fe81d, 0x05823483,
|
||||
0x0030cc6a, 0x3f2fdc18, 0x04f23c81,
|
||||
0x0080e871, 0x3f2fd412, 0x0482407c },
|
||||
},
|
||||
{ 0x3800, {
|
||||
0x3f604043, 0x3f604043, 0x0721c88a,
|
||||
0x3f80544a, 0x3f502c3c, 0x06d1d88a,
|
||||
0x3fb06851, 0x3f301c35, 0x0681e889,
|
||||
0x3fd08456, 0x3f30082f, 0x0611fc88,
|
||||
0x00009c5d, 0x3f200027, 0x05d20884,
|
||||
0x0030b863, 0x3f2ff421, 0x05621880,
|
||||
0x0070d468, 0x3f2fe81b, 0x0502247c,
|
||||
0x00c0ec6f, 0x3f2fe015, 0x04a22877 },
|
||||
},
|
||||
{ 0x3a00, {
|
||||
0x3f904c44, 0x3f904c44, 0x06e1b884,
|
||||
0x3fb0604a, 0x3f70383e, 0x0691c885,
|
||||
0x3fe07451, 0x3f502c36, 0x0661d483,
|
||||
0x00009055, 0x3f401831, 0x0601ec81,
|
||||
0x0030a85b, 0x3f300c2a, 0x05b1f480,
|
||||
0x0070c061, 0x3f300024, 0x0562047a,
|
||||
0x00b0d867, 0x3f3ff41e, 0x05020c77,
|
||||
0x00f0f46b, 0x3f2fec19, 0x04a21474 },
|
||||
},
|
||||
{ 0x3c00, {
|
||||
0x3fb05c43, 0x3fb05c43, 0x06c1b07e,
|
||||
0x3fe06c4b, 0x3f902c3f, 0x0681c081,
|
||||
0x0000844f, 0x3f703838, 0x0631cc7d,
|
||||
0x00309855, 0x3f602433, 0x05d1d47e,
|
||||
0x0060b459, 0x3f50142e, 0x0581e47b,
|
||||
0x00a0c85f, 0x3f400828, 0x0531f078,
|
||||
0x00e0e064, 0x3f300021, 0x0501fc73,
|
||||
0x00b0fc6a, 0x3f3ff41d, 0x04a20873 },
|
||||
},
|
||||
{ 0x3e00, {
|
||||
0x3fe06444, 0x3fe06444, 0x0681a07a,
|
||||
0x00007849, 0x3fc0503f, 0x0641b07a,
|
||||
0x0020904d, 0x3fa0403a, 0x05f1c07a,
|
||||
0x0060a453, 0x3f803034, 0x05c1c878,
|
||||
0x0090b858, 0x3f70202f, 0x0571d477,
|
||||
0x00d0d05d, 0x3f501829, 0x0531e073,
|
||||
0x0110e462, 0x3f500825, 0x04e1e471,
|
||||
0x01510065, 0x3f40001f, 0x04a1f06d },
|
||||
},
|
||||
{ 0x4000, {
|
||||
0x00007044, 0x00007044, 0x06519476,
|
||||
0x00208448, 0x3fe05c3f, 0x0621a476,
|
||||
0x0050984d, 0x3fc04c3a, 0x05e1b075,
|
||||
0x0080ac52, 0x3fa03c35, 0x05a1b875,
|
||||
0x00c0c056, 0x3f803030, 0x0561c473,
|
||||
0x0100d45b, 0x3f70202b, 0x0521d46f,
|
||||
0x0140e860, 0x3f601427, 0x04d1d46e,
|
||||
0x01810064, 0x3f500822, 0x0491dc6b },
|
||||
},
|
||||
{ 0x5000, {
|
||||
0x0110a442, 0x0110a442, 0x0551545e,
|
||||
0x0140b045, 0x00e0983f, 0x0531585f,
|
||||
0x0160c047, 0x00c08c3c, 0x0511645e,
|
||||
0x0190cc4a, 0x00908039, 0x04f1685f,
|
||||
0x01c0dc4c, 0x00707436, 0x04d1705e,
|
||||
0x0200e850, 0x00506833, 0x04b1785b,
|
||||
0x0230f453, 0x00305c30, 0x0491805a,
|
||||
0x02710056, 0x0010542d, 0x04718059 },
|
||||
},
|
||||
{ 0x6000, {
|
||||
0x01c0bc40, 0x01c0bc40, 0x04c13052,
|
||||
0x01e0c841, 0x01a0b43d, 0x04c13851,
|
||||
0x0210cc44, 0x0180a83c, 0x04a13453,
|
||||
0x0230d845, 0x0160a03a, 0x04913c52,
|
||||
0x0260e047, 0x01409838, 0x04714052,
|
||||
0x0280ec49, 0x01208c37, 0x04514c50,
|
||||
0x02b0f44b, 0x01008435, 0x04414c50,
|
||||
0x02d1004c, 0x00e07c33, 0x0431544f },
|
||||
},
|
||||
{ 0x7000, {
|
||||
0x0230c83e, 0x0230c83e, 0x04711c4c,
|
||||
0x0250d03f, 0x0210c43c, 0x0471204b,
|
||||
0x0270d840, 0x0200b83c, 0x0451244b,
|
||||
0x0290dc42, 0x01e0b43a, 0x0441244c,
|
||||
0x02b0e443, 0x01c0b038, 0x0441284b,
|
||||
0x02d0ec44, 0x01b0a438, 0x0421304a,
|
||||
0x02f0f445, 0x0190a036, 0x04213449,
|
||||
0x0310f847, 0x01709c34, 0x04213848 },
|
||||
},
|
||||
{ 0x8000, {
|
||||
0x0280d03d, 0x0280d03d, 0x04310c48,
|
||||
0x02a0d43e, 0x0270c83c, 0x04311047,
|
||||
0x02b0dc3e, 0x0250c83a, 0x04311447,
|
||||
0x02d0e040, 0x0240c03a, 0x04211446,
|
||||
0x02e0e840, 0x0220bc39, 0x04111847,
|
||||
0x0300e842, 0x0210b438, 0x04012445,
|
||||
0x0310f043, 0x0200b037, 0x04012045,
|
||||
0x0330f444, 0x01e0ac36, 0x03f12445 },
|
||||
},
|
||||
{ 0xefff, {
|
||||
0x0340dc3a, 0x0340dc3a, 0x03b0ec40,
|
||||
0x0340e03a, 0x0330e039, 0x03c0f03e,
|
||||
0x0350e03b, 0x0330dc39, 0x03c0ec3e,
|
||||
0x0350e43a, 0x0320dc38, 0x03c0f43e,
|
||||
0x0360e43b, 0x0320d839, 0x03b0f03e,
|
||||
0x0360e83b, 0x0310d838, 0x03c0fc3b,
|
||||
0x0370e83b, 0x0310d439, 0x03a0f83d,
|
||||
0x0370e83c, 0x0300d438, 0x03b0fc3c },
|
||||
}
|
||||
};
|
||||
|
||||
enum rcar_vin_state {
|
||||
STOPPED = 0,
|
||||
RUNNING,
|
||||
|
|
@ -161,6 +504,9 @@ struct rcar_vin_cam {
|
|||
/* Client output, as seen by the VIN */
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
/* User window from S_FMT */
|
||||
unsigned int out_width;
|
||||
unsigned int out_height;
|
||||
/*
|
||||
* User window from S_CROP / G_CROP, produced by client cropping and
|
||||
* scaling, VIN scaling and VIN cropping, mapped back onto the client
|
||||
|
|
@ -332,7 +678,7 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
|
|||
vnmc |= VNMC_BPS;
|
||||
|
||||
/* progressive or interlaced mode */
|
||||
interrupts = progressive ? VNIE_FIE | VNIE_EFE : VNIE_EFE;
|
||||
interrupts = progressive ? VNIE_FIE : VNIE_EFE;
|
||||
|
||||
/* ack interrupts */
|
||||
iowrite32(interrupts, priv->base + VNINTS_REG);
|
||||
|
|
@ -667,6 +1013,60 @@ static void rcar_vin_clock_stop(struct soc_camera_host *ici)
|
|||
/* VIN does not have "mclk" */
|
||||
}
|
||||
|
||||
static void set_coeff(struct rcar_vin_priv *priv, unsigned short xs)
|
||||
{
|
||||
int i;
|
||||
const struct vin_coeff *p_prev_set = NULL;
|
||||
const struct vin_coeff *p_set = NULL;
|
||||
|
||||
/* Look for suitable coefficient values */
|
||||
for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) {
|
||||
p_prev_set = p_set;
|
||||
p_set = &vin_coeff_set[i];
|
||||
|
||||
if (xs < p_set->xs_value)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use previous value if its XS value is closer */
|
||||
if (p_prev_set && p_set &&
|
||||
xs - p_prev_set->xs_value < p_set->xs_value - xs)
|
||||
p_set = p_prev_set;
|
||||
|
||||
/* Set coefficient registers */
|
||||
iowrite32(p_set->coeff_set[0], priv->base + VNC1A_REG);
|
||||
iowrite32(p_set->coeff_set[1], priv->base + VNC1B_REG);
|
||||
iowrite32(p_set->coeff_set[2], priv->base + VNC1C_REG);
|
||||
|
||||
iowrite32(p_set->coeff_set[3], priv->base + VNC2A_REG);
|
||||
iowrite32(p_set->coeff_set[4], priv->base + VNC2B_REG);
|
||||
iowrite32(p_set->coeff_set[5], priv->base + VNC2C_REG);
|
||||
|
||||
iowrite32(p_set->coeff_set[6], priv->base + VNC3A_REG);
|
||||
iowrite32(p_set->coeff_set[7], priv->base + VNC3B_REG);
|
||||
iowrite32(p_set->coeff_set[8], priv->base + VNC3C_REG);
|
||||
|
||||
iowrite32(p_set->coeff_set[9], priv->base + VNC4A_REG);
|
||||
iowrite32(p_set->coeff_set[10], priv->base + VNC4B_REG);
|
||||
iowrite32(p_set->coeff_set[11], priv->base + VNC4C_REG);
|
||||
|
||||
iowrite32(p_set->coeff_set[12], priv->base + VNC5A_REG);
|
||||
iowrite32(p_set->coeff_set[13], priv->base + VNC5B_REG);
|
||||
iowrite32(p_set->coeff_set[14], priv->base + VNC5C_REG);
|
||||
|
||||
iowrite32(p_set->coeff_set[15], priv->base + VNC6A_REG);
|
||||
iowrite32(p_set->coeff_set[16], priv->base + VNC6B_REG);
|
||||
iowrite32(p_set->coeff_set[17], priv->base + VNC6C_REG);
|
||||
|
||||
iowrite32(p_set->coeff_set[18], priv->base + VNC7A_REG);
|
||||
iowrite32(p_set->coeff_set[19], priv->base + VNC7B_REG);
|
||||
iowrite32(p_set->coeff_set[20], priv->base + VNC7C_REG);
|
||||
|
||||
iowrite32(p_set->coeff_set[21], priv->base + VNC8A_REG);
|
||||
iowrite32(p_set->coeff_set[22], priv->base + VNC8B_REG);
|
||||
iowrite32(p_set->coeff_set[23], priv->base + VNC8C_REG);
|
||||
}
|
||||
|
||||
/* rect is guaranteed to not exceed the scaled camera rectangle */
|
||||
static int rcar_vin_set_rect(struct soc_camera_device *icd)
|
||||
{
|
||||
|
|
@ -676,6 +1076,7 @@ static int rcar_vin_set_rect(struct soc_camera_device *icd)
|
|||
unsigned int left_offset, top_offset;
|
||||
unsigned char dsize = 0;
|
||||
struct v4l2_rect *cam_subrect = &cam->subrect;
|
||||
u32 value;
|
||||
|
||||
dev_dbg(icd->parent, "Crop %ux%u@%u:%u\n",
|
||||
icd->user_width, icd->user_height, cam->vin_left, cam->vin_top);
|
||||
|
|
@ -695,40 +1096,64 @@ static int rcar_vin_set_rect(struct soc_camera_device *icd)
|
|||
|
||||
/* Set Start/End Pixel/Line Pre-Clip */
|
||||
iowrite32(left_offset << dsize, priv->base + VNSPPRC_REG);
|
||||
iowrite32((left_offset + cam->width - 1) << dsize,
|
||||
iowrite32((left_offset + cam_subrect->width - 1) << dsize,
|
||||
priv->base + VNEPPRC_REG);
|
||||
switch (priv->field) {
|
||||
case V4L2_FIELD_INTERLACED:
|
||||
case V4L2_FIELD_INTERLACED_TB:
|
||||
case V4L2_FIELD_INTERLACED_BT:
|
||||
iowrite32(top_offset / 2, priv->base + VNSLPRC_REG);
|
||||
iowrite32((top_offset + cam->height) / 2 - 1,
|
||||
iowrite32((top_offset + cam_subrect->height) / 2 - 1,
|
||||
priv->base + VNELPRC_REG);
|
||||
break;
|
||||
default:
|
||||
iowrite32(top_offset, priv->base + VNSLPRC_REG);
|
||||
iowrite32(top_offset + cam->height - 1,
|
||||
iowrite32(top_offset + cam_subrect->height - 1,
|
||||
priv->base + VNELPRC_REG);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set scaling coefficient */
|
||||
value = 0;
|
||||
if (cam_subrect->height != cam->out_height)
|
||||
value = (4096 * cam_subrect->height) / cam->out_height;
|
||||
dev_dbg(icd->parent, "YS Value: %x\n", value);
|
||||
iowrite32(value, priv->base + VNYS_REG);
|
||||
|
||||
value = 0;
|
||||
if (cam_subrect->width != cam->out_width)
|
||||
value = (4096 * cam_subrect->width) / cam->out_width;
|
||||
|
||||
/* Horizontal upscaling is up to double size */
|
||||
if (0 < value && value < 2048)
|
||||
value = 2048;
|
||||
|
||||
dev_dbg(icd->parent, "XS Value: %x\n", value);
|
||||
iowrite32(value, priv->base + VNXS_REG);
|
||||
|
||||
/* Horizontal upscaling is carried out by scaling down from double size */
|
||||
if (value < 4096)
|
||||
value *= 2;
|
||||
|
||||
set_coeff(priv, value);
|
||||
|
||||
/* Set Start/End Pixel/Line Post-Clip */
|
||||
iowrite32(0, priv->base + VNSPPOC_REG);
|
||||
iowrite32(0, priv->base + VNSLPOC_REG);
|
||||
iowrite32((cam_subrect->width - 1) << dsize, priv->base + VNEPPOC_REG);
|
||||
iowrite32((cam->out_width - 1) << dsize, priv->base + VNEPPOC_REG);
|
||||
switch (priv->field) {
|
||||
case V4L2_FIELD_INTERLACED:
|
||||
case V4L2_FIELD_INTERLACED_TB:
|
||||
case V4L2_FIELD_INTERLACED_BT:
|
||||
iowrite32(cam_subrect->height / 2 - 1,
|
||||
iowrite32(cam->out_height / 2 - 1,
|
||||
priv->base + VNELPOC_REG);
|
||||
break;
|
||||
default:
|
||||
iowrite32(cam_subrect->height - 1, priv->base + VNELPOC_REG);
|
||||
iowrite32(cam->out_height - 1, priv->base + VNELPOC_REG);
|
||||
break;
|
||||
}
|
||||
|
||||
iowrite32(ALIGN(cam->width, 0x10), priv->base + VNIS_REG);
|
||||
iowrite32(ALIGN(cam->out_width, 0x10), priv->base + VNIS_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -819,7 +1244,7 @@ static int rcar_vin_set_bus_param(struct soc_camera_device *icd)
|
|||
if (ret < 0 && ret != -ENOIOCTLCMD)
|
||||
return ret;
|
||||
|
||||
val = priv->field == V4L2_FIELD_NONE ? VNDMR2_FTEV : 0;
|
||||
val = VNDMR2_FTEV | VNDMR2_VLV(1);
|
||||
if (!(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
|
||||
val |= VNDMR2_VPS;
|
||||
if (!(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
|
||||
|
|
@ -879,6 +1304,14 @@ static const struct soc_mbus_pixelfmt rcar_vin_formats[] = {
|
|||
.order = SOC_MBUS_ORDER_LE,
|
||||
.layout = SOC_MBUS_LAYOUT_PLANAR_Y_C,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_YUYV,
|
||||
.name = "YUYV",
|
||||
.bits_per_sample = 16,
|
||||
.packing = SOC_MBUS_PACKING_NONE,
|
||||
.order = SOC_MBUS_ORDER_LE,
|
||||
.layout = SOC_MBUS_LAYOUT_PACKED,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_UYVY,
|
||||
.name = "UYVY",
|
||||
|
|
@ -999,6 +1432,8 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx,
|
|||
cam->subrect = rect;
|
||||
cam->width = mf.width;
|
||||
cam->height = mf.height;
|
||||
cam->out_width = mf.width;
|
||||
cam->out_height = mf.height;
|
||||
|
||||
icd->host_priv = cam;
|
||||
} else {
|
||||
|
|
@ -1259,6 +1694,9 @@ static int rcar_vin_set_fmt(struct soc_camera_device *icd,
|
|||
dev_dbg(dev, "W: %u : %u, H: %u : %u\n",
|
||||
vin_sub_width, pix->width, vin_sub_height, pix->height);
|
||||
|
||||
cam->out_width = pix->width;
|
||||
cam->out_height = pix->height;
|
||||
|
||||
icd->current_fmt = xlate;
|
||||
|
||||
priv->field = field;
|
||||
|
|
@ -1310,8 +1748,12 @@ static int rcar_vin_try_fmt(struct soc_camera_device *icd,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pix->width = mf.width;
|
||||
pix->height = mf.height;
|
||||
/* Adjust only if VIN cannot scale */
|
||||
if (pix->width > mf.width * 2)
|
||||
pix->width = mf.width * 2;
|
||||
if (pix->height > mf.height * 3)
|
||||
pix->height = mf.height * 3;
|
||||
|
||||
pix->field = mf.field;
|
||||
pix->colorspace = mf.colorspace;
|
||||
|
||||
|
|
@ -1395,6 +1837,8 @@ static struct soc_camera_host_ops rcar_vin_host_ops = {
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id rcar_vin_of_table[] = {
|
||||
{ .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 },
|
||||
{ .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 },
|
||||
{ .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 },
|
||||
{ .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 },
|
||||
{ .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 },
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,138 +0,0 @@
|
|||
/*
|
||||
* Driver for the VINO (Video In No Out) system found in SGI Indys.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* Copyright (C) 1999 Ulf Karlsson <ulfc@bun.falkenberg.se>
|
||||
* Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
|
||||
*/
|
||||
|
||||
#ifndef _VINO_H_
|
||||
#define _VINO_H_
|
||||
|
||||
#define VINO_BASE 0x00080000 /* Vino is in the EISA address space,
|
||||
* but it is not an EISA bus card */
|
||||
#define VINO_PAGE_SIZE 4096
|
||||
|
||||
struct sgi_vino_channel {
|
||||
u32 _pad_alpha;
|
||||
volatile u32 alpha;
|
||||
|
||||
#define VINO_CLIP_X(x) ((x) & 0x3ff) /* bits 0:9 */
|
||||
#define VINO_CLIP_ODD(x) (((x) & 0x1ff) << 10) /* bits 10:18 */
|
||||
#define VINO_CLIP_EVEN(x) (((x) & 0x1ff) << 19) /* bits 19:27 */
|
||||
u32 _pad_clip_start;
|
||||
volatile u32 clip_start;
|
||||
u32 _pad_clip_end;
|
||||
volatile u32 clip_end;
|
||||
|
||||
#define VINO_FRAMERT_FULL 0xfff
|
||||
#define VINO_FRAMERT_PAL (1<<0) /* 0=NTSC 1=PAL */
|
||||
#define VINO_FRAMERT_RT(x) (((x) & 0xfff) << 1) /* bits 1:12 */
|
||||
u32 _pad_frame_rate;
|
||||
volatile u32 frame_rate;
|
||||
|
||||
u32 _pad_field_counter;
|
||||
volatile u32 field_counter;
|
||||
u32 _pad_line_size;
|
||||
volatile u32 line_size;
|
||||
u32 _pad_line_count;
|
||||
volatile u32 line_count;
|
||||
u32 _pad_page_index;
|
||||
volatile u32 page_index;
|
||||
u32 _pad_next_4_desc;
|
||||
volatile u32 next_4_desc;
|
||||
u32 _pad_start_desc_tbl;
|
||||
volatile u32 start_desc_tbl;
|
||||
|
||||
#define VINO_DESC_JUMP (1<<30)
|
||||
#define VINO_DESC_STOP (1<<31)
|
||||
#define VINO_DESC_VALID (1<<32)
|
||||
u32 _pad_desc_0;
|
||||
volatile u32 desc_0;
|
||||
u32 _pad_desc_1;
|
||||
volatile u32 desc_1;
|
||||
u32 _pad_desc_2;
|
||||
volatile u32 desc_2;
|
||||
u32 _pad_Bdesc_3;
|
||||
volatile u32 desc_3;
|
||||
|
||||
u32 _pad_fifo_thres;
|
||||
volatile u32 fifo_thres;
|
||||
u32 _pad_fifo_read;
|
||||
volatile u32 fifo_read;
|
||||
u32 _pad_fifo_write;
|
||||
volatile u32 fifo_write;
|
||||
};
|
||||
|
||||
struct sgi_vino {
|
||||
#define VINO_CHIP_ID 0xb
|
||||
#define VINO_REV_NUM(x) ((x) & 0x0f)
|
||||
#define VINO_ID_VALUE(x) (((x) & 0xf0) >> 4)
|
||||
u32 _pad_rev_id;
|
||||
volatile u32 rev_id;
|
||||
|
||||
#define VINO_CTRL_LITTLE_ENDIAN (1<<0)
|
||||
#define VINO_CTRL_A_EOF_INT (1<<1) /* Field transferred int */
|
||||
#define VINO_CTRL_A_FIFO_INT (1<<2) /* FIFO overflow int */
|
||||
#define VINO_CTRL_A_EOD_INT (1<<3) /* End of desc table int */
|
||||
#define VINO_CTRL_A_INT (VINO_CTRL_A_EOF_INT | \
|
||||
VINO_CTRL_A_FIFO_INT | \
|
||||
VINO_CTRL_A_EOD_INT)
|
||||
#define VINO_CTRL_B_EOF_INT (1<<4) /* Field transferred int */
|
||||
#define VINO_CTRL_B_FIFO_INT (1<<5) /* FIFO overflow int */
|
||||
#define VINO_CTRL_B_EOD_INT (1<<6) /* End of desc table int */
|
||||
#define VINO_CTRL_B_INT (VINO_CTRL_B_EOF_INT | \
|
||||
VINO_CTRL_B_FIFO_INT | \
|
||||
VINO_CTRL_B_EOD_INT)
|
||||
#define VINO_CTRL_A_DMA_ENBL (1<<7)
|
||||
#define VINO_CTRL_A_INTERLEAVE_ENBL (1<<8)
|
||||
#define VINO_CTRL_A_SYNC_ENBL (1<<9)
|
||||
#define VINO_CTRL_A_SELECT (1<<10) /* 1=D1 0=Philips */
|
||||
#define VINO_CTRL_A_RGB (1<<11) /* 1=RGB 0=YUV */
|
||||
#define VINO_CTRL_A_LUMA_ONLY (1<<12)
|
||||
#define VINO_CTRL_A_DEC_ENBL (1<<13) /* Decimation */
|
||||
#define VINO_CTRL_A_DEC_SCALE_MASK 0x1c000 /* bits 14:17 */
|
||||
#define VINO_CTRL_A_DEC_SCALE_SHIFT (14)
|
||||
#define VINO_CTRL_A_DEC_HOR_ONLY (1<<17) /* Horizontal only */
|
||||
#define VINO_CTRL_A_DITHER (1<<18) /* 24 -> 8 bit dither */
|
||||
#define VINO_CTRL_B_DMA_ENBL (1<<19)
|
||||
#define VINO_CTRL_B_INTERLEAVE_ENBL (1<<20)
|
||||
#define VINO_CTRL_B_SYNC_ENBL (1<<21)
|
||||
#define VINO_CTRL_B_SELECT (1<<22) /* 1=D1 0=Philips */
|
||||
#define VINO_CTRL_B_RGB (1<<23) /* 1=RGB 0=YUV */
|
||||
#define VINO_CTRL_B_LUMA_ONLY (1<<24)
|
||||
#define VINO_CTRL_B_DEC_ENBL (1<<25) /* Decimation */
|
||||
#define VINO_CTRL_B_DEC_SCALE_MASK 0x1c000000 /* bits 26:28 */
|
||||
#define VINO_CTRL_B_DEC_SCALE_SHIFT (26)
|
||||
#define VINO_CTRL_B_DEC_HOR_ONLY (1<<29) /* Decimation horizontal only */
|
||||
#define VINO_CTRL_B_DITHER (1<<30) /* ChanB 24 -> 8 bit dither */
|
||||
u32 _pad_control;
|
||||
volatile u32 control;
|
||||
|
||||
#define VINO_INTSTAT_A_EOF (1<<0) /* Field transferred int */
|
||||
#define VINO_INTSTAT_A_FIFO (1<<1) /* FIFO overflow int */
|
||||
#define VINO_INTSTAT_A_EOD (1<<2) /* End of desc table int */
|
||||
#define VINO_INTSTAT_A (VINO_INTSTAT_A_EOF | \
|
||||
VINO_INTSTAT_A_FIFO | \
|
||||
VINO_INTSTAT_A_EOD)
|
||||
#define VINO_INTSTAT_B_EOF (1<<3) /* Field transferred int */
|
||||
#define VINO_INTSTAT_B_FIFO (1<<4) /* FIFO overflow int */
|
||||
#define VINO_INTSTAT_B_EOD (1<<5) /* End of desc table int */
|
||||
#define VINO_INTSTAT_B (VINO_INTSTAT_B_EOF | \
|
||||
VINO_INTSTAT_B_FIFO | \
|
||||
VINO_INTSTAT_B_EOD)
|
||||
u32 _pad_intr_status;
|
||||
volatile u32 intr_status;
|
||||
|
||||
u32 _pad_i2c_control;
|
||||
volatile u32 i2c_control;
|
||||
u32 _pad_i2c_data;
|
||||
volatile u32 i2c_data;
|
||||
|
||||
struct sgi_vino_channel a;
|
||||
struct sgi_vino_channel b;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -625,7 +625,7 @@ int vivid_vid_out_g_selection(struct file *file, void *priv,
|
|||
sel->r = dev->fmt_out_rect;
|
||||
break;
|
||||
case V4L2_SEL_TGT_CROP_BOUNDS:
|
||||
if (!dev->has_compose_out)
|
||||
if (!dev->has_crop_out)
|
||||
return -EINVAL;
|
||||
sel->r = vivid_max_rect;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ if MEDIA_ANALOG_TV_SUPPORT
|
|||
comment "Analog TV USB devices"
|
||||
source "drivers/media/usb/pvrusb2/Kconfig"
|
||||
source "drivers/media/usb/hdpvr/Kconfig"
|
||||
source "drivers/media/usb/tlg2300/Kconfig"
|
||||
source "drivers/media/usb/usbvision/Kconfig"
|
||||
source "drivers/media/usb/stk1160/Kconfig"
|
||||
source "drivers/media/usb/go7007/Kconfig"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
|
|||
obj-$(CONFIG_VIDEO_AU0828) += au0828/
|
||||
obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/
|
||||
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
|
||||
obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
|
||||
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
|
||||
obj-$(CONFIG_VIDEO_STK1160) += stk1160/
|
||||
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
config VIDEO_TLG2300
|
||||
tristate "Telegent TLG2300 USB video capture support"
|
||||
depends on VIDEO_DEV && I2C && SND && DVB_CORE
|
||||
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.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called poseidon
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1,271 +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;
|
||||
void set_debug_mode(struct video_device *vfd, 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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -1,339 +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) {
|
||||
struct video_device *vfd = &p->radio_data.fm_dev;
|
||||
|
||||
/* default pre-emphasis */
|
||||
if (p->radio_data.pre_emphasis == 0)
|
||||
p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR;
|
||||
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;
|
||||
}
|
||||
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
|
|
@ -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_ */
|
||||
|
|
@ -1017,6 +1017,12 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
|
|||
ret = ops->vidioc_querycap(file, fh, cap);
|
||||
|
||||
cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT;
|
||||
/*
|
||||
* Drivers MUST fill in device_caps, so check for this and
|
||||
* warn if it was forgotten.
|
||||
*/
|
||||
WARN_ON(!(cap->capabilities & V4L2_CAP_DEVICE_CAPS) ||
|
||||
!cap->device_caps);
|
||||
cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT;
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
Loading…
Reference in a new issue