Commit 866b8695 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Staging: add the go7007 video driver

Todo:
	- checkpatch.pl cleanups
	- sparse cleanups
	- lots of little modules, should be merged together
	  and added to the build.
	- testing?
	- handle churn in v4l layer.

Many thanks to Ross Cohen <rcohen@snurgle.org> for cleanup patches on
this driver.

Cc: Ross Cohen <rcohen@snurgle.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent c0f00588
...@@ -31,4 +31,6 @@ source "drivers/staging/sxg/Kconfig" ...@@ -31,4 +31,6 @@ source "drivers/staging/sxg/Kconfig"
source "drivers/staging/me4000/Kconfig" source "drivers/staging/me4000/Kconfig"
source "drivers/staging/go7007/Kconfig"
endif # STAGING endif # STAGING
...@@ -4,3 +4,4 @@ obj-$(CONFIG_ET131X) += et131x/ ...@@ -4,3 +4,4 @@ obj-$(CONFIG_ET131X) += et131x/
obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_SLICOSS) += slicoss/
obj-$(CONFIG_SXG) += sxg/ obj-$(CONFIG_SXG) += sxg/
obj-$(CONFIG_ME4000) += me4000/ obj-$(CONFIG_ME4000) += me4000/
obj-$(CONFIG_VIDEO_GO7007) += go7007/
config VIDEO_GO7007
tristate "Go 7007 support"
depends on VIDEO_DEV && PCI && I2C && INPUT
select VIDEOBUF_DMA_SG
select VIDEO_IR
select VIDEO_TUNER
select VIDEO_TVEEPROM
select CRC32
default N
---help---
This is a video4linux driver for some wierd device...
To compile this driver as a module, choose M here: the
module will be called go7007
config VIDEO_GO7007_USB
tristate "Go 7007 USB support"
depends on VIDEO_GO7007 && USB
default N
---help---
This is a video4linux driver for some wierd device...
To compile this driver as a module, choose M here: the
module will be called go7007-usb
#obj-m += go7007.o go7007-usb.o snd-go7007.o wis-saa7115.o wis-tw9903.o \
wis-uda1342.o wis-sony-tuner.o wis-saa7113.o wis-ov7640.o \
wis-tw2804.o
obj-$(CONFIG_VIDEO_GO7007) += go7007.o
obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o
go7007-objs += go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o snd-go7007.o
#ifneq ($(SAA7134_BUILD),)
#obj-m += saa7134-go7007.o
#endif
EXTRA_CFLAGS += -Idrivers/staging/saa7134
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
Todo:
- checkpatch.pl cleanups
- sparse cleanups
- lots of little modules, should be merged together
and added to the build.
- testing?
- handle churn in v4l layer.
Please send patchs to Greg Kroah-Hartman <greg@kroah.com> and Cc: Ross
Cohen <rcohen@snurgle.org> as well.
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/unistd.h>
#include <linux/time.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/firmware.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <asm/system.h>
#include <linux/videodev.h>
#include <media/tuner.h>
#include <media/v4l2-common.h>
#include "go7007-priv.h"
#include "wis-i2c.h"
/*
* Wait for an interrupt to be delivered from the GO7007SB and return
* the associated value and data.
*
* Must be called with the hw_lock held.
*/
int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data)
{
go->interrupt_available = 0;
go->hpi_ops->read_interrupt(go);
if (wait_event_timeout(go->interrupt_waitq,
go->interrupt_available, 5*HZ) < 0) {
printk(KERN_ERR "go7007: timeout waiting for read interrupt\n");
return -1;
}
if (!go->interrupt_available)
return -1;
go->interrupt_available = 0;
*value = go->interrupt_value & 0xfffe;
*data = go->interrupt_data;
return 0;
}
EXPORT_SYMBOL(go7007_read_interrupt);
/*
* Read a register/address on the GO7007SB.
*
* Must be called with the hw_lock held.
*/
int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data)
{
int count = 100;
u16 value;
if (go7007_write_interrupt(go, 0x0010, addr) < 0)
return -EIO;
while (count-- > 0) {
if (go7007_read_interrupt(go, &value, data) == 0 &&
value == 0xa000)
return 0;
}
return -EIO;
}
EXPORT_SYMBOL(go7007_read_addr);
/*
* Send the boot firmware to the encoder, which just wakes it up and lets
* us talk to the GPIO pins and on-board I2C adapter.
*
* Must be called with the hw_lock held.
*/
static int go7007_load_encoder(struct go7007 *go)
{
const struct firmware *fw_entry;
char fw_name[] = "go7007fw.bin";
void *bounce;
int fw_len, rv = 0;
u16 intr_val, intr_data;
if (request_firmware(&fw_entry, fw_name, go->dev)) {
printk(KERN_ERR
"go7007: unable to load firmware from file \"%s\"\n",
fw_name);
return -1;
}
if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) {
printk(KERN_ERR "go7007: file \"%s\" does not appear to be "
"go7007 firmware\n", fw_name);
release_firmware(fw_entry);
return -1;
}
fw_len = fw_entry->size - 16;
bounce = kmalloc(fw_len, GFP_KERNEL);
if (bounce == NULL) {
printk(KERN_ERR "go7007: unable to allocate %d bytes for "
"firmware transfer\n", fw_len);
release_firmware(fw_entry);
return -1;
}
memcpy(bounce, fw_entry->data + 16, fw_len);
release_firmware(fw_entry);
if (go7007_interface_reset(go) < 0 ||
go7007_send_firmware(go, bounce, fw_len) < 0 ||
go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
(intr_val & ~0x1) != 0x5a5a) {
printk(KERN_ERR "go7007: error transferring firmware\n");
rv = -1;
}
kfree(bounce);
return rv;
}
/*
* Boot the encoder and register the I2C adapter if requested. Do the
* minimum initialization necessary, since the board-specific code may
* still need to probe the board ID.
*
* Must NOT be called with the hw_lock held.
*/
int go7007_boot_encoder(struct go7007 *go, int init_i2c)
{
int ret;
down(&go->hw_lock);
ret = go7007_load_encoder(go);
up(&go->hw_lock);
if (ret < 0)
return -1;
if (!init_i2c)
return 0;
if (go7007_i2c_init(go) < 0)
return -1;
go->i2c_adapter_online = 1;
return 0;
}
EXPORT_SYMBOL(go7007_boot_encoder);
/*
* Configure any hardware-related registers in the GO7007, such as GPIO
* pins and bus parameters, which are board-specific. This assumes
* the boot firmware has already been downloaded.
*
* Must be called with the hw_lock held.
*/
static int go7007_init_encoder(struct go7007 *go)
{
if (go->board_info->audio_flags & GO7007_AUDIO_I2S_MASTER) {
go7007_write_addr(go, 0x1000, 0x0811);
go7007_write_addr(go, 0x1000, 0x0c11);
}
if (go->board_id == GO7007_BOARDID_MATRIX_REV) {
/* Set GPIO pin 0 to be an output (audio clock control) */
go7007_write_addr(go, 0x3c82, 0x0001);
go7007_write_addr(go, 0x3c80, 0x00fe);
}
return 0;
}
/*
* Send the boot firmware to the GO7007 and configure the registers. This
* is the only way to stop the encoder once it has started streaming video.
*
* Must be called with the hw_lock held.
*/
int go7007_reset_encoder(struct go7007 *go)
{
if (go7007_load_encoder(go) < 0)
return -1;
return go7007_init_encoder(go);
}
/*
* Attempt to instantiate an I2C client by ID, probably loading a module.
*/
static int init_i2c_module(struct i2c_adapter *adapter, int id, int addr)
{
char *modname;
switch (id) {
case I2C_DRIVERID_WIS_SAA7115:
modname = "wis-saa7115";
break;
case I2C_DRIVERID_WIS_SAA7113:
modname = "wis-saa7113";
break;
case I2C_DRIVERID_WIS_UDA1342:
modname = "wis-uda1342";
break;
case I2C_DRIVERID_WIS_SONY_TUNER:
modname = "wis-sony-tuner";
break;
case I2C_DRIVERID_WIS_TW9903:
modname = "wis-tw9903";
break;
case I2C_DRIVERID_WIS_TW2804:
modname = "wis-tw2804";
break;
case I2C_DRIVERID_WIS_OV7640:
modname = "wis-ov7640";
break;
default:
modname = NULL;
break;
}
if (modname != NULL)
request_module(modname);
if (wis_i2c_probe_device(adapter, id, addr) == 1)
return 0;
if (modname != NULL)
printk(KERN_INFO
"go7007: probing for module %s failed", modname);
else
printk(KERN_INFO
"go7007: sensor %u seems to be unsupported!\n", id);
return -1;
}
/*
* Finalize the GO7007 hardware setup, register the on-board I2C adapter
* (if used on this board), load the I2C client driver for the sensor
* (SAA7115 or whatever) and other devices, and register the ALSA and V4L2
* interfaces.
*
* Must NOT be called with the hw_lock held.
*/
int go7007_register_encoder(struct go7007 *go)
{
int i, ret;
printk(KERN_INFO "go7007: registering new %s\n", go->name);
down(&go->hw_lock);
ret = go7007_init_encoder(go);
up(&go->hw_lock);
if (ret < 0)
return -1;
if (!go->i2c_adapter_online &&
go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) {
if (go7007_i2c_init(go) < 0)
return -1;
go->i2c_adapter_online = 1;
}
if (go->i2c_adapter_online) {
for (i = 0; i < go->board_info->num_i2c_devs; ++i)
init_i2c_module(&go->i2c_adapter,
go->board_info->i2c_devs[i].id,
go->board_info->i2c_devs[i].addr);
#ifdef TUNER_SET_TYPE_ADDR
if (go->tuner_type >= 0) {
struct tuner_setup tun_setup = {
.mode_mask = T_ANALOG_TV,
.addr = ADDR_UNSET,
.type = go->tuner_type
};
i2c_clients_command(&go->i2c_adapter,
TUNER_SET_TYPE_ADDR, &tun_setup);
}
#else
if (go->tuner_type >= 0)
i2c_clients_command(&go->i2c_adapter,
TUNER_SET_TYPE, &go->tuner_type);
#endif
if (go->board_id == GO7007_BOARDID_ADLINK_MPG24)
i2c_clients_command(&go->i2c_adapter,
DECODER_SET_CHANNEL, &go->channel_number);
}
if (go->board_info->flags & GO7007_BOARD_HAS_AUDIO) {
go->audio_enabled = 1;
go7007_snd_init(go);
}
return go7007_v4l2_init(go);
}
EXPORT_SYMBOL(go7007_register_encoder);
/*
* Send the encode firmware to the encoder, which will cause it
* to immediately start delivering the video and audio streams.
*
* Must be called with the hw_lock held.
*/
int go7007_start_encoder(struct go7007 *go)
{
u8 *fw;
int fw_len, rv = 0, i;
u16 intr_val, intr_data;
go->modet_enable = 0;
if (!go->dvd_mode)
for (i = 0; i < 4; ++i) {
if (go->modet[i].enable) {
go->modet_enable = 1;
continue;
}
go->modet[i].pixel_threshold = 32767;
go->modet[i].motion_threshold = 32767;
go->modet[i].mb_threshold = 32767;
}
if (go7007_construct_fw_image(go, &fw, &fw_len) < 0)
return -1;
if (go7007_send_firmware(go, fw, fw_len) < 0 ||
go7007_read_interrupt(go, &intr_val, &intr_data) < 0) {
printk(KERN_ERR "go7007: error transferring firmware\n");
rv = -1;
goto start_error;
}
go->state = STATE_DATA;
go->parse_length = 0;
go->seen_frame = 0;
if (go7007_stream_start(go) < 0) {
printk(KERN_ERR "go7007: error starting stream transfer\n");
rv = -1;
goto start_error;
}
start_error:
kfree(fw);
return rv;
}
/*
* Store a byte in the current video buffer, if there is one.
*/
static inline void store_byte(struct go7007_buffer *gobuf, u8 byte)
{
if (gobuf != NULL && gobuf->bytesused < GO7007_BUF_SIZE) {
unsigned int pgidx = gobuf->offset >> PAGE_SHIFT;
unsigned int pgoff = gobuf->offset & ~PAGE_MASK;
*((u8 *)page_address(gobuf->pages[pgidx]) + pgoff) = byte;
++gobuf->offset;
++gobuf->bytesused;
}
}
/*
* Deliver the last video buffer and get a new one to start writing to.
*/
static void frame_boundary(struct go7007 *go)
{
struct go7007_buffer *gobuf;
int i;
if (go->active_buf) {
if (go->active_buf->modet_active) {
if (go->active_buf->bytesused + 216 < GO7007_BUF_SIZE) {
for (i = 0; i < 216; ++i)
store_byte(go->active_buf,
go->active_map[i]);
go->active_buf->bytesused -= 216;
} else
go->active_buf->modet_active = 0;
}
go->active_buf->state = BUF_STATE_DONE;
wake_up_interruptible(&go->frame_waitq);
go->active_buf = NULL;
}
list_for_each_entry(gobuf, &go->stream, stream)
if (gobuf->state == BUF_STATE_QUEUED) {
gobuf->seq = go->next_seq;
do_gettimeofday(&gobuf->timestamp);
go->active_buf = gobuf;
break;
}
++go->next_seq;
}
static void write_bitmap_word(struct go7007 *go)
{
int x, y, i, stride = ((go->width >> 4) + 7) >> 3;
for (i = 0; i < 16; ++i) {
y = (((go->parse_length - 1) << 3) + i) / (go->width >> 4);
x = (((go->parse_length - 1) << 3) + i) % (go->width >> 4);
go->active_map[stride * y + (x >> 3)] |=
(go->modet_word & 1) << (x & 0x7);
go->modet_word >>= 1;
}
}
/*
* Parse a chunk of the video stream into frames. The frames are not
* delimited by the hardware, so we have to parse the frame boundaries
* based on the type of video stream we're receiving.
*/
void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length)
{
int i, seq_start_code = -1, frame_start_code = -1;
spin_lock(&go->spinlock);
switch (go->format) {
case GO7007_FORMAT_MPEG4:
seq_start_code = 0xB0;
frame_start_code = 0xB6;
break;
case GO7007_FORMAT_MPEG1:
case GO7007_FORMAT_MPEG2:
seq_start_code = 0xB3;
frame_start_code = 0x00;
break;
}
for (i = 0; i < length; ++i) {
if (go->active_buf != NULL &&
go->active_buf->bytesused >= GO7007_BUF_SIZE - 3) {
printk(KERN_DEBUG "go7007: dropping oversized frame\n");
go->active_buf->offset -= go->active_buf->bytesused;
go->active_buf->bytesused = 0;
go->active_buf->modet_active = 0;
go->active_buf = NULL;
}
switch (go->state) {
case STATE_DATA:
switch (buf[i]) {
case 0x00:
go->state = STATE_00;
break;
case 0xFF:
go->state = STATE_FF;
break;
default:
store_byte(go->active_buf, buf[i]);
break;
}
break;
case STATE_00:
switch (buf[i]) {
case 0x00:
go->state = STATE_00_00;
break;
case 0xFF:
store_byte(go->active_buf, 0x00);
go->state = STATE_FF;
break;
default:
store_byte(go->active_buf, 0x00);
store_byte(go->active_buf, buf[i]);
go->state = STATE_DATA;
break;
}
break;
case STATE_00_00:
switch (buf[i]) {
case 0x00:
store_byte(go->active_buf, 0x00);
/* go->state remains STATE_00_00 */
break;
case 0x01:
go->state = STATE_00_00_01;
break;
case 0xFF:
store_byte(go->active_buf, 0x00);
store_byte(go->active_buf, 0x00);
go->state = STATE_FF;
break;
default:
store_byte(go->active_buf, 0x00);
store_byte(go->active_buf, 0x00);
store_byte(go->active_buf, buf[i]);
go->state = STATE_DATA;
break;
}
break;
case STATE_00_00_01:
/* If this is the start of a new MPEG frame,
* get a new buffer */
if ((go->format == GO7007_FORMAT_MPEG1 ||
go->format == GO7007_FORMAT_MPEG2 ||
go->format == GO7007_FORMAT_MPEG4) &&
(buf[i] == seq_start_code ||
buf[i] == 0xB8 || /* GOP code */
buf[i] == frame_start_code)) {
if (go->active_buf == NULL || go->seen_frame)
frame_boundary(go);
if (buf[i] == frame_start_code) {
if (go->active_buf != NULL)
go->active_buf->frame_offset =
go->active_buf->offset;
go->seen_frame = 1;
} else {
go->seen_frame = 0;
}
}
/* Handle any special chunk types, or just write the
* start code to the (potentially new) buffer */
switch (buf[i]) {
case 0xF5: /* timestamp */
go->parse_length = 12;
go->state = STATE_UNPARSED;
break;
case 0xF6: /* vbi */
go->state = STATE_VBI_LEN_A;
break;
case 0xF8: /* MD map */
go->parse_length = 0;
memset(go->active_map, 0,
sizeof(go->active_map));
go->state = STATE_MODET_MAP;
break;
case 0xFF: /* Potential JPEG start code */
store_byte(go->active_buf, 0x00);
store_byte(go->active_buf, 0x00);
store_byte(go->active_buf, 0x01);
go->state = STATE_FF;
break;
default:
store_byte(go->active_buf, 0x00);
store_byte(go->active_buf, 0x00);
store_byte(go->active_buf, 0x01);
store_byte(go->active_buf, buf[i]);
go->state = STATE_DATA;
break;
}
break;
case STATE_FF:
switch (buf[i]) {
case 0x00:
store_byte(go->active_buf, 0xFF);
go->state = STATE_00;
break;
case 0xFF:
store_byte(go->active_buf, 0xFF);
/* go->state remains STATE_FF */
break;
case 0xD8:
if (go->format == GO7007_FORMAT_MJPEG)
frame_boundary(go);
/* fall through */
default:
store_byte(go->active_buf, 0xFF);
store_byte(go->active_buf, buf[i]);
go->state = STATE_DATA;
break;
}
break;
case STATE_VBI_LEN_A:
go->parse_length = buf[i] << 8;
go->state = STATE_VBI_LEN_B;
break;
case STATE_VBI_LEN_B:
go->parse_length |= buf[i];
if (go->parse_length > 0)
go->state = STATE_UNPARSED;
else
go->state = STATE_DATA;
break;
case STATE_MODET_MAP:
if (go->parse_length < 204) {
if (go->parse_length & 1) {
go->modet_word |= buf[i];
write_bitmap_word(go);
} else
go->modet_word = buf[i] << 8;
} else if (go->parse_length == 207 && go->active_buf) {
go->active_buf->modet_active = buf[i];
}
if (++go->parse_length == 208)
go->state = STATE_DATA;
break;
case STATE_UNPARSED:
if (--go->parse_length == 0)
go->state = STATE_DATA;
break;
}
}
spin_unlock(&go->spinlock);
}
EXPORT_SYMBOL(go7007_parse_video_stream);
/*
* Allocate a new go7007 struct. Used by the hardware-specific probe.
*/
struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev)
{
struct go7007 *go;
int i;
go = kmalloc(sizeof(struct go7007), GFP_KERNEL);
if (go == NULL)
return NULL;
go->dev = dev;
go->board_info = board;
go->board_id = 0;
go->tuner_type = -1;
go->channel_number = 0;
go->name[0] = 0;
init_MUTEX(&go->hw_lock);
init_waitqueue_head(&go->frame_waitq);
spin_lock_init(&go->spinlock);
go->video_dev = NULL;
go->ref_count = 0;
go->status = STATUS_INIT;
memset(&go->i2c_adapter, 0, sizeof(go->i2c_adapter));
go->i2c_adapter_online = 0;
go->interrupt_available = 0;
init_waitqueue_head(&go->interrupt_waitq);
go->in_use = 0;
go->input = 0;
if (board->sensor_flags & GO7007_SENSOR_TV) {
go->standard = GO7007_STD_NTSC;
go->width = 720;
go->height = 480;
go->sensor_framerate = 30000;
} else {
go->standard = GO7007_STD_OTHER;
go->width = board->sensor_width;
go->height = board->sensor_height;
go->sensor_framerate = board->sensor_framerate;
}
go->encoder_v_offset = board->sensor_v_offset;
go->encoder_h_offset = board->sensor_h_offset;
go->encoder_h_halve = 0;
go->encoder_v_halve = 0;
go->encoder_subsample = 0;
go->streaming = 0;
go->format = GO7007_FORMAT_MJPEG;
go->bitrate = 1500000;
go->fps_scale = 1;
go->pali = 0;
go->aspect_ratio = GO7007_RATIO_1_1;
go->gop_size = 0;
go->ipb = 0;
go->closed_gop = 0;
go->repeat_seqhead = 0;
go->seq_header_enable = 0;
go->gop_header_enable = 0;
go->dvd_mode = 0;
go->interlace_coding = 0;
for (i = 0; i < 4; ++i)
go->modet[i].enable = 0;;
for (i = 0; i < 1624; ++i)
go->modet_map[i] = 0;
go->audio_deliver = NULL;
go->audio_enabled = 0;
INIT_LIST_HEAD(&go->stream);
return go;
}
EXPORT_SYMBOL(go7007_alloc);
/*
* Detach and unregister the encoder. The go7007 struct won't be freed
* until v4l2 finishes releasing its resources and all associated fds are
* closed by applications.
*/
void go7007_remove(struct go7007 *go)
{
if (go->i2c_adapter_online) {
if (i2c_del_adapter(&go->i2c_adapter) == 0)
go->i2c_adapter_online = 0;
else
printk(KERN_ERR
"go7007: error removing I2C adapter!\n");
}
if (go->audio_enabled)
go7007_snd_remove(go);
go7007_v4l2_remove(go);
}
EXPORT_SYMBOL(go7007_remove);
MODULE_LICENSE("GPL v2");
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
/*
* This file contains code to generate a firmware image for the GO7007SB
* encoder. Much of the firmware is read verbatim from a file, but some of
* it concerning bitrate control and other things that can be configured at
* run-time are generated dynamically. Note that the format headers
* generated here do not affect the functioning of the encoder; they are
* merely parroted back to the host at the start of each frame.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/time.h>
#include <linux/mm.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/firmware.h>
#include <asm/byteorder.h>
#include "go7007-priv.h"
/* Constants used in the source firmware image to describe code segments */
#define FLAG_MODE_MJPEG (1)
#define FLAG_MODE_MPEG1 (1<<1)
#define FLAG_MODE_MPEG2 (1<<2)
#define FLAG_MODE_MPEG4 (1<<3)
#define FLAG_MODE_H263 (1<<4)
#define FLAG_MODE_ALL (FLAG_MODE_MJPEG | FLAG_MODE_MPEG1 | \
FLAG_MODE_MPEG2 | FLAG_MODE_MPEG4 | \
FLAG_MODE_H263)
#define FLAG_SPECIAL (1<<8)
#define SPECIAL_FRM_HEAD 0
#define SPECIAL_BRC_CTRL 1
#define SPECIAL_CONFIG 2
#define SPECIAL_SEQHEAD 3
#define SPECIAL_AV_SYNC 4
#define SPECIAL_FINAL 5
#define SPECIAL_AUDIO 6
#define SPECIAL_MODET 7
/* Little data class for creating MPEG headers bit-by-bit */
struct code_gen {
unsigned char *p; /* destination */
u32 a; /* collects bits at the top of the variable */
int b; /* bit position of most recently-written bit */
int len; /* written out so far */
};
#define CODE_GEN(name, dest) struct code_gen name = { dest, 0, 32, 0 }
#define CODE_ADD(name, val, length) do { \
name.b -= (length); \
name.a |= (val) << name.b; \
while (name.b <= 24) { \
*name.p = name.a >> 24; \
++name.p; \
name.a <<= 8; \
name.b += 8; \
name.len += 8; \
} \
} while (0)
#define CODE_LENGTH(name) (name.len + (32 - name.b))
/* Tables for creating the bitrate control data */
static const s16 converge_speed_ip[101] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
5, 5, 5, 6, 6, 6, 7, 7, 8, 8,
9, 10, 10, 11, 12, 13, 14, 15, 16, 17,
19, 20, 22, 23, 25, 27, 30, 32, 35, 38,
41, 45, 49, 53, 58, 63, 69, 76, 83, 91,
100
};
static const s16 converge_speed_ipb[101] = {
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
4, 4, 4, 4, 5, 5, 5, 5, 5, 6,
6, 6, 6, 7, 7, 7, 7, 8, 8, 9,
9, 9, 10, 10, 11, 12, 12, 13, 14, 14,
15, 16, 17, 18, 19, 20, 22, 23, 25, 26,
28, 30, 32, 34, 37, 40, 42, 46, 49, 53,
57, 61, 66, 71, 77, 83, 90, 97, 106, 115,
125, 135, 147, 161, 175, 191, 209, 228, 249, 273,
300
};
static const s16 LAMBDA_table[4][101] = {
{ 16, 16, 16, 16, 17, 17, 17, 18, 18, 18,
19, 19, 19, 20, 20, 20, 21, 21, 22, 22,
22, 23, 23, 24, 24, 25, 25, 25, 26, 26,
27, 27, 28, 28, 29, 29, 30, 31, 31, 32,
32, 33, 33, 34, 35, 35, 36, 37, 37, 38,
39, 39, 40, 41, 42, 42, 43, 44, 45, 46,
46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
67, 68, 69, 70, 72, 73, 74, 76, 77, 78,
80, 81, 83, 84, 86, 87, 89, 90, 92, 94,
96
},
{
20, 20, 20, 21, 21, 21, 22, 22, 23, 23,
23, 24, 24, 25, 25, 26, 26, 27, 27, 28,
28, 29, 29, 30, 30, 31, 31, 32, 33, 33,
34, 34, 35, 36, 36, 37, 38, 38, 39, 40,
40, 41, 42, 43, 43, 44, 45, 46, 47, 48,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 62, 64, 65, 66, 67, 68,
70, 71, 72, 73, 75, 76, 78, 79, 80, 82,
83, 85, 86, 88, 90, 91, 93, 95, 96, 98,
100, 102, 103, 105, 107, 109, 111, 113, 115, 117,
120
},
{
24, 24, 24, 25, 25, 26, 26, 27, 27, 28,
28, 29, 29, 30, 30, 31, 31, 32, 33, 33,
34, 34, 35, 36, 36, 37, 38, 38, 39, 40,
41, 41, 42, 43, 44, 44, 45, 46, 47, 48,
49, 50, 50, 51, 52, 53, 54, 55, 56, 57,
58, 59, 60, 62, 63, 64, 65, 66, 67, 69,
70, 71, 72, 74, 75, 76, 78, 79, 81, 82,
84, 85, 87, 88, 90, 92, 93, 95, 97, 98,
100, 102, 104, 106, 108, 110, 112, 114, 116, 118,
120, 122, 124, 127, 129, 131, 134, 136, 138, 141,
144
},
{
32, 32, 33, 33, 34, 34, 35, 36, 36, 37,
38, 38, 39, 40, 41, 41, 42, 43, 44, 44,
45, 46, 47, 48, 49, 50, 50, 51, 52, 53,
54, 55, 56, 57, 58, 59, 60, 62, 63, 64,
65, 66, 67, 69, 70, 71, 72, 74, 75, 76,
78, 79, 81, 82, 84, 85, 87, 88, 90, 92,
93, 95, 97, 98, 100, 102, 104, 106, 108, 110,
112, 114, 116, 118, 120, 122, 124, 127, 129, 131,
134, 136, 139, 141, 144, 146, 149, 152, 154, 157,
160, 163, 166, 169, 172, 175, 178, 181, 185, 188,
192
}
};
/* MPEG blank frame generation tables */
enum mpeg_frame_type {
PFRAME,
BFRAME_PRE,
BFRAME_POST,
BFRAME_BIDIR,
BFRAME_EMPTY
};
static const u32 addrinctab[33][2] = {
{ 0x01, 1 }, { 0x03, 3 }, { 0x02, 3 }, { 0x03, 4 },
{ 0x02, 4 }, { 0x03, 5 }, { 0x02, 5 }, { 0x07, 7 },
{ 0x06, 7 }, { 0x0b, 8 }, { 0x0a, 8 }, { 0x09, 8 },
{ 0x08, 8 }, { 0x07, 8 }, { 0x06, 8 }, { 0x17, 10 },
{ 0x16, 10 }, { 0x15, 10 }, { 0x14, 10 }, { 0x13, 10 },
{ 0x12, 10 }, { 0x23, 11 }, { 0x22, 11 }, { 0x21, 11 },
{ 0x20, 11 }, { 0x1f, 11 }, { 0x1e, 11 }, { 0x1d, 11 },
{ 0x1c, 11 }, { 0x1b, 11 }, { 0x1a, 11 }, { 0x19, 11 },
{ 0x18, 11 }
};
/* Standard JPEG tables */
static const u8 default_intra_quant_table[] = {
8, 16, 19, 22, 26, 27, 29, 34,
16, 16, 22, 24, 27, 29, 34, 37,
19, 22, 26, 27, 29, 34, 34, 38,
22, 22, 26, 27, 29, 34, 37, 40,
22, 26, 27, 29, 32, 35, 40, 48,
26, 27, 29, 32, 35, 40, 48, 58,
26, 27, 29, 34, 38, 46, 56, 69,
27, 29, 35, 38, 46, 56, 69, 83
};
static const u8 bits_dc_luminance[] = {
0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
};
static const u8 val_dc_luminance[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
};
static const u8 bits_dc_chrominance[] = {
0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
};
static const u8 val_dc_chrominance[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
};
static const u8 bits_ac_luminance[] = {
0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
};
static const u8 val_ac_luminance[] = {
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa
};
static const u8 bits_ac_chrominance[] = {
0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77
};
static const u8 val_ac_chrominance[] = {
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa
};
/* Zig-zag mapping for quant table
*
* OK, let's do this mapping on the actual table above so it doesn't have
* to be done on the fly.
*/
static const int zz[64] = {
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
};
static int copy_packages(u16 *dest, u16 *src, int pkg_cnt, int space)
{
int i, cnt = pkg_cnt * 32;
if (space < cnt)
return -1;
for (i = 0; i < cnt; ++i)
dest[i] = __cpu_to_le16(src[i]);
return cnt;
}
static int mjpeg_frame_header(struct go7007 *go, unsigned char *buf, int q)
{
int i, p = 0;
buf[p++] = 0xff;
buf[p++] = 0xd8;
buf[p++] = 0xff;
buf[p++] = 0xdb;
buf[p++] = 0;
buf[p++] = 2 + 65;
buf[p++] = 0;
buf[p++] = default_intra_quant_table[0];
for (i = 1; i < 64; ++i)
/* buf[p++] = (default_intra_quant_table[i] * q) >> 3; */
buf[p++] = (default_intra_quant_table[zz[i]] * q) >> 3;
buf[p++] = 0xff;
buf[p++] = 0xc0;
buf[p++] = 0;
buf[p++] = 17;
buf[p++] = 8;
buf[p++] = go->height >> 8;
buf[p++] = go->height & 0xff;
buf[p++] = go->width >> 8;
buf[p++] = go->width & 0xff;
buf[p++] = 3;
buf[p++] = 1;
buf[p++] = 0x22;
buf[p++] = 0;
buf[p++] = 2;
buf[p++] = 0x11;
buf[p++] = 0;
buf[p++] = 3;
buf[p++] = 0x11;
buf[p++] = 0;
buf[p++] = 0xff;
buf[p++] = 0xc4;
buf[p++] = 418 >> 8;
buf[p++] = 418 & 0xff;
buf[p++] = 0x00;
memcpy(buf + p, bits_dc_luminance + 1, 16);
p += 16;
memcpy(buf + p, val_dc_luminance, sizeof(val_dc_luminance));
p += sizeof(val_dc_luminance);
buf[p++] = 0x01;
memcpy(buf + p, bits_dc_chrominance + 1, 16);
p += 16;
memcpy(buf + p, val_dc_chrominance, sizeof(val_dc_chrominance));
p += sizeof(val_dc_chrominance);
buf[p++] = 0x10;
memcpy(buf + p, bits_ac_luminance + 1, 16);
p += 16;
memcpy(buf + p, val_ac_luminance, sizeof(val_ac_luminance));
p += sizeof(val_ac_luminance);
buf[p++] = 0x11;
memcpy(buf + p, bits_ac_chrominance + 1, 16);
p += 16;
memcpy(buf + p, val_ac_chrominance, sizeof(val_ac_chrominance));
p += sizeof(val_ac_chrominance);
buf[p++] = 0xff;
buf[p++] = 0xda;
buf[p++] = 0;
buf[p++] = 12;
buf[p++] = 3;
buf[p++] = 1;
buf[p++] = 0x00;
buf[p++] = 2;
buf[p++] = 0x11;
buf[p++] = 3;
buf[p++] = 0x11;
buf[p++] = 0;
buf[p++] = 63;
buf[p++] = 0;
return p;
}
static int gen_mjpeghdr_to_package(struct go7007 *go, u16 *code, int space)
{
u8 *buf;
u16 mem = 0x3e00;
unsigned int addr = 0x19;
int size = 0, i, off = 0, chunk;
buf = kmalloc(4096, GFP_KERNEL);
if (buf == NULL) {
printk(KERN_ERR "go7007: unable to allocate 4096 bytes for "
"firmware construction\n");
return -1;
}
memset(buf, 0, 4096);
for (i = 1; i < 32; ++i) {
mjpeg_frame_header(go, buf + size, i);
size += 80;
}
chunk = mjpeg_frame_header(go, buf + size, 1);
memmove(buf + size, buf + size + 80, chunk - 80);
size += chunk - 80;
for (i = 0; i < size; i += chunk * 2) {
if (space - off < 32) {
off = -1;
goto done;
}
code[off + 1] = __cpu_to_le16(0x8000 | mem);
chunk = 28;
if (mem + chunk > 0x4000)
chunk = 0x4000 - mem;
if (i + 2 * chunk > size)
chunk = (size - i) / 2;
if (chunk < 28) {
code[off] = __cpu_to_le16(0x4000 | chunk);
code[off + 31] = __cpu_to_le16(addr++);
mem = 0x3e00;
} else {
code[off] = __cpu_to_le16(0x1000 | 28);
code[off + 31] = 0;
mem += 28;
}
memcpy(&code[off + 2], buf + i, chunk * 2);
off += 32;
}
done:
kfree(buf);
return off;
}
static int mpeg1_frame_header(struct go7007 *go, unsigned char *buf,
int modulo, int pict_struct, enum mpeg_frame_type frame)
{
int i, j, mb_code, mb_len;
int rows = go->interlace_coding ? go->height / 32 : go->height / 16;
CODE_GEN(c, buf + 6);
switch (frame) {
case PFRAME:
mb_code = 0x1;
mb_len = 3;
break;
case BFRAME_PRE:
mb_code = 0x2;
mb_len = 4;
break;
case BFRAME_POST:
mb_code = 0x2;
mb_len = 3;
break;
case BFRAME_BIDIR:
mb_code = 0x2;
mb_len = 2;
break;
default: /* keep the compiler happy */
mb_code = mb_len = 0;
break;
}
CODE_ADD(c, frame == PFRAME ? 0x2 : 0x3, 13);
CODE_ADD(c, 0xffff, 16);
CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4);
if (frame != PFRAME)
CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4);
else
CODE_ADD(c, 0, 4); /* Is this supposed to be here?? */
CODE_ADD(c, 0, 3); /* What is this?? */
/* Byte-align with zeros */
j = 8 - (CODE_LENGTH(c) % 8);
if (j != 8)
CODE_ADD(c, 0, j);
if (go->format == GO7007_FORMAT_MPEG2) {
CODE_ADD(c, 0x1, 24);
CODE_ADD(c, 0xb5, 8);
CODE_ADD(c, 0x844, 12);
CODE_ADD(c, frame == PFRAME ? 0xff : 0x44, 8);
if (go->interlace_coding) {
CODE_ADD(c, pict_struct, 4);
if (go->dvd_mode)
CODE_ADD(c, 0x000, 11);
else
CODE_ADD(c, 0x200, 11);
} else {
CODE_ADD(c, 0x3, 4);
CODE_ADD(c, 0x20c, 11);
}
/* Byte-align with zeros */
j = 8 - (CODE_LENGTH(c) % 8);
if (j != 8)
CODE_ADD(c, 0, j);
}
for (i = 0; i < rows; ++i) {
CODE_ADD(c, 1, 24);
CODE_ADD(c, i + 1, 8);
CODE_ADD(c, 0x2, 6);
CODE_ADD(c, 0x1, 1);
CODE_ADD(c, mb_code, mb_len);
if (go->interlace_coding) {
CODE_ADD(c, 0x1, 2);
CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
}
if (frame == BFRAME_BIDIR) {
CODE_ADD(c, 0x3, 2);
if (go->interlace_coding)
CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
}
CODE_ADD(c, 0x3, 2);
for (j = (go->width >> 4) - 2; j >= 33; j -= 33)
CODE_ADD(c, 0x8, 11);
CODE_ADD(c, addrinctab[j][0], addrinctab[j][1]);
CODE_ADD(c, mb_code, mb_len);
if (go->interlace_coding) {
CODE_ADD(c, 0x1, 2);
CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
}
if (frame == BFRAME_BIDIR) {
CODE_ADD(c, 0x3, 2);
if (go->interlace_coding)
CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
}
CODE_ADD(c, 0x3, 2);
/* Byte-align with zeros */
j = 8 - (CODE_LENGTH(c) % 8);
if (j != 8)
CODE_ADD(c, 0, j);
}
i = CODE_LENGTH(c) + 4 * 8;
buf[2] = 0x00;
buf[3] = 0x00;
buf[4] = 0x01;
buf[5] = 0x00;
return i;
}
static int mpeg1_sequence_header(struct go7007 *go, unsigned char *buf, int ext)
{
int i, aspect_ratio, picture_rate;
CODE_GEN(c, buf + 6);
if (go->format == GO7007_FORMAT_MPEG1) {
switch (go->aspect_ratio) {
case GO7007_RATIO_4_3:
aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2;
break;
case GO7007_RATIO_16_9:
aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4;
break;
default:
aspect_ratio = 1;
break;
}
} else {
switch (go->aspect_ratio) {
case GO7007_RATIO_4_3:
aspect_ratio = 2;
break;
case GO7007_RATIO_16_9:
aspect_ratio = 3;
break;
default:
aspect_ratio = 1;
break;
}
}
switch (go->sensor_framerate) {
case 24000:
picture_rate = 1;
break;
case 24024:
picture_rate = 2;
break;
case 25025:
picture_rate = go->interlace_coding ? 6 : 3;
break;
case 30000:
picture_rate = go->interlace_coding ? 7 : 4;
break;
case 30030:
picture_rate = go->interlace_coding ? 8 : 5;
break;
default:
picture_rate = 5; /* 30 fps seems like a reasonable default */
break;
}
CODE_ADD(c, go->width, 12);
CODE_ADD(c, go->height, 12);
CODE_ADD(c, aspect_ratio, 4);
CODE_ADD(c, picture_rate, 4);
CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 20000 : 0x3ffff, 18);
CODE_ADD(c, 1, 1);
CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 112 : 20, 10);
CODE_ADD(c, 0, 3);
/* Byte-align with zeros */
i = 8 - (CODE_LENGTH(c) % 8);
if (i != 8)
CODE_ADD(c, 0, i);
if (go->format == GO7007_FORMAT_MPEG2) {
CODE_ADD(c, 0x1, 24);
CODE_ADD(c, 0xb5, 8);
CODE_ADD(c, 0x148, 12);
if (go->interlace_coding)
CODE_ADD(c, 0x20001, 20);
else
CODE_ADD(c, 0xa0001, 20);
CODE_ADD(c, 0, 16);
/* Byte-align with zeros */
i = 8 - (CODE_LENGTH(c) % 8);
if (i != 8)
CODE_ADD(c, 0, i);
if (ext) {
CODE_ADD(c, 0x1, 24);
CODE_ADD(c, 0xb52, 12);
CODE_ADD(c, go->standard == GO7007_STD_NTSC ? 2 : 1, 3);
CODE_ADD(c, 0x105, 9);
CODE_ADD(c, 0x505, 16);
CODE_ADD(c, go->width, 14);
CODE_ADD(c, 1, 1);
CODE_ADD(c, go->height, 14);
/* Byte-align with zeros */
i = 8 - (CODE_LENGTH(c) % 8);
if (i != 8)
CODE_ADD(c, 0, i);
}
}
i = CODE_LENGTH(c) + 4 * 8;
buf[0] = i & 0xff;
buf[1] = i >> 8;
buf[2] = 0x00;
buf[3] = 0x00;
buf[4] = 0x01;
buf[5] = 0xb3;
return i;
}
static int gen_mpeg1hdr_to_package(struct go7007 *go,
u16 *code, int space, int *framelen)
{
u8 *buf;
u16 mem = 0x3e00;
unsigned int addr = 0x19;
int i, off = 0, chunk;
buf = kmalloc(5120, GFP_KERNEL);
if (buf == NULL) {
printk(KERN_ERR "go7007: unable to allocate 5120 bytes for "
"firmware construction\n");
return -1;
}
memset(buf, 0, 5120);
framelen[0] = mpeg1_frame_header(go, buf, 0, 1, PFRAME);
if (go->interlace_coding)
framelen[0] += mpeg1_frame_header(go, buf + framelen[0] / 8,
0, 2, PFRAME);
buf[0] = framelen[0] & 0xff;
buf[1] = framelen[0] >> 8;
i = 368;
framelen[1] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_PRE);
if (go->interlace_coding)
framelen[1] += mpeg1_frame_header(go, buf + i + framelen[1] / 8,
0, 2, BFRAME_PRE);
buf[i] = framelen[1] & 0xff;
buf[i + 1] = framelen[1] >> 8;
i += 1632;
framelen[2] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_POST);
if (go->interlace_coding)
framelen[2] += mpeg1_frame_header(go, buf + i + framelen[2] / 8,
0, 2, BFRAME_POST);
buf[i] = framelen[2] & 0xff;
buf[i + 1] = framelen[2] >> 8;
i += 1432;
framelen[3] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_BIDIR);
if (go->interlace_coding)
framelen[3] += mpeg1_frame_header(go, buf + i + framelen[3] / 8,
0, 2, BFRAME_BIDIR);
buf[i] = framelen[3] & 0xff;
buf[i + 1] = framelen[3] >> 8;
i += 1632 + 16;
mpeg1_sequence_header(go, buf + i, 0);
i += 40;
for (i = 0; i < 5120; i += chunk * 2) {
if (space - off < 32) {
off = -1;
goto done;
}
code[off + 1] = __cpu_to_le16(0x8000 | mem);
chunk = 28;
if (mem + chunk > 0x4000)
chunk = 0x4000 - mem;
if (i + 2 * chunk > 5120)
chunk = (5120 - i) / 2;
if (chunk < 28) {
code[off] = __cpu_to_le16(0x4000 | chunk);
code[off + 31] = __cpu_to_le16(addr);
if (mem + chunk == 0x4000) {
mem = 0x3e00;
++addr;
}
} else {
code[off] = __cpu_to_le16(0x1000 | 28);
code[off + 31] = 0;
mem += 28;
}
memcpy(&code[off + 2], buf + i, chunk * 2);
off += 32;
}
done:
kfree(buf);
return off;
}
static int vti_bitlen(struct go7007 *go)
{
unsigned int i, max_time_incr = go->sensor_framerate / go->fps_scale;
for (i = 31; (max_time_incr & ((1 << i) - 1)) == max_time_incr; --i);
return i + 1;
}
static int mpeg4_frame_header(struct go7007 *go, unsigned char *buf,
int modulo, enum mpeg_frame_type frame)
{
int i;
CODE_GEN(c, buf + 6);
int mb_count = (go->width >> 4) * (go->height >> 4);
CODE_ADD(c, frame == PFRAME ? 0x1 : 0x2, 2);
if (modulo)
CODE_ADD(c, 0x1, 1);
CODE_ADD(c, 0x1, 2);
CODE_ADD(c, 0, vti_bitlen(go));
CODE_ADD(c, 0x3, 2);
if (frame == PFRAME)
CODE_ADD(c, 0, 1);
CODE_ADD(c, 0xc, 11);
if (frame != PFRAME)
CODE_ADD(c, 0x4, 3);
if (frame != BFRAME_EMPTY) {
for (i = 0; i < mb_count; ++i) {
switch (frame) {
case PFRAME:
CODE_ADD(c, 0x1, 1);
break;
case BFRAME_PRE:
CODE_ADD(c, 0x47, 8);
break;
case BFRAME_POST:
CODE_ADD(c, 0x27, 7);
break;
case BFRAME_BIDIR:
CODE_ADD(c, 0x5f, 8);
break;
case BFRAME_EMPTY: /* keep compiler quiet */
break;
}
}
}
/* Byte-align with a zero followed by ones */
i = 8 - (CODE_LENGTH(c) % 8);
CODE_ADD(c, 0, 1);
CODE_ADD(c, (1 << (i - 1)) - 1, i - 1);
i = CODE_LENGTH(c) + 4 * 8;
buf[0] = i & 0xff;
buf[1] = i >> 8;
buf[2] = 0x00;
buf[3] = 0x00;
buf[4] = 0x01;
buf[5] = 0xb6;
return i;
}
static int mpeg4_sequence_header(struct go7007 *go, unsigned char *buf, int ext)
{
const unsigned char head[] = { 0x00, 0x00, 0x01, 0xb0, go->pali,
0x00, 0x00, 0x01, 0xb5, 0x09,
0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x20, };
int i, aspect_ratio;
int fps = go->sensor_framerate / go->fps_scale;
CODE_GEN(c, buf + 2 + sizeof(head));
switch (go->aspect_ratio) {
case GO7007_RATIO_4_3:
aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2;
break;
case GO7007_RATIO_16_9:
aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4;
break;
default:
aspect_ratio = 1;
break;
}
memcpy(buf + 2, head, sizeof(head));
CODE_ADD(c, 0x191, 17);
CODE_ADD(c, aspect_ratio, 4);
CODE_ADD(c, 0x1, 4);
CODE_ADD(c, fps, 16);
CODE_ADD(c, 0x3, 2);
CODE_ADD(c, 1001, vti_bitlen(go));
CODE_ADD(c, 1, 1);
CODE_ADD(c, go->width, 13);
CODE_ADD(c, 1, 1);
CODE_ADD(c, go->height, 13);
CODE_ADD(c, 0x2830, 14);
/* Byte-align */
i = 8 - (CODE_LENGTH(c) % 8);
CODE_ADD(c, 0, 1);
CODE_ADD(c, (1 << (i - 1)) - 1, i - 1);
i = CODE_LENGTH(c) + sizeof(head) * 8;
buf[0] = i & 0xff;
buf[1] = i >> 8;
return i;
}
static int gen_mpeg4hdr_to_package(struct go7007 *go,
u16 *code, int space, int *framelen)
{
u8 *buf;
u16 mem = 0x3e00;
unsigned int addr = 0x19;
int i, off = 0, chunk;
buf = kmalloc(5120, GFP_KERNEL);
if (buf == NULL) {
printk(KERN_ERR "go7007: unable to allocate 5120 bytes for "
"firmware construction\n");
return -1;
}
memset(buf, 0, 5120);
framelen[0] = mpeg4_frame_header(go, buf, 0, PFRAME);
i = 368;
framelen[1] = mpeg4_frame_header(go, buf + i, 0, BFRAME_PRE);
i += 1632;
framelen[2] = mpeg4_frame_header(go, buf + i, 0, BFRAME_POST);
i += 1432;
framelen[3] = mpeg4_frame_header(go, buf + i, 0, BFRAME_BIDIR);
i += 1632;
mpeg4_frame_header(go, buf + i, 0, BFRAME_EMPTY);
i += 16;
mpeg4_sequence_header(go, buf + i, 0);
i += 40;
for (i = 0; i < 5120; i += chunk * 2) {
if (space - off < 32) {
off = -1;
goto done;
}
code[off + 1] = __cpu_to_le16(0x8000 | mem);
chunk = 28;
if (mem + chunk > 0x4000)
chunk = 0x4000 - mem;
if (i + 2 * chunk > 5120)
chunk = (5120 - i) / 2;
if (chunk < 28) {
code[off] = __cpu_to_le16(0x4000 | chunk);
code[off + 31] = __cpu_to_le16(addr);
if (mem + chunk == 0x4000) {
mem = 0x3e00;
++addr;
}
} else {
code[off] = __cpu_to_le16(0x1000 | 28);
code[off + 31] = 0;
mem += 28;
}
memcpy(&code[off + 2], buf + i, chunk * 2);
off += 32;
}
mem = 0x3e00;
addr = go->ipb ? 0x14f9 : 0x0af9;
memset(buf, 0, 5120);
framelen[4] = mpeg4_frame_header(go, buf, 1, PFRAME);
i = 368;
framelen[5] = mpeg4_frame_header(go, buf + i, 1, BFRAME_PRE);
i += 1632;
framelen[6] = mpeg4_frame_header(go, buf + i, 1, BFRAME_POST);
i += 1432;
framelen[7] = mpeg4_frame_header(go, buf + i, 1, BFRAME_BIDIR);
i += 1632;
mpeg4_frame_header(go, buf + i, 1, BFRAME_EMPTY);
i += 16;
for (i = 0; i < 5120; i += chunk * 2) {
if (space - off < 32) {
off = -1;
goto done;
}
code[off + 1] = __cpu_to_le16(0x8000 | mem);
chunk = 28;
if (mem + chunk > 0x4000)
chunk = 0x4000 - mem;
if (i + 2 * chunk > 5120)
chunk = (5120 - i) / 2;
if (chunk < 28) {
code[off] = __cpu_to_le16(0x4000 | chunk);
code[off + 31] = __cpu_to_le16(addr);
if (mem + chunk == 0x4000) {
mem = 0x3e00;
++addr;
}
} else {
code[off] = __cpu_to_le16(0x1000 | 28);
code[off + 31] = 0;
mem += 28;
}
memcpy(&code[off + 2], buf + i, chunk * 2);
off += 32;
}
done:
kfree(buf);
return off;
}
static int brctrl_to_package(struct go7007 *go,
u16 *code, int space, int *framelen)
{
int converge_speed = 0;
int lambda = (go->format == GO7007_FORMAT_MJPEG || go->dvd_mode) ?
100 : 0;
int peak_rate = 6 * go->bitrate / 5;
int vbv_buffer = go->format == GO7007_FORMAT_MJPEG ?
go->bitrate :
(go->dvd_mode ? 900000 : peak_rate);
int fps = go->sensor_framerate / go->fps_scale;
int q = 0;
/* Bizarre math below depends on rounding errors in division */
u32 sgop_expt_addr = go->bitrate / 32 * (go->ipb ? 3 : 1) * 1001 / fps;
u32 sgop_peak_addr = peak_rate / 32 * 1001 / fps;
u32 total_expt_addr = go->bitrate / 32 * 1000 / fps * (fps / 1000);
u32 vbv_alert_addr = vbv_buffer * 3 / (4 * 32);
u32 cplx[] = {
q > 0 ? sgop_expt_addr * q :
2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
q > 0 ? sgop_expt_addr * q :
2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
q > 0 ? sgop_expt_addr * q :
2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
q > 0 ? sgop_expt_addr * q :
2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
};
u32 calc_q = q > 0 ? q : cplx[0] / sgop_expt_addr;
u16 pack[] = {
0x200e, 0x0000,
0xBF20, go->ipb ? converge_speed_ipb[converge_speed]
: converge_speed_ip[converge_speed],
0xBF21, go->ipb ? 2 : 0,
0xBF22, go->ipb ? LAMBDA_table[0][lambda / 2 + 50]
: 32767,
0xBF23, go->ipb ? LAMBDA_table[1][lambda] : 32767,
0xBF24, 32767,
0xBF25, lambda > 99 ? 32767 : LAMBDA_table[3][lambda],
0xBF26, sgop_expt_addr & 0x0000FFFF,
0xBF27, sgop_expt_addr >> 16,
0xBF28, sgop_peak_addr & 0x0000FFFF,
0xBF29, sgop_peak_addr >> 16,
0xBF2A, vbv_alert_addr & 0x0000FFFF,
0xBF2B, vbv_alert_addr >> 16,
0xBF2C, 0,
0xBF2D, 0,
0, 0,
0x200e, 0x0000,
0xBF2E, vbv_alert_addr & 0x0000FFFF,
0xBF2F, vbv_alert_addr >> 16,
0xBF30, cplx[0] & 0x0000FFFF,
0xBF31, cplx[0] >> 16,
0xBF32, cplx[1] & 0x0000FFFF,
0xBF33, cplx[1] >> 16,
0xBF34, cplx[2] & 0x0000FFFF,
0xBF35, cplx[2] >> 16,
0xBF36, cplx[3] & 0x0000FFFF,
0xBF37, cplx[3] >> 16,
0xBF38, 0,
0xBF39, 0,
0xBF3A, total_expt_addr & 0x0000FFFF,
0xBF3B, total_expt_addr >> 16,
0, 0,
0x200e, 0x0000,
0xBF3C, total_expt_addr & 0x0000FFFF,
0xBF3D, total_expt_addr >> 16,
0xBF3E, 0,
0xBF3F, 0,
0xBF48, 0,
0xBF49, 0,
0xBF4A, calc_q < 4 ? 4 : (calc_q > 124 ? 124 : calc_q),
0xBF4B, 4,
0xBF4C, 0,
0xBF4D, 0,
0xBF4E, 0,
0xBF4F, 0,
0xBF50, 0,
0xBF51, 0,
0, 0,
0x200e, 0x0000,
0xBF40, sgop_expt_addr & 0x0000FFFF,
0xBF41, sgop_expt_addr >> 16,
0xBF42, 0,
0xBF43, 0,
0xBF44, 0,
0xBF45, 0,
0xBF46, (go->width >> 4) * (go->height >> 4),
0xBF47, 0,
0xBF64, 0,
0xBF65, 0,
0xBF18, framelen[4],
0xBF19, framelen[5],
0xBF1A, framelen[6],
0xBF1B, framelen[7],
0, 0,
#if 0 /* Remove once we don't care about matching */
0x200e, 0x0000,
0xBF56, 4,
0xBF57, 0,
0xBF58, 5,
0xBF59, 0,
0xBF5A, 6,
0xBF5B, 0,
0xBF5C, 8,
0xBF5D, 0,
0xBF5E, 1,
0xBF5F, 0,
0xBF60, 1,
0xBF61, 0,
0xBF62, 0,
0xBF63, 0,
0, 0,
#else
0x2008, 0x0000,
0xBF56, 4,
0xBF57, 0,
0xBF58, 5,
0xBF59, 0,
0xBF5A, 6,
0xBF5B, 0,
0xBF5C, 8,
0xBF5D, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
#endif
0x200e, 0x0000,
0xBF10, 0,
0xBF11, 0,
0xBF12, 0,
0xBF13, 0,
0xBF14, 0,
0xBF15, 0,
0xBF16, 0,
0xBF17, 0,
0xBF7E, 0,
0xBF7F, 1,
0xBF52, framelen[0],
0xBF53, framelen[1],
0xBF54, framelen[2],
0xBF55, framelen[3],
0, 0,
};
return copy_packages(code, pack, 6, space);
}
static int config_package(struct go7007 *go, u16 *code, int space)
{
int fps = go->sensor_framerate / go->fps_scale / 1000;
int rows = go->interlace_coding ? go->height / 32 : go->height / 16;
int brc_window_size = fps;
int q_min = 2, q_max = 31;
int THACCoeffSet0 = 0;
u16 pack[] = {
0x200e, 0x0000,
0xc002, 0x14b4,
0xc003, 0x28b4,
0xc004, 0x3c5a,
0xdc05, 0x2a77,
0xc6c3, go->format == GO7007_FORMAT_MPEG4 ? 0 :
(go->format == GO7007_FORMAT_H263 ? 0 : 1),
0xc680, go->format == GO7007_FORMAT_MPEG4 ? 0xf1 :
(go->format == GO7007_FORMAT_H263 ? 0x61 :
0xd3),
0xc780, 0x0140,
0xe009, 0x0001,
0xc60f, 0x0008,
0xd4ff, 0x0002,
0xe403, 2340,
0xe406, 75,
0xd411, 0x0001,
0xd410, 0xa1d6,
0x0001, 0x2801,
0x200d, 0x0000,
0xe402, 0x018b,
0xe401, 0x8b01,
0xd472, (go->board_info->sensor_flags &
GO7007_SENSOR_TV) &&
(!go->interlace_coding) ?
0x01b0 : 0x0170,
0xd475, (go->board_info->sensor_flags &
GO7007_SENSOR_TV) &&
(!go->interlace_coding) ?
0x0008 : 0x0009,
0xc404, go->interlace_coding ? 0x44 :
(go->format == GO7007_FORMAT_MPEG4 ? 0x11 :
(go->format == GO7007_FORMAT_MPEG1 ? 0x02 :
(go->format == GO7007_FORMAT_MPEG2 ? 0x04 :
(go->format == GO7007_FORMAT_H263 ? 0x08 :
0x20)))),
0xbf0a, (go->format == GO7007_FORMAT_MPEG4 ? 8 :
(go->format == GO7007_FORMAT_MPEG1 ? 1 :
(go->format == GO7007_FORMAT_MPEG2 ? 2 :
(go->format == GO7007_FORMAT_H263 ? 4 : 16)))) |
((go->repeat_seqhead ? 1 : 0) << 6) |
((go->dvd_mode ? 1 : 0) << 9) |
((go->gop_header_enable ? 1 : 0) << 10),
0xbf0b, 0,
0xdd5a, go->ipb ? 0x14 : 0x0a,
0xbf0c, 0,
0xbf0d, 0,
0xc683, THACCoeffSet0,
0xc40a, (go->width << 4) | rows,
0xe01a, go->board_info->hpi_buffer_cap,
0, 0,
0, 0,
0x2008, 0,
0xe402, 0x88,
0xe401, 0x8f01,
0xbf6a, 0,
0xbf6b, 0,
0xbf6c, 0,
0xbf6d, 0,
0xbf6e, 0,
0xbf6f, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0x200e, 0,
0xbf66, brc_window_size,
0xbf67, 0,
0xbf68, q_min,
0xbf69, q_max,
0xbfe0, 0,
0xbfe1, 0,
0xbfe2, 0,
0xbfe3, go->ipb ? 3 : 1,
0xc031, go->board_info->sensor_flags &
GO7007_SENSOR_VBI ? 1 : 0,
0xc01c, 0x1f,
0xdd8c, 0x15,
0xdd94, 0x15,
0xdd88, go->ipb ? 0x1401 : 0x0a01,
0xdd90, go->ipb ? 0x1401 : 0x0a01,
0, 0,
0x200e, 0,
0xbfe4, 0,
0xbfe5, 0,
0xbfe6, 0,
0xbfe7, fps << 8,
0xbfe8, 0x3a00,
0xbfe9, 0,
0xbfea, 0,
0xbfeb, 0,
0xbfec, (go->interlace_coding ? 1 << 15 : 0) |
(go->modet_enable ? 0xa : 0) |
(go->board_info->sensor_flags &
GO7007_SENSOR_VBI ? 1 : 0),
0xbfed, 0,
0xbfee, 0,
0xbfef, 0,
0xbff0, go->board_info->sensor_flags &
GO7007_SENSOR_TV ? 0xf060 : 0xb060,
0xbff1, 0,
0, 0,
};
return copy_packages(code, pack, 5, space);
}
static int seqhead_to_package(struct go7007 *go, u16 *code, int space,
int (*sequence_header_func)(struct go7007 *go,
unsigned char *buf, int ext))
{
int vop_time_increment_bitlength = vti_bitlen(go);
int fps = go->sensor_framerate / go->fps_scale *
(go->interlace_coding ? 2 : 1);
unsigned char buf[40] = { };
int len = sequence_header_func(go, buf, 1);
u16 pack[] = {
0x2006, 0,
0xbf08, fps,
0xbf09, 0,
0xbff2, vop_time_increment_bitlength,
0xbff3, (1 << vop_time_increment_bitlength) - 1,
0xbfe6, 0,
0xbfe7, (fps / 1000) << 8,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0x2007, 0,
0xc800, buf[2] << 8 | buf[3],
0xc801, buf[4] << 8 | buf[5],
0xc802, buf[6] << 8 | buf[7],
0xc803, buf[8] << 8 | buf[9],
0xc406, 64,
0xc407, len - 64,
0xc61b, 1,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0x200e, 0,
0xc808, buf[10] << 8 | buf[11],
0xc809, buf[12] << 8 | buf[13],
0xc80a, buf[14] << 8 | buf[15],
0xc80b, buf[16] << 8 | buf[17],
0xc80c, buf[18] << 8 | buf[19],
0xc80d, buf[20] << 8 | buf[21],
0xc80e, buf[22] << 8 | buf[23],
0xc80f, buf[24] << 8 | buf[25],
0xc810, buf[26] << 8 | buf[27],
0xc811, buf[28] << 8 | buf[29],
0xc812, buf[30] << 8 | buf[31],
0xc813, buf[32] << 8 | buf[33],
0xc814, buf[34] << 8 | buf[35],
0xc815, buf[36] << 8 | buf[37],
0, 0,
0, 0,
0, 0,
};
return copy_packages(code, pack, 3, space);
}
static int relative_prime(int big, int little)
{
int remainder;
while (little != 0) {
remainder = big % little;
big = little;
little = remainder;
}
return big;
}
static int avsync_to_package(struct go7007 *go, u16 *code, int space)
{
int arate = go->board_info->audio_rate * 1001 * go->fps_scale;
int ratio = arate / go->sensor_framerate;
int adjratio = ratio * 215 / 100;
int rprime = relative_prime(go->sensor_framerate,
arate % go->sensor_framerate);
int f1 = (arate % go->sensor_framerate) / rprime;
int f2 = (go->sensor_framerate - arate % go->sensor_framerate) / rprime;
u16 pack[] = {
0x200e, 0,
0xbf98, (u16)((-adjratio) & 0xffff),
0xbf99, (u16)((-adjratio) >> 16),
0xbf92, 0,
0xbf93, 0,
0xbff4, f1 > f2 ? f1 : f2,
0xbff5, f1 < f2 ? f1 : f2,
0xbff6, f1 < f2 ? ratio : ratio + 1,
0xbff7, f1 > f2 ? ratio : ratio + 1,
0xbff8, 0,
0xbff9, 0,
0xbffa, adjratio & 0xffff,
0xbffb, adjratio >> 16,
0xbf94, 0,
0xbf95, 0,
0, 0,
};
return copy_packages(code, pack, 1, space);
}
static int final_package(struct go7007 *go, u16 *code, int space)
{
int rows = go->interlace_coding ? go->height / 32 : go->height / 16;
u16 pack[] = {
0x8000,
0,
0,
0,
0,
0,
0,
2,
((go->board_info->sensor_flags & GO7007_SENSOR_TV) &&
(!go->interlace_coding) ?
(1 << 14) | (1 << 9) : 0) |
((go->encoder_subsample ? 1 : 0) << 8) |
(go->board_info->sensor_flags &
GO7007_SENSOR_CONFIG_MASK),
((go->encoder_v_halve ? 1 : 0) << 14) |
(go->encoder_v_halve ? rows << 9 : rows << 8) |
(go->encoder_h_halve ? 1 << 6 : 0) |
(go->encoder_h_halve ? go->width >> 3 : go->width >> 4),
(1 << 15) | (go->encoder_v_offset << 6) |
(1 << 7) | (go->encoder_h_offset >> 2),
(1 << 6),
0,
0,
((go->fps_scale - 1) << 8) |
(go->board_info->sensor_flags & GO7007_SENSOR_TV ?
(1 << 7) : 0) |
0x41,
go->ipb ? 0xd4c : 0x36b,
(rows << 8) | (go->width >> 4),
go->format == GO7007_FORMAT_MPEG4 ? 0x0404 : 0,
(1 << 15) | ((go->interlace_coding ? 1 : 0) << 13) |
((go->closed_gop ? 1 : 0) << 12) |
((go->format == GO7007_FORMAT_MPEG4 ? 1 : 0) << 11) |
/* (1 << 9) | */
((go->ipb ? 3 : 0) << 7) |
((go->modet_enable ? 1 : 0) << 2) |
((go->dvd_mode ? 1 : 0) << 1) | 1,
(go->format == GO7007_FORMAT_MPEG1 ? 0x89a0 :
(go->format == GO7007_FORMAT_MPEG2 ? 0x89a0 :
(go->format == GO7007_FORMAT_MJPEG ? 0x89a0 :
(go->format == GO7007_FORMAT_MPEG4 ? 0x8920 :
(go->format == GO7007_FORMAT_H263 ? 0x8920 : 0))))),
go->ipb ? 0x1f15 : 0x1f0b,
go->ipb ? 0x0015 : 0x000b,
go->ipb ? 0xa800 : 0x5800,
0xffff,
0x0020 + 0x034b * 0,
0x0020 + 0x034b * 1,
0x0020 + 0x034b * 2,
0x0020 + 0x034b * 3,
0x0020 + 0x034b * 4,
0x0020 + 0x034b * 5,
go->ipb ? (go->gop_size / 3) : go->gop_size,
(go->height >> 4) * (go->width >> 4) * 110 / 100,
};
return copy_packages(code, pack, 1, space);
}
static int audio_to_package(struct go7007 *go, u16 *code, int space)
{
int clock_config = ((go->board_info->audio_flags &
GO7007_AUDIO_I2S_MASTER ? 1 : 0) << 11) |
((go->board_info->audio_flags &
GO7007_AUDIO_OKI_MODE ? 1 : 0) << 8) |
(((go->board_info->audio_bclk_div / 4) - 1) << 4) |
(go->board_info->audio_main_div - 1);
u16 pack[] = {
0x200d, 0,
0x9002, 0,
0x9002, 0,
0x9031, 0,
0x9032, 0,
0x9033, 0,
0x9034, 0,
0x9035, 0,
0x9036, 0,
0x9037, 0,
0x9040, 0,
0x9000, clock_config,
0x9001, (go->board_info->audio_flags & 0xffff) |
(1 << 9),
0x9000, ((go->board_info->audio_flags &
GO7007_AUDIO_I2S_MASTER ?
1 : 0) << 10) |
clock_config,
0, 0,
0, 0,
0x2005, 0,
0x9041, 0,
0x9042, 256,
0x9043, 0,
0x9044, 16,
0x9045, 16,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
};
return copy_packages(code, pack, 2, space);
}
static int modet_to_package(struct go7007 *go, u16 *code, int space)
{
int ret, mb, i, addr, cnt = 0;
u16 pack[32];
u16 thresholds[] = {
0x200e, 0,
0xbf82, go->modet[0].pixel_threshold,
0xbf83, go->modet[1].pixel_threshold,
0xbf84, go->modet[2].pixel_threshold,
0xbf85, go->modet[3].pixel_threshold,
0xbf86, go->modet[0].motion_threshold,
0xbf87, go->modet[1].motion_threshold,
0xbf88, go->modet[2].motion_threshold,
0xbf89, go->modet[3].motion_threshold,
0xbf8a, go->modet[0].mb_threshold,
0xbf8b, go->modet[1].mb_threshold,
0xbf8c, go->modet[2].mb_threshold,
0xbf8d, go->modet[3].mb_threshold,
0xbf8e, 0,
0xbf8f, 0,
0, 0,
};
ret = copy_packages(code, thresholds, 1, space);
if (ret < 0)
return -1;
cnt += ret;
addr = 0xbac0;
memset(pack, 0, 64);
i = 0;
for (mb = 0; mb < 1624; ++mb) {
pack[i * 2 + 3] <<= 2;
pack[i * 2 + 3] |= go->modet_map[mb];
if (mb % 8 != 7)
continue;
pack[i * 2 + 2] = addr++;
++i;
if (i == 10 || mb == 1623) {
pack[0] = 0x2000 | i;
ret = copy_packages(code + cnt, pack, 1, space - cnt);
if (ret < 0)
return -1;
cnt += ret;
i = 0;
memset(pack, 0, 64);
}
pack[i * 2 + 3] = 0;
}
memset(pack, 0, 64);
i = 0;
for (addr = 0xbb90; addr < 0xbbfa; ++addr) {
pack[i * 2 + 2] = addr;
pack[i * 2 + 3] = 0;
++i;
if (i == 10 || addr == 0xbbf9) {
pack[0] = 0x2000 | i;
ret = copy_packages(code + cnt, pack, 1, space - cnt);
if (ret < 0)
return -1;
cnt += ret;
i = 0;
memset(pack, 0, 64);
}
}
return cnt;
}
static int do_special(struct go7007 *go, u16 type, u16 *code, int space,
int *framelen)
{
switch (type) {
case SPECIAL_FRM_HEAD:
switch (go->format) {
case GO7007_FORMAT_MJPEG:
return gen_mjpeghdr_to_package(go, code, space);
case GO7007_FORMAT_MPEG1:
case GO7007_FORMAT_MPEG2:
return gen_mpeg1hdr_to_package(go, code, space,
framelen);
case GO7007_FORMAT_MPEG4:
return gen_mpeg4hdr_to_package(go, code, space,
framelen);
}
case SPECIAL_BRC_CTRL:
return brctrl_to_package(go, code, space, framelen);
case SPECIAL_CONFIG:
return config_package(go, code, space);
case SPECIAL_SEQHEAD:
switch (go->format) {
case GO7007_FORMAT_MPEG1:
case GO7007_FORMAT_MPEG2:
return seqhead_to_package(go, code, space,
mpeg1_sequence_header);
case GO7007_FORMAT_MPEG4:
return seqhead_to_package(go, code, space,
mpeg4_sequence_header);
default:
return 0;
}
case SPECIAL_AV_SYNC:
return avsync_to_package(go, code, space);
case SPECIAL_FINAL:
return final_package(go, code, space);
case SPECIAL_AUDIO:
return audio_to_package(go, code, space);
case SPECIAL_MODET:
return modet_to_package(go, code, space);
}
printk(KERN_ERR
"go7007: firmware file contains unsupported feature %04x\n",
type);
return -1;
}
int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen)
{
const struct firmware *fw_entry;
u16 *code, *src;
int framelen[8] = { }; /* holds the lengths of empty frame templates */
int codespace = 64 * 1024, i = 0, srclen, chunk_len, chunk_flags;
int mode_flag;
int ret;
switch (go->format) {
case GO7007_FORMAT_MJPEG:
mode_flag = FLAG_MODE_MJPEG;
break;
case GO7007_FORMAT_MPEG1:
mode_flag = FLAG_MODE_MPEG1;
break;
case GO7007_FORMAT_MPEG2:
mode_flag = FLAG_MODE_MPEG2;
break;
case GO7007_FORMAT_MPEG4:
mode_flag = FLAG_MODE_MPEG4;
break;
default:
return -1;
}
if (request_firmware(&fw_entry, go->board_info->firmware, go->dev)) {
printk(KERN_ERR
"go7007: unable to load firmware from file \"%s\"\n",
go->board_info->firmware);
return -1;
}
code = kmalloc(codespace * 2, GFP_KERNEL);
if (code == NULL) {
printk(KERN_ERR "go7007: unable to allocate %d bytes for "
"firmware construction\n", codespace * 2);
goto fw_failed;
}
memset(code, 0, codespace * 2);
src = (u16 *)fw_entry->data;
srclen = fw_entry->size / 2;
while (srclen >= 2) {
chunk_flags = __le16_to_cpu(src[0]);
chunk_len = __le16_to_cpu(src[1]);
if (chunk_len + 2 > srclen) {
printk(KERN_ERR "go7007: firmware file \"%s\" "
"appears to be corrupted\n",
go->board_info->firmware);
goto fw_failed;
}
if (chunk_flags & mode_flag) {
if (chunk_flags & FLAG_SPECIAL) {
ret = do_special(go, __le16_to_cpu(src[2]),
&code[i], codespace - i, framelen);
if (ret < 0) {
printk(KERN_ERR "go7007: insufficient "
"memory for firmware "
"construction\n");
goto fw_failed;
}
i += ret;
} else {
if (codespace - i < chunk_len) {
printk(KERN_ERR "go7007: insufficient "
"memory for firmware "
"construction\n");
goto fw_failed;
}
memcpy(&code[i], &src[2], chunk_len * 2);
i += chunk_len;
}
}
srclen -= chunk_len + 2;
src += chunk_len + 2;
}
release_firmware(fw_entry);
*fw = (u8 *)code;
*fwlen = i * 2;
return 0;
fw_failed:
kfree(code);
release_firmware(fw_entry);
return -1;
}
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/unistd.h>
#include <linux/time.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <asm/system.h>
#include "go7007-priv.h"
#include "wis-i2c.h"
/************** Registration interface for I2C client drivers **************/
/* Since there's no way to auto-probe the I2C devices connected to the I2C
* bus on the go7007, we have this silly little registration system that
* client drivers can use to register their I2C driver ID and their
* detect_client function (the one that's normally passed to i2c_probe).
*
* When a new go7007 device is connected, we can look up in a board info
* table by the USB or PCI vendor/product/revision ID to determine
* which I2C client module to load. The client driver module will register
* itself here, and then we can call the registered detect_client function
* to force-load a new client at the address listed in the board info table.
*
* Really the I2C subsystem should have a way to force-load I2C client
* drivers when we have a priori knowledge of what's on the bus, especially
* since the existing I2C auto-probe mechanism is so hokey, but we'll use
* our own mechanism for the time being. */
struct wis_i2c_client_driver {
unsigned int id;
found_proc found_proc;
struct list_head list;
};
static LIST_HEAD(i2c_client_drivers);
static DECLARE_MUTEX(i2c_client_driver_list_lock);
/* Client drivers register here by their I2C driver ID */
int wis_i2c_add_driver(unsigned int id, found_proc found_proc)
{
struct wis_i2c_client_driver *driver;
driver = kmalloc(sizeof(struct wis_i2c_client_driver), GFP_KERNEL);
if (driver == NULL)
return -ENOMEM;
driver->id = id;
driver->found_proc = found_proc;
down(&i2c_client_driver_list_lock);
list_add_tail(&driver->list, &i2c_client_drivers);
up(&i2c_client_driver_list_lock);
return 0;
}
EXPORT_SYMBOL(wis_i2c_add_driver);
void wis_i2c_del_driver(found_proc found_proc)
{
struct wis_i2c_client_driver *driver, *next;
down(&i2c_client_driver_list_lock);
list_for_each_entry_safe(driver, next, &i2c_client_drivers, list)
if (driver->found_proc == found_proc) {
list_del(&driver->list);
kfree(driver);
}
up(&i2c_client_driver_list_lock);
}
EXPORT_SYMBOL(wis_i2c_del_driver);
/* The main go7007 driver calls this to instantiate a client by driver
* ID and bus address, which are both stored in the board info table */
int wis_i2c_probe_device(struct i2c_adapter *adapter,
unsigned int id, int addr)
{
struct wis_i2c_client_driver *driver;
int found = 0;
if (addr < 0 || addr > 0x7f)
return -1;
down(&i2c_client_driver_list_lock);
list_for_each_entry(driver, &i2c_client_drivers, list)
if (driver->id == id) {
if (driver->found_proc(adapter, addr, 0) == 0)
found = 1;
break;
}
up(&i2c_client_driver_list_lock);
return found;
}
/********************* Driver for on-board I2C adapter *********************/
/* #define GO7007_I2C_DEBUG */
#define SPI_I2C_ADDR_BASE 0x1400
#define STATUS_REG_ADDR (SPI_I2C_ADDR_BASE + 0x2)
#define I2C_CTRL_REG_ADDR (SPI_I2C_ADDR_BASE + 0x6)
#define I2C_DEV_UP_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x7)
#define I2C_LO_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x8)
#define I2C_DATA_REG_ADDR (SPI_I2C_ADDR_BASE + 0x9)
#define I2C_CLKFREQ_REG_ADDR (SPI_I2C_ADDR_BASE + 0xa)
#define I2C_STATE_MASK 0x0007
#define I2C_READ_READY_MASK 0x0008
/* There is only one I2C port on the TW2804 that feeds all four GO7007 VIPs
* on the Adlink PCI-MPG24, so access is shared between all of them. */
static DECLARE_MUTEX(adlink_mpg24_i2c_lock);
static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read,
u16 command, int flags, u8 *data)
{
int i, ret = -1;
u16 val;
if (go->status == STATUS_SHUTDOWN)
return -1;
#ifdef GO7007_I2C_DEBUG
if (read)
printk(KERN_DEBUG "go7007-i2c: reading 0x%02x on 0x%02x\n",
command, addr);
else
printk(KERN_DEBUG
"go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n",
*data, command, addr);
#endif
down(&go->hw_lock);
if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) {
/* Bridge the I2C port on this GO7007 to the shared bus */
down(&adlink_mpg24_i2c_lock);
go7007_write_addr(go, 0x3c82, 0x0020);
}
/* Wait for I2C adapter to be ready */
for (i = 0; i < 10; ++i) {
if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0)
goto i2c_done;
if (!(val & I2C_STATE_MASK))
break;
msleep(100);
}
if (i == 10) {
printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n");
goto i2c_done;
}
/* Set target register (command) */
go7007_write_addr(go, I2C_CTRL_REG_ADDR, flags);
go7007_write_addr(go, I2C_LO_ADDR_REG_ADDR, command);
/* If we're writing, send the data and target address and we're done */
if (!read) {
go7007_write_addr(go, I2C_DATA_REG_ADDR, *data);
go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR,
(addr << 9) | (command >> 8));
ret = 0;
goto i2c_done;
}
/* Otherwise, we're reading. First clear i2c_rx_data_rdy. */
if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0)
goto i2c_done;
/* Send the target address plus read flag */
go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR,
(addr << 9) | 0x0100 | (command >> 8));
/* Wait for i2c_rx_data_rdy */
for (i = 0; i < 10; ++i) {
if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0)
goto i2c_done;
if (val & I2C_READ_READY_MASK)
break;
msleep(100);
}
if (i == 10) {
printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n");
goto i2c_done;
}
/* Retrieve the read byte */
if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0)
goto i2c_done;
*data = val;
ret = 0;
i2c_done:
if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) {
/* Isolate the I2C port on this GO7007 from the shared bus */
go7007_write_addr(go, 0x3c82, 0x0000);
up(&adlink_mpg24_i2c_lock);
}
up(&go->hw_lock);
return ret;
}
static int go7007_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data)
{
struct go7007 *go = i2c_get_adapdata(adapter);
if (size != I2C_SMBUS_BYTE_DATA)
return -1;
return go7007_i2c_xfer(go, addr, read_write == I2C_SMBUS_READ, command,
flags & I2C_CLIENT_SCCB ? 0x10 : 0x00, &data->byte);
}
/* VERY LIMITED I2C master xfer function -- only needed because the
* SMBus functions only support 8-bit commands and the SAA7135 uses
* 16-bit commands. The I2C interface on the GO7007, as limited as
* it is, does support this mode. */
static int go7007_i2c_master_xfer(struct i2c_adapter *adapter,
struct i2c_msg msgs[], int num)
{
struct go7007 *go = i2c_get_adapdata(adapter);
int i;
for (i = 0; i < num; ++i) {
/* We can only do two things here -- write three bytes, or
* write two bytes and read one byte. */
if (msgs[i].len == 2) {
if (i + 1 == num || msgs[i].addr != msgs[i + 1].addr ||
(msgs[i].flags & I2C_M_RD) ||
!(msgs[i + 1].flags & I2C_M_RD) ||
msgs[i + 1].len != 1)
return -1;
if (go7007_i2c_xfer(go, msgs[i].addr, 1,
(msgs[i].buf[0] << 8) | msgs[i].buf[1],
0x01, &msgs[i + 1].buf[0]) < 0)
return -1;
++i;
} else if (msgs[i].len == 3) {
if (msgs[i].flags & I2C_M_RD)
return -1;
if (msgs[i].len != 3)
return -1;
if (go7007_i2c_xfer(go, msgs[i].addr, 0,
(msgs[i].buf[0] << 8) | msgs[i].buf[1],
0x01, &msgs[i].buf[2]) < 0)
return -1;
} else
return -1;
}
return 0;
}
static u32 go7007_functionality(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_BYTE_DATA;
}
static struct i2c_algorithm go7007_algo = {
.smbus_xfer = go7007_smbus_xfer,
.master_xfer = go7007_i2c_master_xfer,
.functionality = go7007_functionality,
};
static struct i2c_adapter go7007_adap_templ = {
.owner = THIS_MODULE,
.class = I2C_CLASS_TV_ANALOG,
.name = "WIS GO7007SB",
.id = I2C_ALGO_GO7007,
.algo = &go7007_algo,
};
int go7007_i2c_init(struct go7007 *go)
{
memcpy(&go->i2c_adapter, &go7007_adap_templ,
sizeof(go7007_adap_templ));
go->i2c_adapter.dev.parent = go->dev;
i2c_set_adapdata(&go->i2c_adapter, go);
if (i2c_add_adapter(&go->i2c_adapter) < 0) {
printk(KERN_ERR
"go7007-i2c: error: i2c_add_adapter failed\n");
return -1;
}
return 0;
}
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
/*
* This is the private include file for the go7007 driver. It should not
* be included by anybody but the driver itself, and especially not by
* user-space applications.
*/
struct go7007;
/* IDs to activate board-specific support code */
#define GO7007_BOARDID_MATRIX_II 0
#define GO7007_BOARDID_MATRIX_RELOAD 1
#define GO7007_BOARDID_STAR_TREK 2
#define GO7007_BOARDID_PCI_VOYAGER 3
#define GO7007_BOARDID_XMEN 4
#define GO7007_BOARDID_XMEN_II 5
#define GO7007_BOARDID_XMEN_III 6
#define GO7007_BOARDID_MATRIX_REV 7
#define GO7007_BOARDID_PX_M402U 16
#define GO7007_BOARDID_PX_TV402U_ANY 17 /* need to check tuner model */
#define GO7007_BOARDID_PX_TV402U_NA 18 /* detected NTSC tuner */
#define GO7007_BOARDID_PX_TV402U_EU 19 /* detected PAL tuner */
#define GO7007_BOARDID_PX_TV402U_JP 20 /* detected NTSC-J tuner */
#define GO7007_BOARDID_LIFEVIEW_LR192 21 /* TV Walker Ultra */
#define GO7007_BOARDID_ENDURA 22
#define GO7007_BOARDID_ADLINK_MPG24 23
/* Various characteristics of each board */
#define GO7007_BOARD_HAS_AUDIO (1<<0)
#define GO7007_BOARD_USE_ONBOARD_I2C (1<<1)
#define GO7007_BOARD_HAS_TUNER (1<<2)
/* Characteristics of sensor devices */
#define GO7007_SENSOR_VALID_POLAR (1<<0)
#define GO7007_SENSOR_HREF_POLAR (1<<1)
#define GO7007_SENSOR_VREF_POLAR (1<<2)
#define GO7007_SENSOR_FIELD_ID_POLAR (1<<3)
#define GO7007_SENSOR_BIT_WIDTH (1<<4)
#define GO7007_SENSOR_VALID_ENABLE (1<<5)
#define GO7007_SENSOR_656 (1<<6)
#define GO7007_SENSOR_CONFIG_MASK 0x7f
#define GO7007_SENSOR_TV (1<<7)
#define GO7007_SENSOR_VBI (1<<8)
#define GO7007_SENSOR_SCALING (1<<9)
/* Characteristics of audio sensor devices */
#define GO7007_AUDIO_I2S_MODE_1 (1)
#define GO7007_AUDIO_I2S_MODE_2 (2)
#define GO7007_AUDIO_I2S_MODE_3 (3)
#define GO7007_AUDIO_BCLK_POLAR (1<<2)
#define GO7007_AUDIO_WORD_14 (14<<4)
#define GO7007_AUDIO_WORD_16 (16<<4)
#define GO7007_AUDIO_ONE_CHANNEL (1<<11)
#define GO7007_AUDIO_I2S_MASTER (1<<16)
#define GO7007_AUDIO_OKI_MODE (1<<17)
struct go7007_board_info {
char *firmware;
unsigned int flags;
int hpi_buffer_cap;
unsigned int sensor_flags;
int sensor_width;
int sensor_height;
int sensor_framerate;
int sensor_h_offset;
int sensor_v_offset;
unsigned int audio_flags;
int audio_rate;
int audio_bclk_div;
int audio_main_div;
int num_i2c_devs;
struct {
int id;
int addr;
} i2c_devs[4];
int num_inputs;
struct {
int video_input;
int audio_input;
char *name;
} inputs[4];
};
struct go7007_hpi_ops {
int (*interface_reset)(struct go7007 *go);
int (*write_interrupt)(struct go7007 *go, int addr, int data);
int (*read_interrupt)(struct go7007 *go);
int (*stream_start)(struct go7007 *go);
int (*stream_stop)(struct go7007 *go);
int (*send_firmware)(struct go7007 *go, u8 *data, int len);
};
/* The video buffer size must be a multiple of PAGE_SIZE */
#define GO7007_BUF_PAGES (128 * 1024 / PAGE_SIZE)
#define GO7007_BUF_SIZE (GO7007_BUF_PAGES << PAGE_SHIFT)
struct go7007_buffer {
struct go7007 *go; /* Reverse reference for VMA ops */
int index; /* Reverse reference for DQBUF */
enum { BUF_STATE_IDLE, BUF_STATE_QUEUED, BUF_STATE_DONE } state;
u32 seq;
struct timeval timestamp;
struct list_head stream;
struct page *pages[GO7007_BUF_PAGES + 1]; /* extra for straddling */
unsigned long user_addr;
unsigned int page_count;
unsigned int offset;
unsigned int bytesused;
unsigned int frame_offset;
u32 modet_active;
int mapped;
};
struct go7007_file {
struct go7007 *go;
struct semaphore lock;
int buf_count;
struct go7007_buffer *bufs;
};
#define GO7007_FORMAT_MJPEG 0
#define GO7007_FORMAT_MPEG4 1
#define GO7007_FORMAT_MPEG1 2
#define GO7007_FORMAT_MPEG2 3
#define GO7007_FORMAT_H263 4
#define GO7007_RATIO_1_1 0
#define GO7007_RATIO_4_3 1
#define GO7007_RATIO_16_9 2
enum go7007_parser_state {
STATE_DATA,
STATE_00,
STATE_00_00,
STATE_00_00_01,
STATE_FF,
STATE_VBI_LEN_A,
STATE_VBI_LEN_B,
STATE_MODET_MAP,
STATE_UNPARSED,
};
struct go7007 {
struct device *dev;
struct go7007_board_info *board_info;
unsigned int board_id;
int tuner_type;
int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */
char name[64];
struct video_device *video_dev;
int ref_count;
enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status;
spinlock_t spinlock;
struct semaphore hw_lock;
int streaming;
int in_use;
int audio_enabled;
/* Video input */
int input;
enum { GO7007_STD_NTSC, GO7007_STD_PAL, GO7007_STD_OTHER } standard;
int sensor_framerate;
int width;
int height;
int encoder_h_offset;
int encoder_v_offset;
unsigned int encoder_h_halve:1;
unsigned int encoder_v_halve:1;
unsigned int encoder_subsample:1;
/* Encoder config */
int format;
int bitrate;
int fps_scale;
int pali;
int aspect_ratio;
int gop_size;
unsigned int ipb:1;
unsigned int closed_gop:1;
unsigned int repeat_seqhead:1;
unsigned int seq_header_enable:1;
unsigned int gop_header_enable:1;
unsigned int dvd_mode:1;
unsigned int interlace_coding:1;
/* Motion detection */
unsigned int modet_enable:1;
struct {
unsigned int enable:1;
int pixel_threshold;
int motion_threshold;
int mb_threshold;
} modet[4];
unsigned char modet_map[1624];
unsigned char active_map[216];
/* Video streaming */
struct go7007_buffer *active_buf;
enum go7007_parser_state state;
int parse_length;
u16 modet_word;
int seen_frame;
u32 next_seq;
struct list_head stream;
wait_queue_head_t frame_waitq;
/* Audio streaming */
void (*audio_deliver)(struct go7007 *go, u8 *buf, int length);
void *snd_context;
/* I2C */
int i2c_adapter_online;
struct i2c_adapter i2c_adapter;
/* HPI driver */
struct go7007_hpi_ops *hpi_ops;
void *hpi_context;
int interrupt_available;
wait_queue_head_t interrupt_waitq;
unsigned short interrupt_value;
unsigned short interrupt_data;
};
/* All of these must be called with the hpi_lock semaphore held! */
#define go7007_interface_reset(go) \
((go)->hpi_ops->interface_reset(go))
#define go7007_write_interrupt(go, x, y) \
((go)->hpi_ops->write_interrupt)((go), (x), (y))
#define go7007_stream_start(go) \
((go)->hpi_ops->stream_start(go))
#define go7007_stream_stop(go) \
((go)->hpi_ops->stream_stop(go))
#define go7007_send_firmware(go, x, y) \
((go)->hpi_ops->send_firmware)((go), (x), (y))
#define go7007_write_addr(go, x, y) \
((go)->hpi_ops->write_interrupt)((go), (x)|0x8000, (y))
/* go7007-driver.c */
int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data);
int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data);
int go7007_boot_encoder(struct go7007 *go, int init_i2c);
int go7007_reset_encoder(struct go7007 *go);
int go7007_register_encoder(struct go7007 *go);
int go7007_start_encoder(struct go7007 *go);
void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length);
struct go7007 *go7007_alloc(struct go7007_board_info *board,
struct device *dev);
void go7007_remove(struct go7007 *go);
/* go7007-fw.c */
int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen);
/* go7007-i2c.c */
int go7007_i2c_init(struct go7007 *go);
int go7007_i2c_remove(struct go7007 *go);
/* go7007-v4l2.c */
int go7007_v4l2_init(struct go7007 *go);
void go7007_v4l2_remove(struct go7007 *go);
/* snd-go7007.c */
int go7007_snd_init(struct go7007 *go);
int go7007_snd_remove(struct go7007 *go);
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/mm.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#include <asm/byteorder.h>
#include <media/tvaudio.h>
#include "go7007-priv.h"
#include "wis-i2c.h"
static unsigned int assume_endura;
module_param(assume_endura, int, 0644);
MODULE_PARM_DESC(assume_endura, "when probing fails, hardware is a Pelco Endura");
/* #define GO7007_USB_DEBUG */
/* #define GO7007_I2C_DEBUG */ /* for debugging the EZ-USB I2C adapter */
#define HPI_STATUS_ADDR 0xFFF4
#define INT_PARAM_ADDR 0xFFF6
#define INT_INDEX_ADDR 0xFFF8
/*
* Pipes on EZ-USB interface:
* 0 snd - Control
* 0 rcv - Control
* 2 snd - Download firmware (control)
* 4 rcv - Read Interrupt (interrupt)
* 6 rcv - Read Video (bulk)
* 8 rcv - Read Audio (bulk)
*/
#define GO7007_USB_EZUSB (1<<0)
#define GO7007_USB_EZUSB_I2C (1<<1)
struct go7007_usb_board {
unsigned int flags;
struct go7007_board_info main_info;
};
struct go7007_usb {
struct go7007_usb_board *board;
struct semaphore i2c_lock;
struct usb_device *usbdev;
struct urb *video_urbs[8];
struct urb *audio_urbs[8];
struct urb *intr_urb;
};
/*********************** Product specification data ***********************/
static struct go7007_usb_board board_matrix_ii = {
.flags = GO7007_USB_EZUSB,
.main_info = {
.firmware = "go7007tv.bin",
.flags = GO7007_BOARD_HAS_AUDIO |
GO7007_BOARD_USE_ONBOARD_I2C,
.audio_flags = GO7007_AUDIO_I2S_MODE_1 |
GO7007_AUDIO_WORD_16,
.audio_rate = 48000,
.audio_bclk_div = 8,
.audio_main_div = 2,
.hpi_buffer_cap = 7,
.sensor_flags = GO7007_SENSOR_656 |
GO7007_SENSOR_VALID_ENABLE |
GO7007_SENSOR_TV |
GO7007_SENSOR_VBI |
GO7007_SENSOR_SCALING,
.num_i2c_devs = 1,
.i2c_devs = {
{
.id = I2C_DRIVERID_WIS_SAA7115,
.addr = 0x20,
},
},
.num_inputs = 2,
.inputs = {
{
.video_input = 0,
.name = "Composite",
},
{
.video_input = 9,
.name = "S-Video",
},
},
},
};
static struct go7007_usb_board board_matrix_reload = {
.flags = GO7007_USB_EZUSB,
.main_info = {
.firmware = "go7007tv.bin",
.flags = GO7007_BOARD_HAS_AUDIO |
GO7007_BOARD_USE_ONBOARD_I2C,
.audio_flags = GO7007_AUDIO_I2S_MODE_1 |
GO7007_AUDIO_I2S_MASTER |
GO7007_AUDIO_WORD_16,
.audio_rate = 48000,
.audio_bclk_div = 8,
.audio_main_div = 2,
.hpi_buffer_cap = 7,
.sensor_flags = GO7007_SENSOR_656 |
GO7007_SENSOR_TV,
.num_i2c_devs = 1,
.i2c_devs = {
{
.id = I2C_DRIVERID_WIS_SAA7113,
.addr = 0x25,
},
},
.num_inputs = 2,
.inputs = {
{
.video_input = 0,
.name = "Composite",
},
{
.video_input = 9,
.name = "S-Video",
},
},
},
};
static struct go7007_usb_board board_star_trek = {
.flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
.main_info = {
.firmware = "go7007tv.bin",
.flags = GO7007_BOARD_HAS_AUDIO, /* |
GO7007_BOARD_HAS_TUNER, */
.sensor_flags = GO7007_SENSOR_656 |
GO7007_SENSOR_VALID_ENABLE |
GO7007_SENSOR_TV |
GO7007_SENSOR_VBI |
GO7007_SENSOR_SCALING,
.audio_flags = GO7007_AUDIO_I2S_MODE_1 |
GO7007_AUDIO_WORD_16,
.audio_bclk_div = 8,
.audio_main_div = 2,
.hpi_buffer_cap = 7,
.num_i2c_devs = 1,
.i2c_devs = {
{
.id = I2C_DRIVERID_WIS_SAA7115,
.addr = 0x20,
},
},
.num_inputs = 2,
.inputs = {
{
.video_input = 1,
/* .audio_input = AUDIO_EXTERN, */
.name = "Composite",
},
{
.video_input = 8,
/* .audio_input = AUDIO_EXTERN, */
.name = "S-Video",
},
/* {
* .video_input = 3,
* .audio_input = AUDIO_TUNER,
* .name = "Tuner",
* },
*/
},
},
};
static struct go7007_usb_board board_px_tv402u = {
.flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
.main_info = {
.firmware = "go7007tv.bin",
.flags = GO7007_BOARD_HAS_AUDIO |
GO7007_BOARD_HAS_TUNER,
.sensor_flags = GO7007_SENSOR_656 |
GO7007_SENSOR_VALID_ENABLE |
GO7007_SENSOR_TV |
GO7007_SENSOR_VBI |
GO7007_SENSOR_SCALING,
.audio_flags = GO7007_AUDIO_I2S_MODE_1 |
GO7007_AUDIO_WORD_16,
.audio_bclk_div = 8,
.audio_main_div = 2,
.hpi_buffer_cap = 7,
.num_i2c_devs = 3,
.i2c_devs = {
{
.id = I2C_DRIVERID_WIS_SAA7115,
.addr = 0x20,
},
{
.id = I2C_DRIVERID_WIS_UDA1342,
.addr = 0x1a,
},
{
.id = I2C_DRIVERID_WIS_SONY_TUNER,
.addr = 0x60,
},
},
.num_inputs = 3,
.inputs = {
{
.video_input = 1,
.audio_input = TVAUDIO_INPUT_EXTERN,
.name = "Composite",
},
{
.video_input = 8,
.audio_input = TVAUDIO_INPUT_EXTERN,
.name = "S-Video",
},
{
.video_input = 3,
.audio_input = TVAUDIO_INPUT_TUNER,
.name = "Tuner",
},
},
},
};
static struct go7007_usb_board board_xmen = {
.flags = 0,
.main_info = {
.firmware = "go7007tv.bin",
.flags = GO7007_BOARD_USE_ONBOARD_I2C,
.hpi_buffer_cap = 0,
.sensor_flags = GO7007_SENSOR_VREF_POLAR,
.sensor_width = 320,
.sensor_height = 240,
.sensor_framerate = 30030,
.audio_flags = GO7007_AUDIO_ONE_CHANNEL |
GO7007_AUDIO_I2S_MODE_3 |
GO7007_AUDIO_WORD_14 |
GO7007_AUDIO_I2S_MASTER |
GO7007_AUDIO_BCLK_POLAR |
GO7007_AUDIO_OKI_MODE,
.audio_rate = 8000,
.audio_bclk_div = 48,
.audio_main_div = 1,
.num_i2c_devs = 1,
.i2c_devs = {
{
.id = I2C_DRIVERID_WIS_OV7640,
.addr = 0x21,
},
},
.num_inputs = 1,
.inputs = {
{
.name = "Camera",
},
},
},
};
static struct go7007_usb_board board_matrix_revolution = {
.flags = GO7007_USB_EZUSB,
.main_info = {
.firmware = "go7007tv.bin",
.flags = GO7007_BOARD_HAS_AUDIO |
GO7007_BOARD_USE_ONBOARD_I2C,
.audio_flags = GO7007_AUDIO_I2S_MODE_1 |
GO7007_AUDIO_I2S_MASTER |
GO7007_AUDIO_WORD_16,
.audio_rate = 48000,
.audio_bclk_div = 8,
.audio_main_div = 2,
.hpi_buffer_cap = 7,
.sensor_flags = GO7007_SENSOR_656 |
GO7007_SENSOR_TV |
GO7007_SENSOR_VBI,
.num_i2c_devs = 1,
.i2c_devs = {
{
.id = I2C_DRIVERID_WIS_TW9903,
.addr = 0x44,
},
},
.num_inputs = 2,
.inputs = {
{
.video_input = 2,
.name = "Composite",
},
{
.video_input = 8,
.name = "S-Video",
},
},
},
};
static struct go7007_usb_board board_lifeview_lr192 = {
.flags = GO7007_USB_EZUSB,
.main_info = {
.firmware = "go7007tv.bin",
.flags = GO7007_BOARD_HAS_AUDIO |
GO7007_BOARD_USE_ONBOARD_I2C,
.audio_flags = GO7007_AUDIO_I2S_MODE_1 |
GO7007_AUDIO_WORD_16,
.audio_rate = 48000,
.audio_bclk_div = 8,
.audio_main_div = 2,
.hpi_buffer_cap = 7,
.sensor_flags = GO7007_SENSOR_656 |
GO7007_SENSOR_VALID_ENABLE |
GO7007_SENSOR_TV |
GO7007_SENSOR_VBI |
GO7007_SENSOR_SCALING,
.num_i2c_devs = 0,
.num_inputs = 1,
.inputs = {
{
.video_input = 0,
.name = "Composite",
},
},
},
};
static struct go7007_usb_board board_endura = {
.flags = 0,
.main_info = {
.firmware = "go7007tv.bin",
.flags = 0,
.audio_flags = GO7007_AUDIO_I2S_MODE_1 |
GO7007_AUDIO_I2S_MASTER |
GO7007_AUDIO_WORD_16,
.audio_rate = 8000,
.audio_bclk_div = 48,
.audio_main_div = 8,
.hpi_buffer_cap = 0,
.sensor_flags = GO7007_SENSOR_656 |
GO7007_SENSOR_TV,
.sensor_h_offset = 8,
.num_i2c_devs = 0,
.num_inputs = 1,
.inputs = {
{
.name = "Camera",
},
},
},
};
static struct go7007_usb_board board_adlink_mpg24 = {
.flags = 0,
.main_info = {
.firmware = "go7007tv.bin",
.flags = GO7007_BOARD_USE_ONBOARD_I2C,
.audio_flags = GO7007_AUDIO_I2S_MODE_1 |
GO7007_AUDIO_I2S_MASTER |
GO7007_AUDIO_WORD_16,
.audio_rate = 48000,
.audio_bclk_div = 8,
.audio_main_div = 2,
.hpi_buffer_cap = 0,
.sensor_flags = GO7007_SENSOR_656 |
GO7007_SENSOR_TV |
GO7007_SENSOR_VBI,
.num_i2c_devs = 1,
.i2c_devs = {
{
.id = I2C_DRIVERID_WIS_TW2804,
.addr = 0x00, /* yes, really */
},
},
.num_inputs = 1,
.inputs = {
{
.name = "Composite",
},
},
},
};
static struct usb_device_id go7007_usb_id_table[] = {
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
.idProduct = 0x7007, /* Product ID of GO7007SB chip */
.bcdDevice_lo = 0x200, /* Revision number of XMen */
.bcdDevice_hi = 0x200,
.bInterfaceClass = 255,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 255,
.driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN,
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
.idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
.idProduct = 0x7007, /* Product ID of GO7007SB chip */
.bcdDevice_lo = 0x202, /* Revision number of Matrix II */
.bcdDevice_hi = 0x202,
.driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_II,
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
.idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
.idProduct = 0x7007, /* Product ID of GO7007SB chip */
.bcdDevice_lo = 0x204, /* Revision number of Matrix */
.bcdDevice_hi = 0x204, /* Reloaded */
.driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_RELOAD,
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
.idProduct = 0x7007, /* Product ID of GO7007SB chip */
.bcdDevice_lo = 0x205, /* Revision number of XMen-II */
.bcdDevice_hi = 0x205,
.bInterfaceClass = 255,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 255,
.driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_II,
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
.idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
.idProduct = 0x7007, /* Product ID of GO7007SB chip */
.bcdDevice_lo = 0x208, /* Revision number of Star Trek */
.bcdDevice_hi = 0x208,
.driver_info = (kernel_ulong_t)GO7007_BOARDID_STAR_TREK,
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
.idProduct = 0x7007, /* Product ID of GO7007SB chip */
.bcdDevice_lo = 0x209, /* Revision number of XMen-III */
.bcdDevice_hi = 0x209,
.bInterfaceClass = 255,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 255,
.driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_III,
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
.idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */
.idProduct = 0x7007, /* Product ID of GO7007SB chip */
.bcdDevice_lo = 0x210, /* Revision number of Matrix */
.bcdDevice_hi = 0x210, /* Revolution */
.driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_REV,
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
.idVendor = 0x093b, /* Vendor ID of Plextor */
.idProduct = 0xa102, /* Product ID of M402U */
.bcdDevice_lo = 0x1, /* revision number of Blueberry */
.bcdDevice_hi = 0x1,
.driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_M402U,
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
.idVendor = 0x093b, /* Vendor ID of Plextor */
.idProduct = 0xa104, /* Product ID of TV402U */
.bcdDevice_lo = 0x1,
.bcdDevice_hi = 0x1,
.driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_TV402U_ANY,
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
.idVendor = 0x10fd, /* Vendor ID of Anubis Electronics */
.idProduct = 0xde00, /* Product ID of Lifeview LR192 */
.bcdDevice_lo = 0x1,
.bcdDevice_hi = 0x1,
.driver_info = (kernel_ulong_t)GO7007_BOARDID_LIFEVIEW_LR192,
},
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, go7007_usb_id_table);
/********************* Driver for EZ-USB HPI interface *********************/
static int go7007_usb_vendor_request(struct go7007 *go, int request,
int value, int index, void *transfer_buffer, int length, int in)
{
struct go7007_usb *usb = go->hpi_context;
int timeout = 5000;
if (in) {
return usb_control_msg(usb->usbdev,
usb_rcvctrlpipe(usb->usbdev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
value, index, transfer_buffer, length, timeout);
} else {
return usb_control_msg(usb->usbdev,
usb_sndctrlpipe(usb->usbdev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, transfer_buffer, length, timeout);
}
}
static int go7007_usb_interface_reset(struct go7007 *go)
{
struct go7007_usb *usb = go->hpi_context;
u16 intr_val, intr_data;
/* Reset encoder */
if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0)
return -1;
msleep(100);
if (usb->board->flags & GO7007_USB_EZUSB) {
/* Reset buffer in EZ-USB */
#ifdef GO7007_USB_DEBUG
printk(KERN_DEBUG "go7007-usb: resetting EZ-USB buffers\n");
#endif
if (go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0 ||
go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0)
return -1;
/* Reset encoder again */
if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0)
return -1;
msleep(100);
}
/* Wait for an interrupt to indicate successful hardware reset */
if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
(intr_val & ~0x1) != 0x55aa) {
printk(KERN_ERR
"go7007-usb: unable to reset the USB interface\n");
return -1;
}
return 0;
}
static int go7007_usb_ezusb_write_interrupt(struct go7007 *go,
int addr, int data)
{
struct go7007_usb *usb = go->hpi_context;
int i, r;
u16 status_reg;
int timeout = 500;
#ifdef GO7007_USB_DEBUG
printk(KERN_DEBUG
"go7007-usb: WriteInterrupt: %04x %04x\n", addr, data);
#endif
for (i = 0; i < 100; ++i) {
r = usb_control_msg(usb->usbdev,
usb_rcvctrlpipe(usb->usbdev, 0), 0x14,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
0, HPI_STATUS_ADDR, &status_reg,
sizeof(status_reg), timeout);
if (r < 0)
goto write_int_error;
__le16_to_cpus(&status_reg);
if (!(status_reg & 0x0010))
break;
msleep(10);
}
if (i == 100) {
printk(KERN_ERR
"go7007-usb: device is hung, status reg = 0x%04x\n",
status_reg);
return -1;
}
r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), 0x12,
USB_TYPE_VENDOR | USB_RECIP_DEVICE, data,
INT_PARAM_ADDR, NULL, 0, timeout);
if (r < 0)
goto write_int_error;
r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0),
0x12, USB_TYPE_VENDOR | USB_RECIP_DEVICE, addr,
INT_INDEX_ADDR, NULL, 0, timeout);
if (r < 0)
goto write_int_error;
return 0;
write_int_error:
printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r);
return r;
}
static int go7007_usb_onboard_write_interrupt(struct go7007 *go,
int addr, int data)
{
struct go7007_usb *usb = go->hpi_context;
u8 *tbuf;
int r;
int timeout = 500;
#ifdef GO7007_USB_DEBUG
printk(KERN_DEBUG
"go7007-usb: WriteInterrupt: %04x %04x\n", addr, data);
#endif
tbuf = kmalloc(8, GFP_KERNEL);
if (tbuf == NULL)
return -ENOMEM;
memset(tbuf, 0, 8);
tbuf[0] = data & 0xff;
tbuf[1] = data >> 8;
tbuf[2] = addr & 0xff;
tbuf[3] = addr >> 8;
r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 2), 0x00,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0x55aa,
0xf0f0, tbuf, 8, timeout);
kfree(tbuf);
if (r < 0) {
printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r);
return r;
}
return 0;
}
static void go7007_usb_readinterrupt_complete(struct urb *urb)
{
struct go7007 *go = (struct go7007 *)urb->context;
u16 *regs = (u16 *)urb->transfer_buffer;
if (urb->status != 0) {
if (urb->status != -ESHUTDOWN &&
go->status != STATUS_SHUTDOWN) {
printk(KERN_ERR
"go7007-usb: error in read interrupt: %d\n",
urb->status);
} else {
wake_up(&go->interrupt_waitq);
return;
}
} else if (urb->actual_length != urb->transfer_buffer_length) {
printk(KERN_ERR "go7007-usb: short read in interrupt pipe!\n");
} else {
go->interrupt_available = 1;
go->interrupt_data = __le16_to_cpu(regs[0]);
go->interrupt_value = __le16_to_cpu(regs[1]);
#ifdef GO7007_USB_DEBUG
printk(KERN_DEBUG "go7007-usb: ReadInterrupt: %04x %04x\n",
go->interrupt_value, go->interrupt_data);
#endif
}
wake_up(&go->interrupt_waitq);
}
static int go7007_usb_read_interrupt(struct go7007 *go)
{
struct go7007_usb *usb = go->hpi_context;
int r;
r = usb_submit_urb(usb->intr_urb, GFP_KERNEL);
if (r < 0) {
printk(KERN_ERR
"go7007-usb: unable to submit interrupt urb: %d\n", r);
return r;
}
return 0;
}
static void go7007_usb_read_video_pipe_complete(struct urb *urb)
{
struct go7007 *go = (struct go7007 *)urb->context;
int r;
if (!go->streaming) {
wake_up_interruptible(&go->frame_waitq);
return;
}
if (urb->status != 0) {
printk(KERN_ERR "go7007-usb: error in video pipe: %d\n",
urb->status);
return;
}
if (urb->actual_length != urb->transfer_buffer_length) {
printk(KERN_ERR "go7007-usb: short read in video pipe!\n");
return;
}
go7007_parse_video_stream(go, urb->transfer_buffer, urb->actual_length);
r = usb_submit_urb(urb, GFP_ATOMIC);
if (r < 0)
printk(KERN_ERR "go7007-usb: error in video pipe: %d\n", r);
}
static void go7007_usb_read_audio_pipe_complete(struct urb *urb)
{
struct go7007 *go = (struct go7007 *)urb->context;
int r;
if (!go->streaming)
return;
if (urb->status != 0) {
printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n",
urb->status);
return;
}
if (urb->actual_length != urb->transfer_buffer_length) {
printk(KERN_ERR "go7007-usb: short read in audio pipe!\n");
return;
}
if (go->audio_deliver != NULL)
go->audio_deliver(go, urb->transfer_buffer, urb->actual_length);
r = usb_submit_urb(urb, GFP_ATOMIC);
if (r < 0)
printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n", r);
}
static int go7007_usb_stream_start(struct go7007 *go)
{
struct go7007_usb *usb = go->hpi_context;
int i, r;
for (i = 0; i < 8; ++i) {
r = usb_submit_urb(usb->video_urbs[i], GFP_KERNEL);
if (r < 0) {
printk(KERN_ERR "go7007-usb: error submitting video "
"urb %d: %d\n", i, r);
goto video_submit_failed;
}
}
if (!go->audio_enabled)
return 0;
for (i = 0; i < 8; ++i) {
r = usb_submit_urb(usb->audio_urbs[i], GFP_KERNEL);
if (r < 0) {
printk(KERN_ERR "go7007-usb: error submitting audio "
"urb %d: %d\n", i, r);
goto audio_submit_failed;
}
}
return 0;
audio_submit_failed:
for (i = 0; i < 8; ++i)
usb_kill_urb(usb->audio_urbs[i]);
video_submit_failed:
for (i = 0; i < 8; ++i)
usb_kill_urb(usb->video_urbs[i]);
return -1;
}
static int go7007_usb_stream_stop(struct go7007 *go)
{
struct go7007_usb *usb = go->hpi_context;
int i;
if (go->status == STATUS_SHUTDOWN)
return 0;
for (i = 0; i < 8; ++i)
usb_kill_urb(usb->video_urbs[i]);
if (go->audio_enabled)
for (i = 0; i < 8; ++i)
usb_kill_urb(usb->audio_urbs[i]);
return 0;
}
static int go7007_usb_send_firmware(struct go7007 *go, u8 *data, int len)
{
struct go7007_usb *usb = go->hpi_context;
int transferred, pipe;
int timeout = 500;
#ifdef GO7007_USB_DEBUG
printk(KERN_DEBUG "go7007-usb: DownloadBuffer sending %d bytes\n", len);
#endif
if (usb->board->flags & GO7007_USB_EZUSB)
pipe = usb_sndbulkpipe(usb->usbdev, 2);
else
pipe = usb_sndbulkpipe(usb->usbdev, 3);
return usb_bulk_msg(usb->usbdev, pipe, data, len,
&transferred, timeout);
}
static struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = {
.interface_reset = go7007_usb_interface_reset,
.write_interrupt = go7007_usb_ezusb_write_interrupt,
.read_interrupt = go7007_usb_read_interrupt,
.stream_start = go7007_usb_stream_start,
.stream_stop = go7007_usb_stream_stop,
.send_firmware = go7007_usb_send_firmware,
};
static struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = {
.interface_reset = go7007_usb_interface_reset,
.write_interrupt = go7007_usb_onboard_write_interrupt,
.read_interrupt = go7007_usb_read_interrupt,
.stream_start = go7007_usb_stream_start,
.stream_stop = go7007_usb_stream_stop,
.send_firmware = go7007_usb_send_firmware,
};
/********************* Driver for EZ-USB I2C adapter *********************/
static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter,
struct i2c_msg msgs[], int num)
{
struct go7007 *go = i2c_get_adapdata(adapter);
struct go7007_usb *usb = go->hpi_context;
u8 buf[16];
int buf_len, i;
int ret = -1;
if (go->status == STATUS_SHUTDOWN)
return -1;
down(&usb->i2c_lock);
for (i = 0; i < num; ++i) {
/* The hardware command is "write some bytes then read some
* bytes", so we try to coalesce a write followed by a read
* into a single USB transaction */
if (i + 1 < num && msgs[i].addr == msgs[i + 1].addr &&
!(msgs[i].flags & I2C_M_RD) &&
(msgs[i + 1].flags & I2C_M_RD)) {
#ifdef GO7007_I2C_DEBUG
printk(KERN_DEBUG "go7007-usb: i2c write/read %d/%d "
"bytes on %02x\n", msgs[i].len,
msgs[i + 1].len, msgs[i].addr);
#endif
buf[0] = 0x01;
buf[1] = msgs[i].len + 1;
buf[2] = msgs[i].addr << 1;
memcpy(&buf[3], msgs[i].buf, msgs[i].len);
buf_len = msgs[i].len + 3;
buf[buf_len++] = msgs[++i].len;
} else if (msgs[i].flags & I2C_M_RD) {
#ifdef GO7007_I2C_DEBUG
printk(KERN_DEBUG "go7007-usb: i2c read %d "
"bytes on %02x\n", msgs[i].len,
msgs[i].addr);
#endif
buf[0] = 0x01;
buf[1] = 1;
buf[2] = msgs[i].addr << 1;
buf[3] = msgs[i].len;
buf_len = 4;
} else {
#ifdef GO7007_I2C_DEBUG
printk(KERN_DEBUG "go7007-usb: i2c write %d "
"bytes on %02x\n", msgs[i].len,
msgs[i].addr);
#endif
buf[0] = 0x00;
buf[1] = msgs[i].len + 1;
buf[2] = msgs[i].addr << 1;
memcpy(&buf[3], msgs[i].buf, msgs[i].len);
buf_len = msgs[i].len + 3;
buf[buf_len++] = 0;
}
if (go7007_usb_vendor_request(go, 0x24, 0, 0,
buf, buf_len, 0) < 0)
goto i2c_done;
if (msgs[i].flags & I2C_M_RD) {
memset(buf, 0, sizeof(buf));
if (go7007_usb_vendor_request(go, 0x25, 0, 0, buf,
msgs[i].len + 1, 1) < 0)
goto i2c_done;
memcpy(msgs[i].buf, buf + 1, msgs[i].len);
}
}
ret = 0;
i2c_done:
up(&usb->i2c_lock);
return ret;
}
static u32 go7007_usb_functionality(struct i2c_adapter *adapter)
{
/* No errors are reported by the hardware, so we don't bother
* supporting quick writes to avoid confusing probing */
return (I2C_FUNC_SMBUS_EMUL) & ~I2C_FUNC_SMBUS_QUICK;
}
static struct i2c_algorithm go7007_usb_algo = {
.master_xfer = go7007_usb_i2c_master_xfer,
.functionality = go7007_usb_functionality,
};
static struct i2c_adapter go7007_usb_adap_templ = {
.owner = THIS_MODULE,
.class = I2C_CLASS_TV_ANALOG,
.name = "WIS GO7007SB EZ-USB",
.id = I2C_ALGO_GO7007_USB,
.algo = &go7007_usb_algo,
};
/********************* USB add/remove functions *********************/
static int go7007_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct go7007 *go;
struct go7007_usb *usb;
struct go7007_usb_board *board;
struct usb_device *usbdev = interface_to_usbdev(intf);
char *name;
int video_pipe, i, v_urb_len;
printk(KERN_DEBUG "go7007-usb: probing new GO7007 USB board\n");
switch (id->driver_info) {
case GO7007_BOARDID_MATRIX_II:
name = "WIS Matrix II or compatible";
board = &board_matrix_ii;
break;
case GO7007_BOARDID_MATRIX_RELOAD:
name = "WIS Matrix Reloaded or compatible";
board = &board_matrix_reload;
break;
case GO7007_BOARDID_MATRIX_REV:
name = "WIS Matrix Revolution or compatible";
board = &board_matrix_revolution;
break;
case GO7007_BOARDID_STAR_TREK:
name = "WIS Star Trek or compatible";
board = &board_star_trek;
break;
case GO7007_BOARDID_XMEN:
name = "WIS XMen or compatible";
board = &board_xmen;
break;
case GO7007_BOARDID_XMEN_II:
name = "WIS XMen II or compatible";
board = &board_xmen;
break;
case GO7007_BOARDID_XMEN_III:
name = "WIS XMen III or compatible";
board = &board_xmen;
break;
case GO7007_BOARDID_PX_M402U:
name = "Plextor PX-M402U";
board = &board_matrix_ii;
break;
case GO7007_BOARDID_PX_TV402U_ANY:
name = "Plextor PX-TV402U (unknown tuner)";
board = &board_px_tv402u;
break;
case GO7007_BOARDID_LIFEVIEW_LR192:
printk(KERN_ERR "go7007-usb: The Lifeview TV Walker Ultra "
"is not supported. Sorry!\n");
return 0;
name = "Lifeview TV Walker Ultra";
board = &board_lifeview_lr192;
break;
default:
printk(KERN_ERR "go7007-usb: unknown board ID %d!\n",
(unsigned int)id->driver_info);
return 0;
}
usb = kmalloc(sizeof(struct go7007_usb), GFP_KERNEL);
if (usb == NULL)
return -ENOMEM;
memset(usb, 0, sizeof(struct go7007_usb));
/* Allocate the URB and buffer for receiving incoming interrupts */
usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
if (usb->intr_urb == NULL)
goto allocfail;
usb->intr_urb->transfer_buffer = kmalloc(2*sizeof(u16), GFP_KERNEL);
if (usb->intr_urb->transfer_buffer == NULL)
goto allocfail;
go = go7007_alloc(&board->main_info, &intf->dev);
if (go == NULL)
goto allocfail;
usb->board = board;
usb->usbdev = usbdev;
go->board_id = id->driver_info;
strncpy(go->name, name, sizeof(go->name));
if (board->flags & GO7007_USB_EZUSB)
go->hpi_ops = &go7007_usb_ezusb_hpi_ops;
else
go->hpi_ops = &go7007_usb_onboard_hpi_ops;
go->hpi_context = usb;
usb_fill_int_urb(usb->intr_urb, usb->usbdev,
usb_rcvintpipe(usb->usbdev, 4),
usb->intr_urb->transfer_buffer, 2*sizeof(u16),
go7007_usb_readinterrupt_complete, go, 8);
usb_set_intfdata(intf, go);
/* Boot the GO7007 */
if (go7007_boot_encoder(go, go->board_info->flags &
GO7007_BOARD_USE_ONBOARD_I2C) < 0)
goto initfail;
/* Register the EZ-USB I2C adapter, if we're using it */
if (board->flags & GO7007_USB_EZUSB_I2C) {
memcpy(&go->i2c_adapter, &go7007_usb_adap_templ,
sizeof(go7007_usb_adap_templ));
init_MUTEX(&usb->i2c_lock);
go->i2c_adapter.dev.parent = go->dev;
i2c_set_adapdata(&go->i2c_adapter, go);
if (i2c_add_adapter(&go->i2c_adapter) < 0) {
printk(KERN_ERR
"go7007-usb: error: i2c_add_adapter failed\n");
goto initfail;
}
go->i2c_adapter_online = 1;
}
/* Pelco and Adlink reused the XMen and XMen-III vendor and product
* IDs for their own incompatible designs. We can detect XMen boards
* by probing the sensor, but there is no way to probe the sensors on
* the Pelco and Adlink designs so we default to the Adlink. If it
* is actually a Pelco, the user must set the assume_endura module
* parameter. */
if ((go->board_id == GO7007_BOARDID_XMEN ||
go->board_id == GO7007_BOARDID_XMEN_III) &&
go->i2c_adapter_online) {
union i2c_smbus_data data;
/* Check to see if register 0x0A is 0x76 */
i2c_smbus_xfer(&go->i2c_adapter, 0x21, I2C_CLIENT_SCCB,
I2C_SMBUS_READ, 0x0A, I2C_SMBUS_BYTE_DATA, &data);
if (data.byte != 0x76) {
if (assume_endura) {
go->board_id = GO7007_BOARDID_ENDURA;
usb->board = board = &board_endura;
go->board_info = &board->main_info;
strncpy(go->name, "Pelco Endura",
sizeof(go->name));
} else {
u16 channel;
/* set GPIO5 to be an output, currently low */
go7007_write_addr(go, 0x3c82, 0x0000);
go7007_write_addr(go, 0x3c80, 0x00df);
/* read channel number from GPIO[1:0] */
go7007_read_addr(go, 0x3c81, &channel);
channel &= 0x3;
go->board_id = GO7007_BOARDID_ADLINK_MPG24;
usb->board = board = &board_adlink_mpg24;
go->board_info = &board->main_info;
go->channel_number = channel;
snprintf(go->name, sizeof(go->name),
"Adlink PCI-MPG24, channel #%d",
channel);
}
}
}
/* Probe the tuner model on the TV402U */
if (go->board_id == GO7007_BOARDID_PX_TV402U_ANY) {
u8 data[3];
/* Board strapping indicates tuner model */
if (go7007_usb_vendor_request(go, 0x41, 0, 0, data, 3, 1) < 0) {
printk(KERN_ERR "go7007-usb: GPIO read failed!\n");
goto initfail;
}
switch (data[0] >> 6) {
case 1:
go->board_id = GO7007_BOARDID_PX_TV402U_EU;
go->tuner_type = TUNER_SONY_BTF_PG472Z;
strncpy(go->name, "Plextor PX-TV402U-EU",
sizeof(go->name));
break;
case 2:
go->board_id = GO7007_BOARDID_PX_TV402U_JP;
go->tuner_type = TUNER_SONY_BTF_PK467Z;
strncpy(go->name, "Plextor PX-TV402U-JP",
sizeof(go->name));
break;
case 3:
go->board_id = GO7007_BOARDID_PX_TV402U_NA;
go->tuner_type = TUNER_SONY_BTF_PB463Z;
strncpy(go->name, "Plextor PX-TV402U-NA",
sizeof(go->name));
break;
default:
printk(KERN_DEBUG "go7007-usb: unable to detect "
"tuner type!\n");
break;
}
/* Configure tuner mode selection inputs connected
* to the EZ-USB GPIO output pins */
if (go7007_usb_vendor_request(go, 0x40, 0x7f02, 0,
NULL, 0, 0) < 0) {
printk(KERN_ERR
"go7007-usb: GPIO write failed!\n");
goto initfail;
}
}
/* Print a nasty message if the user attempts to use a USB2.0 device in
* a USB1.1 port. There will be silent corruption of the stream. */
if ((board->flags & GO7007_USB_EZUSB) &&
usbdev->speed != USB_SPEED_HIGH)
printk(KERN_ERR "go7007-usb: *** WARNING *** This device "
"must be connected to a USB 2.0 port! "
"Attempting to capture video through a USB 1.1 "
"port will result in stream corruption, even "
"at low bitrates!\n");
/* Do any final GO7007 initialization, then register the
* V4L2 and ALSA interfaces */
if (go7007_register_encoder(go) < 0)
goto initfail;
/* Allocate the URBs and buffers for receiving the video stream */
if (board->flags & GO7007_USB_EZUSB) {
v_urb_len = 1024;
video_pipe = usb_rcvbulkpipe(usb->usbdev, 6);
} else {
v_urb_len = 512;
video_pipe = usb_rcvbulkpipe(usb->usbdev, 1);
}
for (i = 0; i < 8; ++i) {
usb->video_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
if (usb->video_urbs[i] == NULL)
goto initfail;
usb->video_urbs[i]->transfer_buffer =
kmalloc(v_urb_len, GFP_KERNEL);
if (usb->video_urbs[i]->transfer_buffer == NULL)
goto initfail;
usb_fill_bulk_urb(usb->video_urbs[i], usb->usbdev, video_pipe,
usb->video_urbs[i]->transfer_buffer, v_urb_len,
go7007_usb_read_video_pipe_complete, go);
}
/* Allocate the URBs and buffers for receiving the audio stream */
if ((board->flags & GO7007_USB_EZUSB) && go->audio_enabled)
for (i = 0; i < 8; ++i) {
usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
if (usb->audio_urbs[i] == NULL)
goto initfail;
usb->audio_urbs[i]->transfer_buffer = kmalloc(4096,
GFP_KERNEL);
if (usb->audio_urbs[i]->transfer_buffer == NULL)
goto initfail;
usb_fill_bulk_urb(usb->audio_urbs[i], usb->usbdev,
usb_rcvbulkpipe(usb->usbdev, 8),
usb->audio_urbs[i]->transfer_buffer, 4096,
go7007_usb_read_audio_pipe_complete, go);
}
go->status = STATUS_ONLINE;
return 0;
initfail:
go->status = STATUS_SHUTDOWN;
return 0;
allocfail:
if (usb->intr_urb) {
kfree(usb->intr_urb->transfer_buffer);
usb_free_urb(usb->intr_urb);
}
kfree(usb);
return -ENOMEM;
}
static void go7007_usb_disconnect(struct usb_interface *intf)
{
struct go7007 *go = usb_get_intfdata(intf);
struct go7007_usb *usb = go->hpi_context;
int i;
go->status = STATUS_SHUTDOWN;
usb_kill_urb(usb->intr_urb);
/* Free USB-related structs */
for (i = 0; i < 8; ++i) {
if (usb->video_urbs[i] != NULL) {
if (usb->video_urbs[i]->transfer_buffer != NULL)
kfree(usb->video_urbs[i]->transfer_buffer);
usb_free_urb(usb->video_urbs[i]);
}
if (usb->audio_urbs[i] != NULL) {
if (usb->audio_urbs[i]->transfer_buffer != NULL)
kfree(usb->audio_urbs[i]->transfer_buffer);
usb_free_urb(usb->audio_urbs[i]);
}
}
kfree(usb->intr_urb->transfer_buffer);
usb_free_urb(usb->intr_urb);
kfree(go->hpi_context);
go7007_remove(go);
}
static struct usb_driver go7007_usb_driver = {
.name = "go7007",
.probe = go7007_usb_probe,
.disconnect = go7007_usb_disconnect,
.id_table = go7007_usb_id_table,
};
static int __init go7007_usb_init(void)
{
return usb_register(&go7007_usb_driver);
}
static void __exit go7007_usb_cleanup(void)
{
usb_deregister(&go7007_usb_driver);
}
module_init(go7007_usb_init);
module_exit(go7007_usb_cleanup);
MODULE_LICENSE("GPL v2");
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/fs.h>
#include <linux/unistd.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
#include <linux/videodev.h>
#include <linux/video_decoder.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <linux/i2c.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <asm/system.h>
#include "go7007.h"
#include "go7007-priv.h"
#include "wis-i2c.h"
static void deactivate_buffer(struct go7007_buffer *gobuf)
{
int i;
if (gobuf->state != BUF_STATE_IDLE) {
list_del(&gobuf->stream);
gobuf->state = BUF_STATE_IDLE;
}
if (gobuf->page_count > 0) {
for (i = 0; i < gobuf->page_count; ++i)
page_cache_release(gobuf->pages[i]);
gobuf->page_count = 0;
}
}
static void abort_queued(struct go7007 *go)
{
struct go7007_buffer *gobuf, *next;
list_for_each_entry_safe(gobuf, next, &go->stream, stream) {
deactivate_buffer(gobuf);
}
}
static int go7007_streamoff(struct go7007 *go)
{
int retval = -EINVAL;
unsigned long flags;
down(&go->hw_lock);
if (go->streaming) {
go->streaming = 0;
go7007_stream_stop(go);
spin_lock_irqsave(&go->spinlock, flags);
abort_queued(go);
spin_unlock_irqrestore(&go->spinlock, flags);
go7007_reset_encoder(go);
retval = 0;
}
up(&go->hw_lock);
return 0;
}
static int go7007_open(struct inode *inode, struct file *file)
{
struct go7007 *go = video_get_drvdata(video_devdata(file));
struct go7007_file *gofh;
if (go->status != STATUS_ONLINE)
return -EBUSY;
gofh = kmalloc(sizeof(struct go7007_file), GFP_KERNEL);
if (gofh == NULL)
return -ENOMEM;
++go->ref_count;
gofh->go = go;
init_MUTEX(&gofh->lock);
gofh->buf_count = 0;
file->private_data = gofh;
return 0;
}
static int go7007_release(struct inode *inode, struct file *file)
{
struct go7007_file *gofh = file->private_data;
struct go7007 *go = gofh->go;
if (gofh->buf_count > 0) {
go7007_streamoff(go);
go->in_use = 0;
kfree(gofh->bufs);
gofh->buf_count = 0;
}
kfree(gofh);
if (--go->ref_count == 0)
kfree(go);
file->private_data = NULL;
return 0;
}
static u32 get_frame_type_flag(struct go7007_buffer *gobuf, int format)
{
u8 *f = page_address(gobuf->pages[0]);
switch (format) {
case GO7007_FORMAT_MJPEG:
return V4L2_BUF_FLAG_KEYFRAME;
case GO7007_FORMAT_MPEG4:
switch ((f[gobuf->frame_offset + 4] >> 6) & 0x3) {
case 0:
return V4L2_BUF_FLAG_KEYFRAME;
case 1:
return V4L2_BUF_FLAG_PFRAME;
case 2:
return V4L2_BUF_FLAG_BFRAME;
default:
return 0;
}
case GO7007_FORMAT_MPEG1:
case GO7007_FORMAT_MPEG2:
switch ((f[gobuf->frame_offset + 5] >> 3) & 0x7) {
case 1:
return V4L2_BUF_FLAG_KEYFRAME;
case 2:
return V4L2_BUF_FLAG_PFRAME;
case 3:
return V4L2_BUF_FLAG_BFRAME;
default:
return 0;
}
}
return 0;
}
static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try)
{
int sensor_height = 0, sensor_width = 0;
int width, height, i;
if (fmt != NULL && fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG &&
fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG &&
fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG4)
return -EINVAL;
switch (go->standard) {
case GO7007_STD_NTSC:
sensor_width = 720;
sensor_height = 480;
break;
case GO7007_STD_PAL:
sensor_width = 720;
sensor_height = 576;
break;
case GO7007_STD_OTHER:
sensor_width = go->board_info->sensor_width;
sensor_height = go->board_info->sensor_height;
break;
}
if (fmt == NULL) {
width = sensor_width;
height = sensor_height;
} else if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
if (fmt->fmt.pix.width > sensor_width)
width = sensor_width;
else if (fmt->fmt.pix.width < 144)
width = 144;
else
width = fmt->fmt.pix.width & ~0x0f;
if (fmt->fmt.pix.height > sensor_height)
height = sensor_height;
else if (fmt->fmt.pix.height < 96)
height = 96;
else
height = fmt->fmt.pix.height & ~0x0f;
} else {
int requested_size = fmt->fmt.pix.width * fmt->fmt.pix.height;
int sensor_size = sensor_width * sensor_height;
if (64 * requested_size < 9 * sensor_size) {
width = sensor_width / 4;
height = sensor_height / 4;
} else if (64 * requested_size < 36 * sensor_size) {
width = sensor_width / 2;
height = sensor_height / 2;
} else {
width = sensor_width;
height = sensor_height;
}
width &= ~0xf;
height &= ~0xf;
}
if (fmt != NULL) {
u32 pixelformat = fmt->fmt.pix.pixelformat;
memset(fmt, 0, sizeof(*fmt));
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt->fmt.pix.width = width;
fmt->fmt.pix.height = height;
fmt->fmt.pix.pixelformat = pixelformat;
fmt->fmt.pix.field = V4L2_FIELD_NONE;
fmt->fmt.pix.bytesperline = 0;
fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE;
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* ?? */
}
if (try)
return 0;
go->width = width;
go->height = height;
go->encoder_h_offset = go->board_info->sensor_h_offset;
go->encoder_v_offset = go->board_info->sensor_v_offset;
for (i = 0; i < 4; ++i)
go->modet[i].enable = 0;
for (i = 0; i < 1624; ++i)
go->modet_map[i] = 0;
if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
struct video_decoder_resolution res;
res.width = width;
if (height > sensor_height / 2) {
res.height = height / 2;
go->encoder_v_halve = 0;
} else {
res.height = height;
go->encoder_v_halve = 1;
}
if (go->i2c_adapter_online)
i2c_clients_command(&go->i2c_adapter,
DECODER_SET_RESOLUTION, &res);
} else {
if (width <= sensor_width / 4) {
go->encoder_h_halve = 1;
go->encoder_v_halve = 1;
go->encoder_subsample = 1;
} else if (width <= sensor_width / 2) {
go->encoder_h_halve = 1;
go->encoder_v_halve = 1;
go->encoder_subsample = 0;
} else {
go->encoder_h_halve = 0;
go->encoder_v_halve = 0;
go->encoder_subsample = 0;
}
}
if (fmt == NULL)
return 0;
switch (fmt->fmt.pix.pixelformat) {
case V4L2_PIX_FMT_MPEG:
if (go->format == GO7007_FORMAT_MPEG1 ||
go->format == GO7007_FORMAT_MPEG2 ||
go->format == GO7007_FORMAT_MPEG4)
break;
go->format = GO7007_FORMAT_MPEG1;
go->pali = 0;
go->aspect_ratio = GO7007_RATIO_1_1;
go->gop_size = go->sensor_framerate / 1000;
go->ipb = 0;
go->closed_gop = 1;
go->repeat_seqhead = 1;
go->seq_header_enable = 1;
go->gop_header_enable = 1;
go->dvd_mode = 0;
break;
/* Backwards compatibility only! */
case V4L2_PIX_FMT_MPEG4:
if (go->format == GO7007_FORMAT_MPEG4)
break;
go->format = GO7007_FORMAT_MPEG4;
go->pali = 0xf5;
go->aspect_ratio = GO7007_RATIO_1_1;
go->gop_size = go->sensor_framerate / 1000;
go->ipb = 0;
go->closed_gop = 1;
go->repeat_seqhead = 1;
go->seq_header_enable = 1;
go->gop_header_enable = 1;
go->dvd_mode = 0;
break;
case V4L2_PIX_FMT_MJPEG:
go->format = GO7007_FORMAT_MJPEG;
go->pali = 0;
go->aspect_ratio = GO7007_RATIO_1_1;
go->gop_size = 0;
go->ipb = 0;
go->closed_gop = 0;
go->repeat_seqhead = 0;
go->seq_header_enable = 0;
go->gop_header_enable = 0;
go->dvd_mode = 0;
break;
}
return 0;
}
static int clip_to_modet_map(struct go7007 *go, int region,
struct v4l2_clip *clip_list)
{
struct v4l2_clip clip, *clip_ptr;
int x, y, mbnum;
/* Check if coordinates are OK and if any macroblocks are already
* used by other regions (besides 0) */
clip_ptr = clip_list;
while (clip_ptr) {
if (copy_from_user(&clip, clip_ptr, sizeof(clip)))
return -EFAULT;
if (clip.c.left < 0 || (clip.c.left & 0xF) ||
clip.c.width <= 0 || (clip.c.width & 0xF))
return -EINVAL;
if (clip.c.left + clip.c.width > go->width)
return -EINVAL;
if (clip.c.top < 0 || (clip.c.top & 0xF) ||
clip.c.height <= 0 || (clip.c.height & 0xF))
return -EINVAL;
if (clip.c.top + clip.c.height > go->height)
return -EINVAL;
for (y = 0; y < clip.c.height; y += 16)
for (x = 0; x < clip.c.width; x += 16) {
mbnum = (go->width >> 4) *
((clip.c.top + y) >> 4) +
((clip.c.left + x) >> 4);
if (go->modet_map[mbnum] != 0 &&
go->modet_map[mbnum] != region)
return -EBUSY;
}
clip_ptr = clip.next;
}
/* Clear old region macroblocks */
for (mbnum = 0; mbnum < 1624; ++mbnum)
if (go->modet_map[mbnum] == region)
go->modet_map[mbnum] = 0;
/* Claim macroblocks in this list */
clip_ptr = clip_list;
while (clip_ptr) {
if (copy_from_user(&clip, clip_ptr, sizeof(clip)))
return -EFAULT;
for (y = 0; y < clip.c.height; y += 16)
for (x = 0; x < clip.c.width; x += 16) {
mbnum = (go->width >> 4) *
((clip.c.top + y) >> 4) +
((clip.c.left + x) >> 4);
go->modet_map[mbnum] = region;
}
clip_ptr = clip.next;
}
return 0;
}
static int go7007_do_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg)
{
struct go7007_file *gofh = file->private_data;
struct go7007 *go = gofh->go;
unsigned long flags;
int retval = 0;
switch (cmd) {
case VIDIOC_QUERYCAP:
{
struct v4l2_capability *cap = arg;
memset(cap, 0, sizeof(*cap));
strcpy(cap->driver, "go7007");
strncpy(cap->card, go->name, sizeof(cap->card));
cap->version = KERNEL_VERSION(0, 9, 8);
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING; /* | V4L2_CAP_AUDIO; */
if (go->board_info->flags & GO7007_BOARD_HAS_TUNER)
cap->capabilities |= V4L2_CAP_TUNER;
return 0;
}
case VIDIOC_ENUM_FMT:
{
struct v4l2_fmtdesc *fmt = arg;
unsigned int index;
char *desc;
u32 pixelformat;
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
switch (fmt->index) {
case 0:
pixelformat = V4L2_PIX_FMT_MJPEG;
desc = "Motion-JPEG";
break;
case 1:
pixelformat = V4L2_PIX_FMT_MPEG;
desc = "MPEG1/MPEG2/MPEG4";
break;
default:
return -EINVAL;
}
index = fmt->index;
memset(fmt, 0, sizeof(*fmt));
fmt->index = index;
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt->flags = V4L2_FMT_FLAG_COMPRESSED;
strncpy(fmt->description, desc, sizeof(fmt->description));
fmt->pixelformat = pixelformat;
return 0;
}
case VIDIOC_TRY_FMT:
{
struct v4l2_format *fmt = arg;
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
return set_capture_size(go, fmt, 1);
}
case VIDIOC_G_FMT:
{
struct v4l2_format *fmt = arg;
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
memset(fmt, 0, sizeof(*fmt));
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt->fmt.pix.width = go->width;
fmt->fmt.pix.height = go->height;
fmt->fmt.pix.pixelformat = go->format == GO7007_FORMAT_MJPEG ?
V4L2_PIX_FMT_MJPEG : V4L2_PIX_FMT_MPEG;
fmt->fmt.pix.field = V4L2_FIELD_NONE;
fmt->fmt.pix.bytesperline = 0;
fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE;
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* ?? */
return 0;
}
case VIDIOC_S_FMT:
{
struct v4l2_format *fmt = arg;
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (go->streaming)
return -EBUSY;
return set_capture_size(go, fmt, 0);
}
case VIDIOC_G_FBUF:
case VIDIOC_S_FBUF:
return -EINVAL;
case VIDIOC_REQBUFS:
{
struct v4l2_requestbuffers *req = arg;
unsigned int count, i;
if (go->streaming)
return -EBUSY;
if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
req->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
down(&gofh->lock);
retval = -EBUSY;
for (i = 0; i < gofh->buf_count; ++i)
if (gofh->bufs[i].mapped > 0)
goto unlock_and_return;
down(&go->hw_lock);
if (go->in_use > 0 && gofh->buf_count == 0) {
up(&go->hw_lock);
goto unlock_and_return;
}
if (gofh->buf_count > 0)
kfree(gofh->bufs);
retval = -ENOMEM;
count = req->count;
if (count > 0) {
if (count < 2)
count = 2;
if (count > 32)
count = 32;
gofh->bufs = kmalloc(count *
sizeof(struct go7007_buffer),
GFP_KERNEL);
if (gofh->bufs == NULL) {
up(&go->hw_lock);
goto unlock_and_return;
}
memset(gofh->bufs, 0,
count * sizeof(struct go7007_buffer));
for (i = 0; i < count; ++i) {
gofh->bufs[i].go = go;
gofh->bufs[i].index = i;
gofh->bufs[i].state = BUF_STATE_IDLE;
gofh->bufs[i].mapped = 0;
}
go->in_use = 1;
} else {
go->in_use = 0;
}
gofh->buf_count = count;
up(&go->hw_lock);
up(&gofh->lock);
memset(req, 0, sizeof(*req));
req->count = count;
req->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req->memory = V4L2_MEMORY_MMAP;
return 0;
}
case VIDIOC_QUERYBUF:
{
struct v4l2_buffer *buf = arg;
unsigned int index;
retval = -EINVAL;
if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
index = buf->index;
down(&gofh->lock);
if (index >= gofh->buf_count)
goto unlock_and_return;
memset(buf, 0, sizeof(*buf));
buf->index = index;
buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
switch (gofh->bufs[index].state) {
case BUF_STATE_QUEUED:
buf->flags = V4L2_BUF_FLAG_QUEUED;
break;
case BUF_STATE_DONE:
buf->flags = V4L2_BUF_FLAG_DONE;
break;
default:
buf->flags = 0;
}
if (gofh->bufs[index].mapped)
buf->flags |= V4L2_BUF_FLAG_MAPPED;
buf->memory = V4L2_MEMORY_MMAP;
buf->m.offset = index * GO7007_BUF_SIZE;
buf->length = GO7007_BUF_SIZE;
up(&gofh->lock);
return 0;
}
case VIDIOC_QBUF:
{
struct v4l2_buffer *buf = arg;
struct go7007_buffer *gobuf;
int ret;
retval = -EINVAL;
if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
buf->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
down(&gofh->lock);
if (buf->index < 0 || buf->index >= gofh->buf_count)
goto unlock_and_return;
gobuf = &gofh->bufs[buf->index];
if (gobuf->mapped == 0)
goto unlock_and_return;
retval = -EBUSY;
if (gobuf->state != BUF_STATE_IDLE)
goto unlock_and_return;
/* offset will be 0 until we really support USERPTR streaming */
gobuf->offset = gobuf->user_addr & ~PAGE_MASK;
gobuf->bytesused = 0;
gobuf->frame_offset = 0;
gobuf->modet_active = 0;
if (gobuf->offset > 0)
gobuf->page_count = GO7007_BUF_PAGES + 1;
else
gobuf->page_count = GO7007_BUF_PAGES;
retval = -ENOMEM;
down_read(&current->mm->mmap_sem);
ret = get_user_pages(current, current->mm,
gobuf->user_addr & PAGE_MASK, gobuf->page_count,
1, 1, gobuf->pages, NULL);
up_read(&current->mm->mmap_sem);
if (ret != gobuf->page_count) {
int i;
for (i = 0; i < ret; ++i)
page_cache_release(gobuf->pages[i]);
gobuf->page_count = 0;
goto unlock_and_return;
}
gobuf->state = BUF_STATE_QUEUED;
spin_lock_irqsave(&go->spinlock, flags);
list_add_tail(&gobuf->stream, &go->stream);
spin_unlock_irqrestore(&go->spinlock, flags);
up(&gofh->lock);
return 0;
}
case VIDIOC_DQBUF:
{
struct v4l2_buffer *buf = arg;
struct go7007_buffer *gobuf;
u32 frame_type_flag;
DEFINE_WAIT(wait);
if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (buf->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
down(&gofh->lock);
retval = -EINVAL;
if (list_empty(&go->stream))
goto unlock_and_return;
gobuf = list_entry(go->stream.next,
struct go7007_buffer, stream);
retval = -EAGAIN;
if (gobuf->state != BUF_STATE_DONE &&
!(file->f_flags & O_NONBLOCK)) {
for (;;) {
prepare_to_wait(&go->frame_waitq, &wait,
TASK_INTERRUPTIBLE);
if (gobuf->state == BUF_STATE_DONE)
break;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
schedule();
}
finish_wait(&go->frame_waitq, &wait);
}
if (gobuf->state != BUF_STATE_DONE)
goto unlock_and_return;
spin_lock_irqsave(&go->spinlock, flags);
deactivate_buffer(gobuf);
spin_unlock_irqrestore(&go->spinlock, flags);
frame_type_flag = get_frame_type_flag(gobuf, go->format);
gobuf->state = BUF_STATE_IDLE;
memset(buf, 0, sizeof(*buf));
buf->index = gobuf->index;
buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf->bytesused = gobuf->bytesused;
buf->flags = V4L2_BUF_FLAG_MAPPED | frame_type_flag;
buf->field = V4L2_FIELD_NONE;
buf->timestamp = gobuf->timestamp;
buf->sequence = gobuf->seq;
buf->memory = V4L2_MEMORY_MMAP;
buf->m.offset = gobuf->index * GO7007_BUF_SIZE;
buf->length = GO7007_BUF_SIZE;
buf->reserved = gobuf->modet_active;
up(&gofh->lock);
return 0;
}
case VIDIOC_STREAMON:
{
unsigned int *type = arg;
if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
down(&gofh->lock);
down(&go->hw_lock);
if (!go->streaming) {
go->streaming = 1;
go->next_seq = 0;
go->active_buf = NULL;
if (go7007_start_encoder(go) < 0)
retval = -EIO;
else
retval = 0;
}
up(&go->hw_lock);
up(&gofh->lock);
return retval;
}
case VIDIOC_STREAMOFF:
{
unsigned int *type = arg;
if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
down(&gofh->lock);
go7007_streamoff(go);
up(&gofh->lock);
return 0;
}
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *ctrl = arg;
u32 id;
if (!go->i2c_adapter_online)
return -EIO;
id = ctrl->id;
memset(ctrl, 0, sizeof(*ctrl));
ctrl->id = id;
i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, arg);
return ctrl->name[0] == 0 ? -EINVAL : 0;
}
case VIDIOC_G_CTRL:
{
struct v4l2_control *ctrl = arg;
struct v4l2_queryctrl query;
if (!go->i2c_adapter_online)
return -EIO;
memset(&query, 0, sizeof(query));
query.id = ctrl->id;
i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query);
if (query.name[0] == 0)
return -EINVAL;
i2c_clients_command(&go->i2c_adapter, VIDIOC_G_CTRL, arg);
return 0;
}
case VIDIOC_S_CTRL:
{
struct v4l2_control *ctrl = arg;
struct v4l2_queryctrl query;
if (!go->i2c_adapter_online)
return -EIO;
memset(&query, 0, sizeof(query));
query.id = ctrl->id;
i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query);
if (query.name[0] == 0)
return -EINVAL;
i2c_clients_command(&go->i2c_adapter, VIDIOC_S_CTRL, arg);
return 0;
}
case VIDIOC_G_PARM:
{
struct v4l2_streamparm *parm = arg;
struct v4l2_fract timeperframe = {
.numerator = 1001 * go->fps_scale,
.denominator = go->sensor_framerate,
};
if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
memset(parm, 0, sizeof(*parm));
parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME;
parm->parm.capture.timeperframe = timeperframe;
return 0;
}
case VIDIOC_S_PARM:
{
struct v4l2_streamparm *parm = arg;
unsigned int n, d;
if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (parm->parm.capture.capturemode != 0)
return -EINVAL;
n = go->sensor_framerate *
parm->parm.capture.timeperframe.numerator;
d = 1001 * parm->parm.capture.timeperframe.denominator;
if (n != 0 && d != 0 && n > d)
go->fps_scale = (n + d/2) / d;
else
go->fps_scale = 1;
return 0;
}
case VIDIOC_ENUMSTD:
{
struct v4l2_standard *std = arg;
if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
go->input == go->board_info->num_inputs - 1) {
if (!go->i2c_adapter_online)
return -EIO;
i2c_clients_command(&go->i2c_adapter,
VIDIOC_ENUMSTD, arg);
if (!std->id) /* hack to indicate EINVAL from tuner */
return -EINVAL;
} else if (go->board_info->sensor_flags & GO7007_SENSOR_TV) {
switch (std->index) {
case 0:
v4l2_video_std_construct(std,
V4L2_STD_NTSC, "NTSC");
break;
case 1:
v4l2_video_std_construct(std,
V4L2_STD_PAL | V4L2_STD_SECAM,
"PAL/SECAM");
break;
default:
return -EINVAL;
}
} else {
if (std->index != 0)
return -EINVAL;
memset(std, 0, sizeof(*std));
snprintf(std->name, sizeof(std->name), "%dx%d, %dfps",
go->board_info->sensor_width,
go->board_info->sensor_height,
go->board_info->sensor_framerate / 1000);
std->frameperiod.numerator = 1001;
std->frameperiod.denominator =
go->board_info->sensor_framerate;
}
return 0;
}
case VIDIOC_G_STD:
{
v4l2_std_id *std = arg;
if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
go->input == go->board_info->num_inputs - 1) {
if (!go->i2c_adapter_online)
return -EIO;
i2c_clients_command(&go->i2c_adapter,
VIDIOC_G_STD, arg);
} else if (go->board_info->sensor_flags & GO7007_SENSOR_TV) {
if (go->standard == GO7007_STD_NTSC)
*std = V4L2_STD_NTSC;
else
*std = V4L2_STD_PAL | V4L2_STD_SECAM;
} else
*std = 0;
return 0;
}
case VIDIOC_S_STD:
{
v4l2_std_id *std = arg;
int norm;
if (go->streaming)
return -EBUSY;
if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) &&
*std != 0)
return -EINVAL;
if (*std == 0)
return -EINVAL;
if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
go->input == go->board_info->num_inputs - 1) {
if (!go->i2c_adapter_online)
return -EIO;
i2c_clients_command(&go->i2c_adapter,
VIDIOC_S_STD, arg);
if (!*std) /* hack to indicate EINVAL from tuner */
return -EINVAL;
}
if (*std & V4L2_STD_NTSC) {
go->standard = GO7007_STD_NTSC;
go->sensor_framerate = 30000;
norm = VIDEO_MODE_NTSC;
} else if (*std & V4L2_STD_PAL) {
go->standard = GO7007_STD_PAL;
go->sensor_framerate = 25025;
norm = VIDEO_MODE_PAL;
} else if (*std & V4L2_STD_SECAM) {
go->standard = GO7007_STD_PAL;
go->sensor_framerate = 25025;
norm = VIDEO_MODE_SECAM;
} else
return -EINVAL;
if (go->i2c_adapter_online)
i2c_clients_command(&go->i2c_adapter,
DECODER_SET_NORM, &norm);
set_capture_size(go, NULL, 0);
return 0;
}
case VIDIOC_QUERYSTD:
{
v4l2_std_id *std = arg;
if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
go->input == go->board_info->num_inputs - 1) {
if (!go->i2c_adapter_online)
return -EIO;
i2c_clients_command(&go->i2c_adapter,
VIDIOC_QUERYSTD, arg);
} else if (go->board_info->sensor_flags & GO7007_SENSOR_TV)
*std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
else
*std = 0;
return 0;
}
case VIDIOC_ENUMINPUT:
{
struct v4l2_input *inp = arg;
int index;
if (inp->index >= go->board_info->num_inputs)
return -EINVAL;
index = inp->index;
memset(inp, 0, sizeof(*inp));
inp->index = index;
strncpy(inp->name, go->board_info->inputs[index].name,
sizeof(inp->name));
/* If this board has a tuner, it will be the last input */
if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
index == go->board_info->num_inputs - 1)
inp->type = V4L2_INPUT_TYPE_TUNER;
else
inp->type = V4L2_INPUT_TYPE_CAMERA;
inp->audioset = 0;
inp->tuner = 0;
if (go->board_info->sensor_flags & GO7007_SENSOR_TV)
inp->std = V4L2_STD_NTSC | V4L2_STD_PAL |
V4L2_STD_SECAM;
else
inp->std = 0;
return 0;
}
case VIDIOC_G_INPUT:
{
int *input = arg;
*input = go->input;
return 0;
}
case VIDIOC_S_INPUT:
{
int *input = arg;
if (*input >= go->board_info->num_inputs)
return -EINVAL;
if (go->streaming)
return -EBUSY;
go->input = *input;
if (go->i2c_adapter_online) {
i2c_clients_command(&go->i2c_adapter, DECODER_SET_INPUT,
&go->board_info->inputs[*input].video_input);
i2c_clients_command(&go->i2c_adapter, VIDIOC_S_AUDIO,
&go->board_info->inputs[*input].audio_input);
}
return 0;
}
case VIDIOC_G_TUNER:
{
struct v4l2_tuner *t = arg;
if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
return -EINVAL;
if (t->index != 0)
return -EINVAL;
if (!go->i2c_adapter_online)
return -EIO;
i2c_clients_command(&go->i2c_adapter, VIDIOC_G_TUNER, arg);
t->index = 0;
return 0;
}
case VIDIOC_S_TUNER:
{
struct v4l2_tuner *t = arg;
if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
return -EINVAL;
if (t->index != 0)
return -EINVAL;
if (!go->i2c_adapter_online)
return -EIO;
switch (go->board_id) {
case GO7007_BOARDID_PX_TV402U_NA:
case GO7007_BOARDID_PX_TV402U_JP:
/* No selectable options currently */
if (t->audmode != V4L2_TUNER_MODE_STEREO)
return -EINVAL;
break;
}
i2c_clients_command(&go->i2c_adapter, VIDIOC_S_TUNER, arg);
return 0;
}
case VIDIOC_G_FREQUENCY:
{
struct v4l2_frequency *f = arg;
if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
return -EINVAL;
if (!go->i2c_adapter_online)
return -EIO;
memset(f, 0, sizeof(*f));
f->type = V4L2_TUNER_ANALOG_TV;
i2c_clients_command(&go->i2c_adapter, VIDIOC_G_FREQUENCY, arg);
return 0;
}
case VIDIOC_S_FREQUENCY:
{
if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
return -EINVAL;
if (!go->i2c_adapter_online)
return -EIO;
i2c_clients_command(&go->i2c_adapter, VIDIOC_S_FREQUENCY, arg);
return 0;
}
case VIDIOC_CROPCAP:
{
struct v4l2_cropcap *cropcap = arg;
if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
memset(cropcap, 0, sizeof(*cropcap));
cropcap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/* These specify the raw input of the sensor */
switch (go->standard) {
case GO7007_STD_NTSC:
cropcap->bounds.top = 0;
cropcap->bounds.left = 0;
cropcap->bounds.width = 720;
cropcap->bounds.height = 480;
cropcap->defrect.top = 0;
cropcap->defrect.left = 0;
cropcap->defrect.width = 720;
cropcap->defrect.height = 480;
break;
case GO7007_STD_PAL:
cropcap->bounds.top = 0;
cropcap->bounds.left = 0;
cropcap->bounds.width = 720;
cropcap->bounds.height = 576;
cropcap->defrect.top = 0;
cropcap->defrect.left = 0;
cropcap->defrect.width = 720;
cropcap->defrect.height = 576;
break;
case GO7007_STD_OTHER:
cropcap->bounds.top = 0;
cropcap->bounds.left = 0;
cropcap->bounds.width = go->board_info->sensor_width;
cropcap->bounds.height = go->board_info->sensor_height;
cropcap->defrect.top = 0;
cropcap->defrect.left = 0;
cropcap->defrect.width = go->board_info->sensor_width;
cropcap->defrect.height = go->board_info->sensor_height;
break;
}
return 0;
}
case VIDIOC_G_CROP:
{
struct v4l2_crop *crop = arg;
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
memset(crop, 0, sizeof(*crop));
crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/* These specify the raw input of the sensor */
switch (go->standard) {
case GO7007_STD_NTSC:
crop->c.top = 0;
crop->c.left = 0;
crop->c.width = 720;
crop->c.height = 480;
break;
case GO7007_STD_PAL:
crop->c.top = 0;
crop->c.left = 0;
crop->c.width = 720;
crop->c.height = 576;
break;
case GO7007_STD_OTHER:
crop->c.top = 0;
crop->c.left = 0;
crop->c.width = go->board_info->sensor_width;
crop->c.height = go->board_info->sensor_height;
break;
}
return 0;
}
case VIDIOC_S_CROP:
{
struct v4l2_crop *crop = arg;
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
return 0;
}
case VIDIOC_G_JPEGCOMP:
{
struct v4l2_jpegcompression *params = arg;
memset(params, 0, sizeof(*params));
params->quality = 50; /* ?? */
params->jpeg_markers = V4L2_JPEG_MARKER_DHT |
V4L2_JPEG_MARKER_DQT;
return 0;
}
case VIDIOC_S_JPEGCOMP:
{
struct v4l2_jpegcompression *params = arg;
if (params->quality != 50 ||
params->jpeg_markers != (V4L2_JPEG_MARKER_DHT |
V4L2_JPEG_MARKER_DQT))
return -EINVAL;
return 0;
}
/* Temporary ioctls for controlling compression characteristics */
case GO7007IOC_S_BITRATE:
{
int *bitrate = arg;
if (go->streaming)
return -EINVAL;
/* Upper bound is kind of arbitrary here */
if (*bitrate < 64000 || *bitrate > 10000000)
return -EINVAL;
go->bitrate = *bitrate;
return 0;
}
case GO7007IOC_G_BITRATE:
{
int *bitrate = arg;
*bitrate = go->bitrate;
return 0;
}
case GO7007IOC_S_COMP_PARAMS:
{
struct go7007_comp_params *comp = arg;
if (go->format == GO7007_FORMAT_MJPEG)
return -EINVAL;
if (comp->gop_size > 0)
go->gop_size = comp->gop_size;
else
go->gop_size = go->sensor_framerate / 1000;
if (go->gop_size != 15)
go->dvd_mode = 0;
/*go->ipb = comp->max_b_frames > 0;*/ /* completely untested */
if (go->board_info->sensor_flags & GO7007_SENSOR_TV) {
switch (comp->aspect_ratio) {
case GO7007_ASPECT_RATIO_4_3_NTSC:
case GO7007_ASPECT_RATIO_4_3_PAL:
go->aspect_ratio = GO7007_RATIO_4_3;
break;
case GO7007_ASPECT_RATIO_16_9_NTSC:
case GO7007_ASPECT_RATIO_16_9_PAL:
go->aspect_ratio = GO7007_RATIO_16_9;
break;
default:
go->aspect_ratio = GO7007_RATIO_1_1;
break;
}
}
if (comp->flags & GO7007_COMP_OMIT_SEQ_HEADER) {
go->dvd_mode = 0;
go->seq_header_enable = 0;
} else {
go->seq_header_enable = 1;
}
/* fall-through */
}
case GO7007IOC_G_COMP_PARAMS:
{
struct go7007_comp_params *comp = arg;
if (go->format == GO7007_FORMAT_MJPEG)
return -EINVAL;
memset(comp, 0, sizeof(*comp));
comp->gop_size = go->gop_size;
comp->max_b_frames = go->ipb ? 2 : 0;
switch (go->aspect_ratio) {
case GO7007_RATIO_4_3:
if (go->standard == GO7007_STD_NTSC)
comp->aspect_ratio =
GO7007_ASPECT_RATIO_4_3_NTSC;
else
comp->aspect_ratio =
GO7007_ASPECT_RATIO_4_3_PAL;
break;
case GO7007_RATIO_16_9:
if (go->standard == GO7007_STD_NTSC)
comp->aspect_ratio =
GO7007_ASPECT_RATIO_16_9_NTSC;
else
comp->aspect_ratio =
GO7007_ASPECT_RATIO_16_9_PAL;
break;
default:
comp->aspect_ratio = GO7007_ASPECT_RATIO_1_1;
break;
}
if (go->closed_gop)
comp->flags |= GO7007_COMP_CLOSED_GOP;
if (!go->seq_header_enable)
comp->flags |= GO7007_COMP_OMIT_SEQ_HEADER;
return 0;
}
case GO7007IOC_S_MPEG_PARAMS:
{
struct go7007_mpeg_params *mpeg = arg;
if (go->format != GO7007_FORMAT_MPEG1 &&
go->format != GO7007_FORMAT_MPEG2 &&
go->format != GO7007_FORMAT_MPEG4)
return -EINVAL;
if (mpeg->flags & GO7007_MPEG_FORCE_DVD_MODE) {
go->format = GO7007_FORMAT_MPEG2;
go->bitrate = 9800000;
go->gop_size = 15;
go->pali = 0x48;
go->closed_gop = 1;
go->repeat_seqhead = 0;
go->seq_header_enable = 1;
go->gop_header_enable = 1;
go->dvd_mode = 1;
} else {
switch (mpeg->mpeg_video_standard) {
case GO7007_MPEG_VIDEO_MPEG1:
go->format = GO7007_FORMAT_MPEG1;
go->pali = 0;
break;
case GO7007_MPEG_VIDEO_MPEG2:
go->format = GO7007_FORMAT_MPEG2;
if (mpeg->pali >> 24 == 2)
go->pali = mpeg->pali & 0xff;
else
go->pali = 0x48;
break;
case GO7007_MPEG_VIDEO_MPEG4:
go->format = GO7007_FORMAT_MPEG4;
if (mpeg->pali >> 24 == 4)
go->pali = mpeg->pali & 0xff;
else
go->pali = 0xf5;
break;
default:
return -EINVAL;
}
go->gop_header_enable =
mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER
? 0 : 1;
if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER)
go->repeat_seqhead = 1;
else
go->repeat_seqhead = 0;
go->dvd_mode = 0;
}
/* fall-through */
}
case GO7007IOC_G_MPEG_PARAMS:
{
struct go7007_mpeg_params *mpeg = arg;
memset(mpeg, 0, sizeof(*mpeg));
switch (go->format) {
case GO7007_FORMAT_MPEG1:
mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG1;
mpeg->pali = 0;
break;
case GO7007_FORMAT_MPEG2:
mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG2;
mpeg->pali = GO7007_MPEG_PROFILE(2, go->pali);
break;
case GO7007_FORMAT_MPEG4:
mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG4;
mpeg->pali = GO7007_MPEG_PROFILE(4, go->pali);
break;
default:
return -EINVAL;
}
if (!go->gop_header_enable)
mpeg->flags |= GO7007_MPEG_OMIT_GOP_HEADER;
if (go->repeat_seqhead)
mpeg->flags |= GO7007_MPEG_REPEAT_SEQHEADER;
if (go->dvd_mode)
mpeg->flags |= GO7007_MPEG_FORCE_DVD_MODE;
return 0;
}
case GO7007IOC_S_MD_PARAMS:
{
struct go7007_md_params *mdp = arg;
if (mdp->region > 3)
return -EINVAL;
if (mdp->trigger > 0) {
go->modet[mdp->region].pixel_threshold =
mdp->pixel_threshold >> 1;
go->modet[mdp->region].motion_threshold =
mdp->motion_threshold >> 1;
go->modet[mdp->region].mb_threshold =
mdp->trigger >> 1;
go->modet[mdp->region].enable = 1;
} else
go->modet[mdp->region].enable = 0;
/* fall-through */
}
case GO7007IOC_G_MD_PARAMS:
{
struct go7007_md_params *mdp = arg;
int region = mdp->region;
if (mdp->region > 3)
return -EINVAL;
memset(mdp, 0, sizeof(struct go7007_md_params));
mdp->region = region;
if (!go->modet[region].enable)
return 0;
mdp->pixel_threshold =
(go->modet[region].pixel_threshold << 1) + 1;
mdp->motion_threshold =
(go->modet[region].motion_threshold << 1) + 1;
mdp->trigger =
(go->modet[region].mb_threshold << 1) + 1;
return 0;
}
case GO7007IOC_S_MD_REGION:
{
struct go7007_md_region *region = arg;
if (region->region < 1 || region->region > 3)
return -EINVAL;
return clip_to_modet_map(go, region->region, region->clips);
}
default:
printk(KERN_DEBUG "go7007: unsupported ioctl %d\n", cmd);
return -ENOIOCTLCMD;
}
return 0;
unlock_and_return:
up(&gofh->lock);
return retval;
}
static int go7007_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct go7007_file *gofh = file->private_data;
if (gofh->go->status != STATUS_ONLINE)
return -EIO;
return video_usercopy(inode, file, cmd, arg, go7007_do_ioctl);
}
static ssize_t go7007_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
{
return -EINVAL;
}
static void go7007_vm_open(struct vm_area_struct *vma)
{
struct go7007_buffer *gobuf = vma->vm_private_data;
++gobuf->mapped;
}
static void go7007_vm_close(struct vm_area_struct *vma)
{
struct go7007_buffer *gobuf = vma->vm_private_data;
unsigned long flags;
if (--gobuf->mapped == 0) {
spin_lock_irqsave(&gobuf->go->spinlock, flags);
deactivate_buffer(gobuf);
spin_unlock_irqrestore(&gobuf->go->spinlock, flags);
}
}
/* Copied from videobuf-dma-sg.c */
static int go7007_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct page *page;
page = alloc_page(GFP_USER | __GFP_DMA32);
if (!page)
return VM_FAULT_OOM;
clear_user_page(page_address(page), (unsigned long)vmf->virtual_address,
page);
vmf->page = page;
return 0;
}
static struct vm_operations_struct go7007_vm_ops = {
.open = go7007_vm_open,
.close = go7007_vm_close,
.fault = go7007_vm_fault,
};
static int go7007_mmap(struct file *file, struct vm_area_struct *vma)
{
struct go7007_file *gofh = file->private_data;
unsigned int index;
if (gofh->go->status != STATUS_ONLINE)
return -EIO;
if (!(vma->vm_flags & VM_SHARED))
return -EINVAL; /* only support VM_SHARED mapping */
if (vma->vm_end - vma->vm_start != GO7007_BUF_SIZE)
return -EINVAL; /* must map exactly one full buffer */
down(&gofh->lock);
index = vma->vm_pgoff / GO7007_BUF_PAGES;
if (index >= gofh->buf_count) {
up(&gofh->lock);
return -EINVAL; /* trying to map beyond requested buffers */
}
if (index * GO7007_BUF_PAGES != vma->vm_pgoff) {
up(&gofh->lock);
return -EINVAL; /* offset is not aligned on buffer boundary */
}
if (gofh->bufs[index].mapped > 0) {
up(&gofh->lock);
return -EBUSY;
}
gofh->bufs[index].mapped = 1;
gofh->bufs[index].user_addr = vma->vm_start;
vma->vm_ops = &go7007_vm_ops;
vma->vm_flags |= VM_DONTEXPAND;
vma->vm_flags &= ~VM_IO;
vma->vm_private_data = &gofh->bufs[index];
up(&gofh->lock);
return 0;
}
static unsigned int go7007_poll(struct file *file, poll_table *wait)
{
struct go7007_file *gofh = file->private_data;
struct go7007_buffer *gobuf;
if (list_empty(&gofh->go->stream))
return POLLERR;
gobuf = list_entry(gofh->go->stream.next, struct go7007_buffer, stream);
poll_wait(file, &gofh->go->frame_waitq, wait);
if (gobuf->state == BUF_STATE_DONE)
return POLLIN | POLLRDNORM;
return 0;
}
static void go7007_vfl_release(struct video_device *vfd)
{
struct go7007 *go = video_get_drvdata(vfd);
video_device_release(vfd);
if (--go->ref_count == 0)
kfree(go);
}
static struct file_operations go7007_fops = {
.owner = THIS_MODULE,
.open = go7007_open,
.release = go7007_release,
.ioctl = go7007_ioctl,
.llseek = no_llseek,
.read = go7007_read,
.mmap = go7007_mmap,
.poll = go7007_poll,
};
static struct video_device go7007_template = {
.name = "go7007",
.fops = &go7007_fops,
.minor = -1,
.release = go7007_vfl_release,
};
int go7007_v4l2_init(struct go7007 *go)
{
int rv;
go->video_dev = video_device_alloc();
if (go->video_dev == NULL)
return -ENOMEM;
memcpy(go->video_dev, &go7007_template, sizeof(go7007_template));
go->video_dev->parent = go->dev;
rv = video_register_device(go->video_dev, VFL_TYPE_GRABBER, -1);
if (rv < 0) {
video_device_release(go->video_dev);
go->video_dev = NULL;
return rv;
}
video_set_drvdata(go->video_dev, go);
++go->ref_count;
return 0;
}
void go7007_v4l2_remove(struct go7007 *go)
{
unsigned long flags;
down(&go->hw_lock);
if (go->streaming) {
go->streaming = 0;
go7007_stream_stop(go);
spin_lock_irqsave(&go->spinlock, flags);
abort_queued(go);
spin_unlock_irqrestore(&go->spinlock, flags);
}
up(&go->hw_lock);
if (go->video_dev)
video_unregister_device(go->video_dev);
}
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and the associated README documentation file (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* DEPRECATED -- use V4L2_PIX_FMT_MPEG and then call GO7007IOC_S_MPEG_PARAMS
* to select between MPEG1, MPEG2, and MPEG4 */
#define V4L2_PIX_FMT_MPEG4 v4l2_fourcc('M', 'P', 'G', '4') /* MPEG4 */
/* These will be replaced with a better interface
* soon, so don't get too attached to them */
#define GO7007IOC_S_BITRATE _IOW('V', BASE_VIDIOC_PRIVATE + 0, int)
#define GO7007IOC_G_BITRATE _IOR('V', BASE_VIDIOC_PRIVATE + 1, int)
enum go7007_aspect_ratio {
GO7007_ASPECT_RATIO_1_1 = 0,
GO7007_ASPECT_RATIO_4_3_NTSC = 1,
GO7007_ASPECT_RATIO_4_3_PAL = 2,
GO7007_ASPECT_RATIO_16_9_NTSC = 3,
GO7007_ASPECT_RATIO_16_9_PAL = 4,
};
/* Used to set generic compression parameters */
struct go7007_comp_params {
__u32 gop_size;
__u32 max_b_frames;
enum go7007_aspect_ratio aspect_ratio;
__u32 flags;
__u32 reserved[8];
};
#define GO7007_COMP_CLOSED_GOP 0x00000001
#define GO7007_COMP_OMIT_SEQ_HEADER 0x00000002
enum go7007_mpeg_video_standard {
GO7007_MPEG_VIDEO_MPEG1 = 0,
GO7007_MPEG_VIDEO_MPEG2 = 1,
GO7007_MPEG_VIDEO_MPEG4 = 2,
};
/* Used to set parameters for V4L2_PIX_FMT_MPEG format */
struct go7007_mpeg_params {
enum go7007_mpeg_video_standard mpeg_video_standard;
__u32 flags;
__u32 pali;
__u32 reserved[8];
};
#define GO7007_MPEG_FORCE_DVD_MODE 0x00000001
#define GO7007_MPEG_OMIT_GOP_HEADER 0x00000002
#define GO7007_MPEG_REPEAT_SEQHEADER 0x00000004
#define GO7007_MPEG_PROFILE(format, pali) (((format)<<24)|(pali))
#define GO7007_MPEG2_PROFILE_MAIN_MAIN GO7007_MPEG_PROFILE(2, 0x48)
#define GO7007_MPEG4_PROFILE_S_L0 GO7007_MPEG_PROFILE(4, 0x08)
#define GO7007_MPEG4_PROFILE_S_L1 GO7007_MPEG_PROFILE(4, 0x01)
#define GO7007_MPEG4_PROFILE_S_L2 GO7007_MPEG_PROFILE(4, 0x02)
#define GO7007_MPEG4_PROFILE_S_L3 GO7007_MPEG_PROFILE(4, 0x03)
#define GO7007_MPEG4_PROFILE_ARTS_L1 GO7007_MPEG_PROFILE(4, 0x91)
#define GO7007_MPEG4_PROFILE_ARTS_L2 GO7007_MPEG_PROFILE(4, 0x92)
#define GO7007_MPEG4_PROFILE_ARTS_L3 GO7007_MPEG_PROFILE(4, 0x93)
#define GO7007_MPEG4_PROFILE_ARTS_L4 GO7007_MPEG_PROFILE(4, 0x94)
#define GO7007_MPEG4_PROFILE_AS_L0 GO7007_MPEG_PROFILE(4, 0xf0)
#define GO7007_MPEG4_PROFILE_AS_L1 GO7007_MPEG_PROFILE(4, 0xf1)
#define GO7007_MPEG4_PROFILE_AS_L2 GO7007_MPEG_PROFILE(4, 0xf2)
#define GO7007_MPEG4_PROFILE_AS_L3 GO7007_MPEG_PROFILE(4, 0xf3)
#define GO7007_MPEG4_PROFILE_AS_L4 GO7007_MPEG_PROFILE(4, 0xf4)
#define GO7007_MPEG4_PROFILE_AS_L5 GO7007_MPEG_PROFILE(4, 0xf5)
struct go7007_md_params {
__u16 region;
__u16 trigger;
__u16 pixel_threshold;
__u16 motion_threshold;
__u32 reserved[8];
};
struct go7007_md_region {
__u16 region;
__u16 flags;
struct v4l2_clip *clips;
__u32 reserved[8];
};
#define GO7007IOC_S_MPEG_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 2, \
struct go7007_mpeg_params)
#define GO7007IOC_G_MPEG_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 3, \
struct go7007_mpeg_params)
#define GO7007IOC_S_COMP_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 4, \
struct go7007_comp_params)
#define GO7007IOC_G_COMP_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 5, \
struct go7007_comp_params)
#define GO7007IOC_S_MD_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 6, \
struct go7007_md_params)
#define GO7007IOC_G_MD_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 7, \
struct go7007_md_params)
#define GO7007IOC_S_MD_REGION _IOW('V', BASE_VIDIOC_PRIVATE + 8, \
struct go7007_md_region)
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/mm.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#include <asm/byteorder.h>
#include <media/audiochip.h>
#include "saa7134-reg.h"
#include "saa7134.h"
#include "go7007-priv.h"
#define GO7007_HPI_DEBUG
enum hpi_address {
HPI_ADDR_VIDEO_BUFFER = 0xe4,
HPI_ADDR_INIT_BUFFER = 0xea,
HPI_ADDR_INTR_RET_VALUE = 0xee,
HPI_ADDR_INTR_RET_DATA = 0xec,
HPI_ADDR_INTR_STATUS = 0xf4,
HPI_ADDR_INTR_WR_PARAM = 0xf6,
HPI_ADDR_INTR_WR_INDEX = 0xf8,
};
enum gpio_command {
GPIO_COMMAND_RESET = 0x00, /* 000b */
GPIO_COMMAND_REQ1 = 0x04, /* 001b */
GPIO_COMMAND_WRITE = 0x20, /* 010b */
GPIO_COMMAND_REQ2 = 0x24, /* 011b */
GPIO_COMMAND_READ = 0x80, /* 100b */
GPIO_COMMAND_VIDEO = 0x84, /* 101b */
GPIO_COMMAND_IDLE = 0xA0, /* 110b */
GPIO_COMMAND_ADDR = 0xA4, /* 111b */
};
struct saa7134_go7007 {
struct saa7134_dev *dev;
u8 *top;
u8 *bottom;
dma_addr_t top_dma;
dma_addr_t bottom_dma;
};
static struct go7007_board_info board_voyager = {
.firmware = "go7007tv.bin",
.flags = 0,
.sensor_flags = GO7007_SENSOR_656 |
GO7007_SENSOR_VALID_ENABLE |
GO7007_SENSOR_TV |
GO7007_SENSOR_VBI,
.audio_flags = GO7007_AUDIO_I2S_MODE_1 |
GO7007_AUDIO_WORD_16,
.audio_rate = 48000,
.audio_bclk_div = 8,
.audio_main_div = 2,
.hpi_buffer_cap = 7,
.num_inputs = 1,
.inputs = {
{
.name = "SAA7134",
},
},
};
/********************* Driver for GPIO HPI interface *********************/
static int gpio_write(struct saa7134_dev *dev, u8 addr, u16 data)
{
saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
/* Write HPI address */
saa_writeb(SAA7134_GPIO_GPSTATUS0, addr);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
/* Write low byte */
saa_writeb(SAA7134_GPIO_GPSTATUS0, data & 0xff);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
/* Write high byte */
saa_writeb(SAA7134_GPIO_GPSTATUS0, data >> 8);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
return 0;
}
static int gpio_read(struct saa7134_dev *dev, u8 addr, u16 *data)
{
saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
/* Write HPI address */
saa_writeb(SAA7134_GPIO_GPSTATUS0, addr);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
saa_writeb(SAA7134_GPIO_GPMODE0, 0x00);
/* Read low byte */
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ);
saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
*data = saa_readb(SAA7134_GPIO_GPSTATUS0);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
/* Read high byte */
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ);
saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
*data |= saa_readb(SAA7134_GPIO_GPSTATUS0) << 8;
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
return 0;
}
static int saa7134_go7007_interface_reset(struct go7007 *go)
{
struct saa7134_go7007 *saa = go->hpi_context;
struct saa7134_dev *dev = saa->dev;
u32 status;
u16 intr_val, intr_data;
int count = 20;
saa_clearb(SAA7134_TS_PARALLEL, 0x80); /* Disable TS interface */
saa_writeb(SAA7134_GPIO_GPMODE2, 0xa4);
saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_RESET);
msleep(1);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2);
msleep(10);
saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
status = saa_readb(SAA7134_GPIO_GPSTATUS2);
/*printk(KERN_DEBUG "status is %s\n", status & 0x40 ? "OK" : "not OK"); */
/* enter command mode...(?) */
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2);
do {
saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
status = saa_readb(SAA7134_GPIO_GPSTATUS2);
/*printk(KERN_INFO "gpio is %08x\n", saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)); */
} while (--count > 0);
/* Wait for an interrupt to indicate successful hardware reset */
if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
(intr_val & ~0x1) != 0x55aa) {
printk(KERN_ERR
"saa7134-go7007: unable to reset the GO7007\n");
return -1;
}
return 0;
}
static int saa7134_go7007_write_interrupt(struct go7007 *go, int addr, int data)
{
struct saa7134_go7007 *saa = go->hpi_context;
struct saa7134_dev *dev = saa->dev;
int i;
u16 status_reg;
#ifdef GO7007_HPI_DEBUG
printk(KERN_DEBUG
"saa7134-go7007: WriteInterrupt: %04x %04x\n", addr, data);
#endif
for (i = 0; i < 100; ++i) {
gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg);
if (!(status_reg & 0x0010))
break;
msleep(10);
}
if (i == 100) {
printk(KERN_ERR
"saa7134-go7007: device is hung, status reg = 0x%04x\n",
status_reg);
return -1;
}
gpio_write(dev, HPI_ADDR_INTR_WR_PARAM, data);
gpio_write(dev, HPI_ADDR_INTR_WR_INDEX, addr);
return 0;
}
static int saa7134_go7007_read_interrupt(struct go7007 *go)
{
struct saa7134_go7007 *saa = go->hpi_context;
struct saa7134_dev *dev = saa->dev;
/* XXX we need to wait if there is no interrupt available */
go->interrupt_available = 1;
gpio_read(dev, HPI_ADDR_INTR_RET_VALUE, &go->interrupt_value);
gpio_read(dev, HPI_ADDR_INTR_RET_DATA, &go->interrupt_data);
#ifdef GO7007_HPI_DEBUG
printk(KERN_DEBUG "saa7134-go7007: ReadInterrupt: %04x %04x\n",
go->interrupt_value, go->interrupt_data);
#endif
return 0;
}
static void saa7134_go7007_irq_ts_done(struct saa7134_dev *dev,
unsigned long status)
{
struct go7007 *go = video_get_drvdata(dev->empress_dev);
struct saa7134_go7007 *saa = go->hpi_context;
if (!go->streaming)
return;
if (0 != (status & 0x000f0000))
printk(KERN_DEBUG "saa7134-go7007: irq: lost %ld\n",
(status >> 16) & 0x0f);
if (status & 0x100000) {
dma_sync_single(&dev->pci->dev,
saa->bottom_dma, PAGE_SIZE, DMA_FROM_DEVICE);
go7007_parse_video_stream(go, saa->bottom, PAGE_SIZE);
saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma));
} else {
dma_sync_single(&dev->pci->dev,
saa->top_dma, PAGE_SIZE, DMA_FROM_DEVICE);
go7007_parse_video_stream(go, saa->top, PAGE_SIZE);
saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma));
}
}
static int saa7134_go7007_stream_start(struct go7007 *go)
{
struct saa7134_go7007 *saa = go->hpi_context;
struct saa7134_dev *dev = saa->dev;
saa->top_dma = dma_map_page(&dev->pci->dev, virt_to_page(saa->top),
0, PAGE_SIZE, DMA_FROM_DEVICE);
if (!saa->top_dma)
return -ENOMEM;
saa->bottom_dma = dma_map_page(&dev->pci->dev,
virt_to_page(saa->bottom),
0, PAGE_SIZE, DMA_FROM_DEVICE);
if (!saa->bottom_dma) {
dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE,
DMA_FROM_DEVICE);
return -ENOMEM;
}
saa_writel(SAA7134_VIDEO_PORT_CTRL0 >> 2, 0xA300B000);
saa_writel(SAA7134_VIDEO_PORT_CTRL4 >> 2, 0x40000200);
/* Set HPI interface for video */
saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_VIDEO_BUFFER);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
saa_writeb(SAA7134_GPIO_GPMODE0, 0x00);
/* Enable TS interface */
saa_writeb(SAA7134_TS_PARALLEL, 0xe6);
/* Reset TS interface */
saa_setb(SAA7134_TS_SERIAL1, 0x01);
saa_clearb(SAA7134_TS_SERIAL1, 0x01);
/* Set up transfer block size */
saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 128 - 1);
saa_writeb(SAA7134_TS_DMA0, (PAGE_SIZE >> 7) - 1);
saa_writeb(SAA7134_TS_DMA1, 0);
saa_writeb(SAA7134_TS_DMA2, 0);
/* Enable video streaming mode */
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_VIDEO);
saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma));
saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma));
saa_writel(SAA7134_RS_PITCH(5), 128);
saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_MAX);
/* Enable TS FIFO */
saa_setl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5);
/* Enable DMA IRQ */
saa_setl(SAA7134_IRQ1,
SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0);
return 0;
}
static int saa7134_go7007_stream_stop(struct go7007 *go)
{
struct saa7134_go7007 *saa = go->hpi_context;
struct saa7134_dev *dev = saa->dev;
/* Shut down TS FIFO */
saa_clearl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5);
/* Disable DMA IRQ */
saa_clearl(SAA7134_IRQ1,
SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0);
/* Disable TS interface */
saa_clearb(SAA7134_TS_PARALLEL, 0x80);
dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE,
DMA_FROM_DEVICE);
dma_unmap_page(&dev->pci->dev, saa->bottom_dma, PAGE_SIZE,
DMA_FROM_DEVICE);
return 0;
}
static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len)
{
struct saa7134_go7007 *saa = go->hpi_context;
struct saa7134_dev *dev = saa->dev;
u16 status_reg;
int i;
#ifdef GO7007_HPI_DEBUG
printk(KERN_DEBUG "saa7134-go7007: DownloadBuffer "
"sending %d bytes\n", len);
#endif
while (len > 0) {
i = len > 64 ? 64 : len;
saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_INIT_BUFFER);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
while (i-- > 0) {
saa_writeb(SAA7134_GPIO_GPSTATUS0, *data);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
++data;
--len;
}
for (i = 0; i < 100; ++i) {
gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg);
if (!(status_reg & 0x0002))
break;
}
if (i == 100) {
printk(KERN_ERR "saa7134-go7007: device is hung, "
"status reg = 0x%04x\n", status_reg);
return -1;
}
}
return 0;
}
static struct go7007_hpi_ops saa7134_go7007_hpi_ops = {
.interface_reset = saa7134_go7007_interface_reset,
.write_interrupt = saa7134_go7007_write_interrupt,
.read_interrupt = saa7134_go7007_read_interrupt,
.stream_start = saa7134_go7007_stream_start,
.stream_stop = saa7134_go7007_stream_stop,
.send_firmware = saa7134_go7007_send_firmware,
};
/********************* Add/remove functions *********************/
static int saa7134_go7007_init(struct saa7134_dev *dev)
{
struct go7007 *go;
struct saa7134_go7007 *saa;
printk(KERN_DEBUG "saa7134-go7007: probing new SAA713X board\n");
saa = kmalloc(sizeof(struct saa7134_go7007), GFP_KERNEL);
if (saa == NULL)
return -ENOMEM;
memset(saa, 0, sizeof(struct saa7134_go7007));
/* Allocate a couple pages for receiving the compressed stream */
saa->top = (u8 *)get_zeroed_page(GFP_KERNEL);
if (!saa->top)
goto allocfail;
saa->bottom = (u8 *)get_zeroed_page(GFP_KERNEL);
if (!saa->bottom)
goto allocfail;
go = go7007_alloc(&board_voyager, &dev->pci->dev);
if (go == NULL)
goto allocfail;
go->board_id = GO7007_BOARDID_PCI_VOYAGER;
strncpy(go->name, saa7134_boards[dev->board].name, sizeof(go->name));
go->hpi_ops = &saa7134_go7007_hpi_ops;
go->hpi_context = saa;
saa->dev = dev;
/* Boot the GO7007 */
if (go7007_boot_encoder(go, go->board_info->flags &
GO7007_BOARD_USE_ONBOARD_I2C) < 0)
goto initfail;
/* Do any final GO7007 initialization, then register the
* V4L2 and ALSA interfaces */
if (go7007_register_encoder(go) < 0)
goto initfail;
dev->empress_dev = go->video_dev;
video_set_drvdata(dev->empress_dev, go);
go->status = STATUS_ONLINE;
return 0;
initfail:
go->status = STATUS_SHUTDOWN;
return 0;
allocfail:
if (saa->top)
free_page((unsigned long)saa->top);
if (saa->bottom)
free_page((unsigned long)saa->bottom);
kfree(saa);
return -ENOMEM;
}
static int saa7134_go7007_fini(struct saa7134_dev *dev)
{
struct go7007 *go;
struct saa7134_go7007 *saa;
if (NULL == dev->empress_dev)
return 0;
go = video_get_drvdata(dev->empress_dev);
saa = go->hpi_context;
go->status = STATUS_SHUTDOWN;
free_page((unsigned long)saa->top);
free_page((unsigned long)saa->bottom);
kfree(saa);
go7007_remove(go);
dev->empress_dev = NULL;
return 0;
}
static struct saa7134_mpeg_ops saa7134_go7007_ops = {
.type = SAA7134_MPEG_GO7007,
.init = saa7134_go7007_init,
.fini = saa7134_go7007_fini,
.irq_ts_done = saa7134_go7007_irq_ts_done,
};
static int __init saa7134_go7007_mod_init(void)
{
return saa7134_ts_register(&saa7134_go7007_ops);
}
static void __exit saa7134_go7007_mod_cleanup(void)
{
saa7134_ts_unregister(&saa7134_go7007_ops);
}
module_init(saa7134_go7007_mod_init);
module_exit(saa7134_go7007_mod_cleanup);
MODULE_LICENSE("GPL v2");
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/vmalloc.h>
#include <linux/time.h>
#include <linux/mm.h>
#include <linux/i2c.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <asm/system.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include "go7007-priv.h"
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
module_param_array(id, charp, NULL, 0444);
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the go7007 audio driver");
MODULE_PARM_DESC(index, "ID string for the go7007 audio driver");
MODULE_PARM_DESC(index, "Enable for the go7007 audio driver");
struct go7007_snd {
struct snd_card *card;
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
spinlock_t lock;
int w_idx;
int hw_ptr;
int avail;
int capturing;
};
static struct snd_pcm_hardware go7007_snd_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
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 = (128*1024),
.period_bytes_min = 4096,
.period_bytes_max = (128*1024),
.periods_min = 1,
.periods_max = 32,
};
static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length)
{
struct go7007_snd *gosnd = go->snd_context;
struct snd_pcm_runtime *runtime = gosnd->substream->runtime;
int frames = bytes_to_frames(runtime, length);
spin_lock(&gosnd->lock);
gosnd->hw_ptr += frames;
if (gosnd->hw_ptr >= runtime->buffer_size)
gosnd->hw_ptr -= runtime->buffer_size;
gosnd->avail += frames;
spin_unlock(&gosnd->lock);
if (gosnd->w_idx + length > runtime->dma_bytes) {
int cpy = runtime->dma_bytes - gosnd->w_idx;
memcpy(runtime->dma_area + gosnd->w_idx, buf, cpy);
length -= cpy;
buf += cpy;
gosnd->w_idx = 0;
}
memcpy(runtime->dma_area + gosnd->w_idx, buf, length);
gosnd->w_idx += length;
spin_lock(&gosnd->lock);
if (gosnd->avail < runtime->period_size) {
spin_unlock(&gosnd->lock);
return;
}
gosnd->avail -= runtime->period_size;
spin_unlock(&gosnd->lock);
if (gosnd->capturing)
snd_pcm_period_elapsed(gosnd->substream);
}
static int go7007_snd_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct go7007 *go = snd_pcm_substream_chip(substream);
unsigned int bytes;
bytes = params_buffer_bytes(hw_params);
if (substream->runtime->dma_bytes > 0)
vfree(substream->runtime->dma_area);
substream->runtime->dma_bytes = 0;
substream->runtime->dma_area = vmalloc(bytes);
if (substream->runtime->dma_area == NULL)
return -ENOMEM;
substream->runtime->dma_bytes = bytes;
go->audio_deliver = parse_audio_stream_data;
return 0;
}
static int go7007_snd_hw_free(struct snd_pcm_substream *substream)
{
struct go7007 *go = snd_pcm_substream_chip(substream);
go->audio_deliver = NULL;
if (substream->runtime->dma_bytes > 0)
vfree(substream->runtime->dma_area);
substream->runtime->dma_bytes = 0;
return 0;
}
static int go7007_snd_capture_open(struct snd_pcm_substream *substream)
{
struct go7007 *go = snd_pcm_substream_chip(substream);
struct go7007_snd *gosnd = go->snd_context;
unsigned long flags;
int r;
spin_lock_irqsave(&gosnd->lock, flags);
if (gosnd->substream == NULL) {
gosnd->substream = substream;
substream->runtime->hw = go7007_snd_capture_hw;
r = 0;
} else
r = -EBUSY;
spin_unlock_irqrestore(&gosnd->lock, flags);
return r;
}
static int go7007_snd_capture_close(struct snd_pcm_substream *substream)
{
struct go7007 *go = snd_pcm_substream_chip(substream);
struct go7007_snd *gosnd = go->snd_context;
gosnd->substream = NULL;
return 0;
}
static int go7007_snd_pcm_prepare(struct snd_pcm_substream *substream)
{
return 0;
}
static int go7007_snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct go7007 *go = snd_pcm_substream_chip(substream);
struct go7007_snd *gosnd = go->snd_context;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
/* Just set a flag to indicate we should signal ALSA when
* sound comes in */
gosnd->capturing = 1;
return 0;
case SNDRV_PCM_TRIGGER_STOP:
gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
gosnd->capturing = 0;
return 0;
default:
return -EINVAL;
}
}
static snd_pcm_uframes_t go7007_snd_pcm_pointer(struct snd_pcm_substream *substream)
{
struct go7007 *go = snd_pcm_substream_chip(substream);
struct go7007_snd *gosnd = go->snd_context;
return gosnd->hw_ptr;
}
static struct page *go7007_snd_pcm_page(struct snd_pcm_substream *substream,
unsigned long offset)
{
return vmalloc_to_page(substream->runtime->dma_area + offset);
}
static struct snd_pcm_ops go7007_snd_capture_ops = {
.open = go7007_snd_capture_open,
.close = go7007_snd_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = go7007_snd_hw_params,
.hw_free = go7007_snd_hw_free,
.prepare = go7007_snd_pcm_prepare,
.trigger = go7007_snd_pcm_trigger,
.pointer = go7007_snd_pcm_pointer,
.page = go7007_snd_pcm_page,
};
static int go7007_snd_free(struct snd_device *device)
{
struct go7007 *go = device->device_data;
kfree(go->snd_context);
go->snd_context = NULL;
if (--go->ref_count == 0)
kfree(go);
return 0;
}
static struct snd_device_ops go7007_snd_device_ops = {
.dev_free = go7007_snd_free,
};
int go7007_snd_init(struct go7007 *go)
{
static int dev;
struct go7007_snd *gosnd;
int ret = 0;
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
dev++;
return -ENOENT;
}
gosnd = kmalloc(sizeof(struct go7007_snd), GFP_KERNEL);
if (gosnd == NULL)
return -ENOMEM;
spin_lock_init(&gosnd->lock);
gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
gosnd->capturing = 0;
gosnd->card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
if (gosnd->card == NULL) {
kfree(gosnd);
return -ENOMEM;
}
ret = snd_device_new(gosnd->card, SNDRV_DEV_LOWLEVEL, go,
&go7007_snd_device_ops);
if (ret < 0) {
kfree(gosnd);
return ret;
}
snd_card_set_dev(gosnd->card, go->dev);
ret = snd_pcm_new(gosnd->card, "go7007", 0, 0, 1, &gosnd->pcm);
if (ret < 0) {
snd_card_free(gosnd->card);
kfree(gosnd);
return ret;
}
strncpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver));
strncpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->driver));
strncpy(gosnd->card->longname, gosnd->card->shortname,
sizeof(gosnd->card->longname));
gosnd->pcm->private_data = go;
snd_pcm_set_ops(gosnd->pcm, SNDRV_PCM_STREAM_CAPTURE,
&go7007_snd_capture_ops);
ret = snd_card_register(gosnd->card);
if (ret < 0) {
snd_card_free(gosnd->card);
kfree(gosnd);
return ret;
}
gosnd->substream = NULL;
go->snd_context = gosnd;
++dev;
++go->ref_count;
return 0;
}
EXPORT_SYMBOL(go7007_snd_init);
int go7007_snd_remove(struct go7007 *go)
{
struct go7007_snd *gosnd = go->snd_context;
snd_card_disconnect(gosnd->card);
snd_card_free_when_closed(gosnd->card);
return 0;
}
EXPORT_SYMBOL(go7007_snd_remove);
MODULE_LICENSE("GPL v2");
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
/* Temporary I2C IDs -- these need to be replaced with real registered IDs */
#define I2C_DRIVERID_WIS_SAA7115 0xf0f0
#define I2C_DRIVERID_WIS_UDA1342 0xf0f1
#define I2C_DRIVERID_WIS_SONY_TUNER 0xf0f2
#define I2C_DRIVERID_WIS_TW9903 0xf0f3
#define I2C_DRIVERID_WIS_SAA7113 0xf0f4
#define I2C_DRIVERID_WIS_OV7640 0xf0f5
#define I2C_DRIVERID_WIS_TW2804 0xf0f6
#define I2C_ALGO_GO7007 0xf00000
#define I2C_ALGO_GO7007_USB 0xf10000
/* Flag to indicate that the client needs to be accessed with SCCB semantics */
/* We re-use the I2C_M_TEN value so the flag passes through the masks in the
* core I2C code. Major kludge, but the I2C layer ain't exactly flexible. */
#define I2C_CLIENT_SCCB 0x10
typedef int (*found_proc) (struct i2c_adapter *, int, int);
int wis_i2c_add_driver(unsigned int id, found_proc found_proc);
void wis_i2c_del_driver(found_proc found_proc);
int wis_i2c_probe_device(struct i2c_adapter *adapter,
unsigned int id, int addr);
/* Definitions for new video decoder commands */
struct video_decoder_resolution {
unsigned int width;
unsigned int height;
};
#define DECODER_SET_RESOLUTION _IOW('d', 200, struct video_decoder_resolution)
#define DECODER_SET_CHANNEL _IOW('d', 201, int)
/* Sony tuner types */
#define TUNER_SONY_BTF_PG472Z 200
#define TUNER_SONY_BTF_PK467Z 201
#define TUNER_SONY_BTF_PB463Z 202
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/i2c.h>
#include <linux/videodev.h>
#include <linux/video_decoder.h>
#include "wis-i2c.h"
struct wis_ov7640 {
int brightness;
int contrast;
int saturation;
int hue;
};
static u8 initial_registers[] =
{
0x12, 0x80,
0x12, 0x54,
0x14, 0x24,
0x15, 0x01,
0x28, 0x20,
0x75, 0x82,
0xFF, 0xFF, /* Terminator (reg 0xFF is unused) */
};
static int write_regs(struct i2c_client *client, u8 *regs)
{
int i;
for (i = 0; regs[i] != 0xFF; i += 2)
if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
return -1;
return 0;
}
static struct i2c_driver wis_ov7640_driver;
static struct i2c_client wis_ov7640_client_templ = {
.name = "OV7640 (WIS)",
.driver = &wis_ov7640_driver,
};
static int wis_ov7640_detect(struct i2c_adapter *adapter, int addr, int kind)
{
struct i2c_client *client;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == NULL)
return -ENOMEM;
memcpy(client, &wis_ov7640_client_templ,
sizeof(wis_ov7640_client_templ));
client->adapter = adapter;
client->addr = addr;
client->flags = I2C_CLIENT_SCCB;
printk(KERN_DEBUG
"wis-ov7640: initializing OV7640 at address %d on %s\n",
addr, adapter->name);
if (write_regs(client, initial_registers) < 0) {
printk(KERN_ERR "wis-ov7640: error initializing OV7640\n");
kfree(client);
return 0;
}
i2c_attach_client(client);
return 0;
}
static int wis_ov7640_detach(struct i2c_client *client)
{
int r;
r = i2c_detach_client(client);
if (r < 0)
return r;
kfree(client);
return 0;
}
static struct i2c_driver wis_ov7640_driver = {
.driver = {
.name = "WIS OV7640 I2C driver",
},
.id = I2C_DRIVERID_WIS_OV7640,
.detach_client = wis_ov7640_detach,
};
static int __init wis_ov7640_init(void)
{
int r;
r = i2c_add_driver(&wis_ov7640_driver);
if (r < 0)
return r;
return wis_i2c_add_driver(wis_ov7640_driver.id, wis_ov7640_detect);
}
static void __exit wis_ov7640_cleanup(void)
{
wis_i2c_del_driver(wis_ov7640_detect);
i2c_del_driver(&wis_ov7640_driver);
}
module_init(wis_ov7640_init);
module_exit(wis_ov7640_cleanup);
MODULE_LICENSE("GPL v2");
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/i2c.h>
#include <linux/videodev.h>
#include <linux/video_decoder.h>
#include <linux/ioctl.h>
#include "wis-i2c.h"
struct wis_saa7113 {
int norm;
int brightness;
int contrast;
int saturation;
int hue;
};
static u8 initial_registers[] =
{
0x01, 0x08,
0x02, 0xc0,
0x03, 0x33,
0x04, 0x00,
0x05, 0x00,
0x06, 0xe9,
0x07, 0x0d,
0x08, 0xd8,
0x09, 0x40,
0x0a, 0x80,
0x0b, 0x47,
0x0c, 0x40,
0x0d, 0x00,
0x0e, 0x01,
0x0f, 0x2a,
0x10, 0x40,
0x11, 0x0c,
0x12, 0xfe,
0x13, 0x00,
0x14, 0x00,
0x15, 0x04,
0x16, 0x00,
0x17, 0x00,
0x18, 0x00,
0x19, 0x00,
0x1a, 0x00,
0x1b, 0x00,
0x1c, 0x00,
0x1d, 0x00,
0x1e, 0x00,
0x1f, 0xc8,
0x40, 0x00,
0x41, 0xff,
0x42, 0xff,
0x43, 0xff,
0x44, 0xff,
0x45, 0xff,
0x46, 0xff,
0x47, 0xff,
0x48, 0xff,
0x49, 0xff,
0x4a, 0xff,
0x4b, 0xff,
0x4c, 0xff,
0x4d, 0xff,
0x4e, 0xff,
0x4f, 0xff,
0x50, 0xff,
0x51, 0xff,
0x52, 0xff,
0x53, 0xff,
0x54, 0xff,
0x55, 0xff,
0x56, 0xff,
0x57, 0xff,
0x58, 0x00,
0x59, 0x54,
0x5a, 0x07,
0x5b, 0x83,
0x5c, 0x00,
0x5d, 0x00,
0x5e, 0x00,
0x5f, 0x00,
0x60, 0x00,
0x61, 0x00,
0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
};
static int write_reg(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
static int write_regs(struct i2c_client *client, u8 *regs)
{
int i;
for (i = 0; regs[i] != 0x00; i += 2)
if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
return -1;
return 0;
}
static int wis_saa7113_command(struct i2c_client *client,
unsigned int cmd, void *arg)
{
struct wis_saa7113 *dec = i2c_get_clientdata(client);
switch (cmd) {
case DECODER_SET_INPUT:
{
int *input = arg;
i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input);
i2c_smbus_write_byte_data(client, 0x09,
*input < 6 ? 0x40 : 0x80);
break;
}
case DECODER_SET_NORM:
{
int *input = arg;
dec->norm = *input;
switch (dec->norm) {
case VIDEO_MODE_PAL:
write_reg(client, 0x0e, 0x01);
write_reg(client, 0x10, 0x48);
break;
case VIDEO_MODE_NTSC:
write_reg(client, 0x0e, 0x01);
write_reg(client, 0x10, 0x40);
break;
case VIDEO_MODE_SECAM:
write_reg(client, 0x0e, 0x50);
write_reg(client, 0x10, 0x48);
break;
}
break;
}
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *ctrl = arg;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
ctrl->minimum = 0;
ctrl->maximum = 255;
ctrl->step = 1;
ctrl->default_value = 128;
ctrl->flags = 0;
break;
case V4L2_CID_CONTRAST:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
ctrl->minimum = 0;
ctrl->maximum = 127;
ctrl->step = 1;
ctrl->default_value = 71;
ctrl->flags = 0;
break;
case V4L2_CID_SATURATION:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
ctrl->minimum = 0;
ctrl->maximum = 127;
ctrl->step = 1;
ctrl->default_value = 64;
ctrl->flags = 0;
break;
case V4L2_CID_HUE:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
ctrl->minimum = -128;
ctrl->maximum = 127;
ctrl->step = 1;
ctrl->default_value = 0;
ctrl->flags = 0;
break;
}
break;
}
case VIDIOC_S_CTRL:
{
struct v4l2_control *ctrl = arg;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value > 255)
dec->brightness = 255;
else if (ctrl->value < 0)
dec->brightness = 0;
else
dec->brightness = ctrl->value;
write_reg(client, 0x0a, dec->brightness);
break;
case V4L2_CID_CONTRAST:
if (ctrl->value > 127)
dec->contrast = 127;
else if (ctrl->value < 0)
dec->contrast = 0;
else
dec->contrast = ctrl->value;
write_reg(client, 0x0b, dec->contrast);
break;
case V4L2_CID_SATURATION:
if (ctrl->value > 127)
dec->saturation = 127;
else if (ctrl->value < 0)
dec->saturation = 0;
else
dec->saturation = ctrl->value;
write_reg(client, 0x0c, dec->saturation);
break;
case V4L2_CID_HUE:
if (ctrl->value > 127)
dec->hue = 127;
else if (ctrl->value < -128)
dec->hue = -128;
else
dec->hue = ctrl->value;
write_reg(client, 0x0d, dec->hue);
break;
}
break;
}
case VIDIOC_G_CTRL:
{
struct v4l2_control *ctrl = arg;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->value = dec->brightness;
break;
case V4L2_CID_CONTRAST:
ctrl->value = dec->contrast;
break;
case V4L2_CID_SATURATION:
ctrl->value = dec->saturation;
break;
case V4L2_CID_HUE:
ctrl->value = dec->hue;
break;
}
break;
}
default:
break;
}
return 0;
}
static struct i2c_driver wis_saa7113_driver;
static struct i2c_client wis_saa7113_client_templ = {
.name = "SAA7113 (WIS)",
.driver = &wis_saa7113_driver,
};
static int wis_saa7113_detect(struct i2c_adapter *adapter, int addr, int kind)
{
struct i2c_client *client;
struct wis_saa7113 *dec;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == NULL)
return -ENOMEM;
memcpy(client, &wis_saa7113_client_templ,
sizeof(wis_saa7113_client_templ));
client->adapter = adapter;
client->addr = addr;
dec = kmalloc(sizeof(struct wis_saa7113), GFP_KERNEL);
if (dec == NULL) {
kfree(client);
return -ENOMEM;
}
dec->norm = VIDEO_MODE_NTSC;
dec->brightness = 128;
dec->contrast = 71;
dec->saturation = 64;
dec->hue = 0;
i2c_set_clientdata(client, dec);
printk(KERN_DEBUG
"wis-saa7113: initializing SAA7113 at address %d on %s\n",
addr, adapter->name);
if (write_regs(client, initial_registers) < 0) {
printk(KERN_ERR
"wis-saa7113: error initializing SAA7113\n");
kfree(client);
kfree(dec);
return 0;
}
i2c_attach_client(client);
return 0;
}
static int wis_saa7113_detach(struct i2c_client *client)
{
struct wis_saa7113 *dec = i2c_get_clientdata(client);
int r;
r = i2c_detach_client(client);
if (r < 0)
return r;
kfree(client);
kfree(dec);
return 0;
}
static struct i2c_driver wis_saa7113_driver = {
.driver = {
.name = "WIS SAA7113 I2C driver",
},
.id = I2C_DRIVERID_WIS_SAA7113,
.detach_client = wis_saa7113_detach,
.command = wis_saa7113_command,
};
static int __init wis_saa7113_init(void)
{
int r;
r = i2c_add_driver(&wis_saa7113_driver);
if (r < 0)
return r;
return wis_i2c_add_driver(wis_saa7113_driver.id, wis_saa7113_detect);
}
static void __exit wis_saa7113_cleanup(void)
{
wis_i2c_del_driver(wis_saa7113_detect);
i2c_del_driver(&wis_saa7113_driver);
}
module_init(wis_saa7113_init);
module_exit(wis_saa7113_cleanup);
MODULE_LICENSE("GPL v2");
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/i2c.h>
#include <linux/videodev.h>
#include <linux/video_decoder.h>
#include <linux/ioctl.h>
#include "wis-i2c.h"
struct wis_saa7115 {
int norm;
int brightness;
int contrast;
int saturation;
int hue;
};
static u8 initial_registers[] =
{
0x01, 0x08,
0x02, 0xc0,
0x03, 0x20,
0x04, 0x80,
0x05, 0x80,
0x06, 0xeb,
0x07, 0xe0,
0x08, 0xf0, /* always toggle FID */
0x09, 0x40,
0x0a, 0x80,
0x0b, 0x40,
0x0c, 0x40,
0x0d, 0x00,
0x0e, 0x03,
0x0f, 0x2a,
0x10, 0x0e,
0x11, 0x00,
0x12, 0x8d,
0x13, 0x00,
0x14, 0x00,
0x15, 0x11,
0x16, 0x01,
0x17, 0xda,
0x18, 0x40,
0x19, 0x80,
0x1a, 0x00,
0x1b, 0x42,
0x1c, 0xa9,
0x30, 0x66,
0x31, 0x90,
0x32, 0x01,
0x34, 0x00,
0x35, 0x00,
0x36, 0x20,
0x38, 0x03,
0x39, 0x20,
0x3a, 0x88,
0x40, 0x00,
0x41, 0xff,
0x42, 0xff,
0x43, 0xff,
0x44, 0xff,
0x45, 0xff,
0x46, 0xff,
0x47, 0xff,
0x48, 0xff,
0x49, 0xff,
0x4a, 0xff,
0x4b, 0xff,
0x4c, 0xff,
0x4d, 0xff,
0x4e, 0xff,
0x4f, 0xff,
0x50, 0xff,
0x51, 0xff,
0x52, 0xff,
0x53, 0xff,
0x54, 0xf4 /*0xff*/,
0x55, 0xff,
0x56, 0xff,
0x57, 0xff,
0x58, 0x40,
0x59, 0x47,
0x5a, 0x06 /*0x03*/,
0x5b, 0x83,
0x5d, 0x06,
0x5e, 0x00,
0x80, 0x30, /* window defined scaler operation, task A and B enabled */
0x81, 0x03, /* use scaler datapath generated V */
0x83, 0x00,
0x84, 0x00,
0x85, 0x00,
0x86, 0x45,
0x87, 0x31,
0x88, 0xc0,
0x90, 0x02, /* task A process top field */
0x91, 0x08,
0x92, 0x09,
0x93, 0x80,
0x94, 0x06,
0x95, 0x00,
0x96, 0xc0,
0x97, 0x02,
0x98, 0x12,
0x99, 0x00,
0x9a, 0xf2,
0x9b, 0x00,
0x9c, 0xd0,
0x9d, 0x02,
0x9e, 0xf2,
0x9f, 0x00,
0xa0, 0x01,
0xa1, 0x01,
0xa2, 0x01,
0xa4, 0x80,
0xa5, 0x40,
0xa6, 0x40,
0xa8, 0x00,
0xa9, 0x04,
0xaa, 0x00,
0xac, 0x00,
0xad, 0x02,
0xae, 0x00,
0xb0, 0x00,
0xb1, 0x04,
0xb2, 0x00,
0xb3, 0x04,
0xb4, 0x00,
0xb8, 0x00,
0xbc, 0x00,
0xc0, 0x03, /* task B process bottom field */
0xc1, 0x08,
0xc2, 0x09,
0xc3, 0x80,
0xc4, 0x06,
0xc5, 0x00,
0xc6, 0xc0,
0xc7, 0x02,
0xc8, 0x12,
0xc9, 0x00,
0xca, 0xf2,
0xcb, 0x00,
0xcc, 0xd0,
0xcd, 0x02,
0xce, 0xf2,
0xcf, 0x00,
0xd0, 0x01,
0xd1, 0x01,
0xd2, 0x01,
0xd4, 0x80,
0xd5, 0x40,
0xd6, 0x40,
0xd8, 0x00,
0xd9, 0x04,
0xda, 0x00,
0xdc, 0x00,
0xdd, 0x02,
0xde, 0x00,
0xe0, 0x00,
0xe1, 0x04,
0xe2, 0x00,
0xe3, 0x04,
0xe4, 0x00,
0xe8, 0x00,
0x88, 0xf0, /* End of original static list */
0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
};
static int write_reg(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
static int write_regs(struct i2c_client *client, u8 *regs)
{
int i;
for (i = 0; regs[i] != 0x00; i += 2)
if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
return -1;
return 0;
}
static int wis_saa7115_command(struct i2c_client *client,
unsigned int cmd, void *arg)
{
struct wis_saa7115 *dec = i2c_get_clientdata(client);
switch (cmd) {
case DECODER_SET_INPUT:
{
int *input = arg;
i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input);
i2c_smbus_write_byte_data(client, 0x09,
*input < 6 ? 0x40 : 0xC0);
break;
}
case DECODER_SET_RESOLUTION:
{
struct video_decoder_resolution *res = arg;
/* Course-grained scaler */
int h_integer_scaler = res->width < 704 ? 704 / res->width : 1;
/* Fine-grained scaler to take care of remainder */
int h_scaling_increment = (704 / h_integer_scaler) *
1024 / res->width;
/* Fine-grained scaler only */
int v_scaling_increment = (dec->norm == VIDEO_MODE_NTSC ?
240 : 288) * 1024 / res->height;
u8 regs[] = {
0x88, 0xc0,
0x9c, res->width & 0xff,
0x9d, res->width >> 8,
0x9e, res->height & 0xff,
0x9f, res->height >> 8,
0xa0, h_integer_scaler,
0xa1, 1,
0xa2, 1,
0xa8, h_scaling_increment & 0xff,
0xa9, h_scaling_increment >> 8,
0xac, (h_scaling_increment / 2) & 0xff,
0xad, (h_scaling_increment / 2) >> 8,
0xb0, v_scaling_increment & 0xff,
0xb1, v_scaling_increment >> 8,
0xb2, v_scaling_increment & 0xff,
0xb3, v_scaling_increment >> 8,
0xcc, res->width & 0xff,
0xcd, res->width >> 8,
0xce, res->height & 0xff,
0xcf, res->height >> 8,
0xd0, h_integer_scaler,
0xd1, 1,
0xd2, 1,
0xd8, h_scaling_increment & 0xff,
0xd9, h_scaling_increment >> 8,
0xdc, (h_scaling_increment / 2) & 0xff,
0xdd, (h_scaling_increment / 2) >> 8,
0xe0, v_scaling_increment & 0xff,
0xe1, v_scaling_increment >> 8,
0xe2, v_scaling_increment & 0xff,
0xe3, v_scaling_increment >> 8,
0x88, 0xf0,
0, 0,
};
write_regs(client, regs);
break;
}
case DECODER_SET_NORM:
{
int *input = arg;
u8 regs[] = {
0x88, 0xc0,
0x98, *input == VIDEO_MODE_NTSC ? 0x12 : 0x16,
0x9a, *input == VIDEO_MODE_NTSC ? 0xf2 : 0x20,
0x9b, *input == VIDEO_MODE_NTSC ? 0x00 : 0x01,
0xc8, *input == VIDEO_MODE_NTSC ? 0x12 : 0x16,
0xca, *input == VIDEO_MODE_NTSC ? 0xf2 : 0x20,
0xcb, *input == VIDEO_MODE_NTSC ? 0x00 : 0x01,
0x88, 0xf0,
0x30, *input == VIDEO_MODE_NTSC ? 0x66 : 0x00,
0x31, *input == VIDEO_MODE_NTSC ? 0x90 : 0xe0,
0, 0,
};
write_regs(client, regs);
dec->norm = *input;
break;
}
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *ctrl = arg;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
ctrl->minimum = 0;
ctrl->maximum = 255;
ctrl->step = 1;
ctrl->default_value = 128;
ctrl->flags = 0;
break;
case V4L2_CID_CONTRAST:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
ctrl->minimum = 0;
ctrl->maximum = 127;
ctrl->step = 1;
ctrl->default_value = 64;
ctrl->flags = 0;
break;
case V4L2_CID_SATURATION:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
ctrl->minimum = 0;
ctrl->maximum = 127;
ctrl->step = 1;
ctrl->default_value = 64;
ctrl->flags = 0;
break;
case V4L2_CID_HUE:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
ctrl->minimum = -128;
ctrl->maximum = 127;
ctrl->step = 1;
ctrl->default_value = 0;
ctrl->flags = 0;
break;
}
break;
}
case VIDIOC_S_CTRL:
{
struct v4l2_control *ctrl = arg;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value > 255)
dec->brightness = 255;
else if (ctrl->value < 0)
dec->brightness = 0;
else
dec->brightness = ctrl->value;
write_reg(client, 0x0a, dec->brightness);
break;
case V4L2_CID_CONTRAST:
if (ctrl->value > 127)
dec->contrast = 127;
else if (ctrl->value < 0)
dec->contrast = 0;
else
dec->contrast = ctrl->value;
write_reg(client, 0x0b, dec->contrast);
break;
case V4L2_CID_SATURATION:
if (ctrl->value > 127)
dec->saturation = 127;
else if (ctrl->value < 0)
dec->saturation = 0;
else
dec->saturation = ctrl->value;
write_reg(client, 0x0c, dec->saturation);
break;
case V4L2_CID_HUE:
if (ctrl->value > 127)
dec->hue = 127;
else if (ctrl->value < -128)
dec->hue = -128;
else
dec->hue = ctrl->value;
write_reg(client, 0x0d, dec->hue);
break;
}
break;
}
case VIDIOC_G_CTRL:
{
struct v4l2_control *ctrl = arg;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->value = dec->brightness;
break;
case V4L2_CID_CONTRAST:
ctrl->value = dec->contrast;
break;
case V4L2_CID_SATURATION:
ctrl->value = dec->saturation;
break;
case V4L2_CID_HUE:
ctrl->value = dec->hue;
break;
}
break;
}
default:
break;
}
return 0;
}
static struct i2c_driver wis_saa7115_driver;
static struct i2c_client wis_saa7115_client_templ = {
.name = "SAA7115 (WIS)",
.driver = &wis_saa7115_driver,
};
static int wis_saa7115_detect(struct i2c_adapter *adapter, int addr, int kind)
{
struct i2c_client *client;
struct wis_saa7115 *dec;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == NULL)
return -ENOMEM;
memcpy(client, &wis_saa7115_client_templ,
sizeof(wis_saa7115_client_templ));
client->adapter = adapter;
client->addr = addr;
dec = kmalloc(sizeof(struct wis_saa7115), GFP_KERNEL);
if (dec == NULL) {
kfree(client);
return -ENOMEM;
}
dec->norm = VIDEO_MODE_NTSC;
dec->brightness = 128;
dec->contrast = 64;
dec->saturation = 64;
dec->hue = 0;
i2c_set_clientdata(client, dec);
printk(KERN_DEBUG
"wis-saa7115: initializing SAA7115 at address %d on %s\n",
addr, adapter->name);
if (write_regs(client, initial_registers) < 0) {
printk(KERN_ERR
"wis-saa7115: error initializing SAA7115\n");
kfree(client);
kfree(dec);
return 0;
}
i2c_attach_client(client);
return 0;
}
static int wis_saa7115_detach(struct i2c_client *client)
{
struct wis_saa7115 *dec = i2c_get_clientdata(client);
int r;
r = i2c_detach_client(client);
if (r < 0)
return r;
kfree(client);
kfree(dec);
return 0;
}
static struct i2c_driver wis_saa7115_driver = {
.driver = {
.name = "WIS SAA7115 I2C driver",
},
.id = I2C_DRIVERID_WIS_SAA7115,
.detach_client = wis_saa7115_detach,
.command = wis_saa7115_command,
};
static int __init wis_saa7115_init(void)
{
int r;
r = i2c_add_driver(&wis_saa7115_driver);
if (r < 0)
return r;
return wis_i2c_add_driver(wis_saa7115_driver.id, wis_saa7115_detect);
}
static void __exit wis_saa7115_cleanup(void)
{
wis_i2c_del_driver(wis_saa7115_detect);
i2c_del_driver(&wis_saa7115_driver);
}
module_init(wis_saa7115_init);
module_exit(wis_saa7115_cleanup);
MODULE_LICENSE("GPL v2");
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/i2c.h>
#include <linux/videodev.h>
#include <media/tuner.h>
#include <media/v4l2-common.h>
#include "wis-i2c.h"
/* #define MPX_DEBUG */
/* AS(IF/MPX) pin: LOW HIGH/OPEN
* IF/MPX address: 0x42/0x40 0x43/0x44
*/
#define IF_I2C_ADDR 0x43
#define MPX_I2C_ADDR 0x44
static v4l2_std_id force_band;
static char force_band_str[] = "-";
module_param_string(force_band, force_band_str, sizeof(force_band_str), 0644);
static int force_mpx_mode = -1;
module_param(force_mpx_mode, int, 0644);
/* Store tuner info in the same format as tuner.c, so maybe we can put the
* Sony tuner support in there. */
struct sony_tunertype {
char *name;
unsigned char Vendor; /* unused here */
unsigned char Type; /* unused here */
unsigned short thresh1; /* band switch VHF_LO <=> VHF_HI */
unsigned short thresh2; /* band switch VHF_HI <=> UHF */
unsigned char VHF_L;
unsigned char VHF_H;
unsigned char UHF;
unsigned char config;
unsigned short IFPCoff;
};
/* This array is indexed by (tuner_type - 200) */
static struct sony_tunertype sony_tuners[] = {
{ "Sony PAL+SECAM (BTF-PG472Z)", 0, 0,
16*144.25, 16*427.25, 0x01, 0x02, 0x04, 0xc6, 623},
{ "Sony NTSC_JP (BTF-PK467Z)", 0, 0,
16*220.25, 16*467.25, 0x01, 0x02, 0x04, 0xc6, 940},
{ "Sony NTSC (BTF-PB463Z)", 0, 0,
16*130.25, 16*364.25, 0x01, 0x02, 0x04, 0xc6, 732},
};
struct wis_sony_tuner {
int type;
v4l2_std_id std;
unsigned int freq;
int mpxmode;
u32 audmode;
};
/* Basically the same as default_set_tv_freq() in tuner.c */
static int set_freq(struct i2c_client *client, int freq)
{
struct wis_sony_tuner *t = i2c_get_clientdata(client);
char *band_name;
int n;
int band_select;
struct sony_tunertype *tun;
u8 buffer[4];
tun = &sony_tuners[t->type - 200];
if (freq < tun->thresh1) {
band_name = "VHF_L";
band_select = tun->VHF_L;
} else if (freq < tun->thresh2) {
band_name = "VHF_H";
band_select = tun->VHF_H;
} else {
band_name = "UHF";
band_select = tun->UHF;
}
printk(KERN_DEBUG "wis-sony-tuner: tuning to frequency %d.%04d (%s)\n",
freq / 16, (freq % 16) * 625, band_name);
n = freq + tun->IFPCoff;
buffer[0] = n >> 8;
buffer[1] = n & 0xff;
buffer[2] = tun->config;
buffer[3] = band_select;
i2c_master_send(client, buffer, 4);
return 0;
}
static int mpx_write(struct i2c_client *client, int dev, int addr, int val)
{
u8 buffer[5];
struct i2c_msg msg;
buffer[0] = dev;
buffer[1] = addr >> 8;
buffer[2] = addr & 0xff;
buffer[3] = val >> 8;
buffer[4] = val & 0xff;
msg.addr = MPX_I2C_ADDR;
msg.flags = 0;
msg.len = 5;
msg.buf = buffer;
i2c_transfer(client->adapter, &msg, 1);
return 0;
}
/*
* MPX register values for the BTF-PG472Z:
*
* FM_ NICAM_ SCART_
* MODUS SOURCE ACB PRESCAL PRESCAL PRESCAL SYSTEM VOLUME
* 10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/0000
* ---------------------------------------------------------------
* Auto 1003 0020 0100 2603 5000 XXXX 0001 7500
*
* B/G
* Mono 1003 0020 0100 2603 5000 XXXX 0003 7500
* A2 1003 0020 0100 2601 5000 XXXX 0003 7500
* NICAM 1003 0120 0100 2603 5000 XXXX 0008 7500
*
* I
* Mono 1003 0020 0100 2603 7900 XXXX 000A 7500
* NICAM 1003 0120 0100 2603 7900 XXXX 000A 7500
*
* D/K
* Mono 1003 0020 0100 2603 5000 XXXX 0004 7500
* A2-1 1003 0020 0100 2601 5000 XXXX 0004 7500
* A2-2 1003 0020 0100 2601 5000 XXXX 0005 7500
* A2-3 1003 0020 0100 2601 5000 XXXX 0007 7500
* NICAM 1003 0120 0100 2603 5000 XXXX 000B 7500
*
* L/L'
* Mono 0003 0200 0100 7C03 5000 2200 0009 7500
* NICAM 0003 0120 0100 7C03 5000 XXXX 0009 7500
*
* M
* Mono 1003 0200 0100 2B03 5000 2B00 0002 7500
*
* For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX.
*
* Bilingual selection in A2/NICAM:
*
* High byte of SOURCE Left chan Right chan
* 0x01 MAIN SUB
* 0x03 MAIN MAIN
* 0x04 SUB SUB
*
* Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or
* 0x00 (all other bands). Force mono in A2 with FMONO_A2:
*
* FMONO_A2
* 10/0022
* --------
* Forced mono ON 07F0
* Forced mono OFF 0190
*/
static struct {
enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode;
u16 modus;
u16 source;
u16 acb;
u16 fm_prescale;
u16 nicam_prescale;
u16 scart_prescale;
u16 system;
u16 volume;
} mpx_audio_modes[] = {
/* Auto */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
0x5000, 0x0000, 0x0001, 0x7500 },
/* B/G Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
0x5000, 0x0000, 0x0003, 0x7500 },
/* B/G A2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
0x5000, 0x0000, 0x0003, 0x7500 },
/* B/G NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,
0x5000, 0x0000, 0x0008, 0x7500 },
/* I Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
0x7900, 0x0000, 0x000A, 0x7500 },
/* I NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,
0x7900, 0x0000, 0x000A, 0x7500 },
/* D/K Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
0x5000, 0x0000, 0x0004, 0x7500 },
/* D/K A2-1 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
0x5000, 0x0000, 0x0004, 0x7500 },
/* D/K A2-2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
0x5000, 0x0000, 0x0005, 0x7500 },
/* D/K A2-3 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
0x5000, 0x0000, 0x0007, 0x7500 },
/* D/K NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,
0x5000, 0x0000, 0x000B, 0x7500 },
/* L/L' Mono */ { AUD_MONO, 0x0003, 0x0200, 0x0100, 0x7C03,
0x5000, 0x2200, 0x0009, 0x7500 },
/* L/L' NICAM */{ AUD_NICAM_L, 0x0003, 0x0120, 0x0100, 0x7C03,
0x5000, 0x0000, 0x0009, 0x7500 },
};
#define MPX_NUM_MODES ARRAY_SIZE(mpx_audio_modes)
static int mpx_setup(struct i2c_client *client)
{
struct wis_sony_tuner *t = i2c_get_clientdata(client);
u16 source = 0;
u8 buffer[3];
struct i2c_msg msg;
/* reset MPX */
buffer[0] = 0x00;
buffer[1] = 0x80;
buffer[2] = 0x00;
msg.addr = MPX_I2C_ADDR;
msg.flags = 0;
msg.len = 3;
msg.buf = buffer;
i2c_transfer(client->adapter, &msg, 1);
buffer[1] = 0x00;
i2c_transfer(client->adapter, &msg, 1);
if (mpx_audio_modes[t->mpxmode].audio_mode != AUD_MONO) {
switch (t->audmode) {
case V4L2_TUNER_MODE_MONO:
switch (mpx_audio_modes[t->mpxmode].audio_mode) {
case AUD_A2:
source = mpx_audio_modes[t->mpxmode].source;
break;
case AUD_NICAM:
source = 0x0000;
break;
case AUD_NICAM_L:
source = 0x0200;
break;
default:
break;
}
break;
case V4L2_TUNER_MODE_STEREO:
source = mpx_audio_modes[t->mpxmode].source;
break;
case V4L2_TUNER_MODE_LANG1:
source = 0x0300;
break;
case V4L2_TUNER_MODE_LANG2:
source = 0x0400;
break;
}
source |= mpx_audio_modes[t->mpxmode].source & 0x00ff;
} else
source = mpx_audio_modes[t->mpxmode].source;
mpx_write(client, 0x10, 0x0030, mpx_audio_modes[t->mpxmode].modus);
mpx_write(client, 0x12, 0x0008, source);
mpx_write(client, 0x12, 0x0013, mpx_audio_modes[t->mpxmode].acb);
mpx_write(client, 0x12, 0x000e,
mpx_audio_modes[t->mpxmode].fm_prescale);
mpx_write(client, 0x12, 0x0010,
mpx_audio_modes[t->mpxmode].nicam_prescale);
mpx_write(client, 0x12, 0x000d,
mpx_audio_modes[t->mpxmode].scart_prescale);
mpx_write(client, 0x10, 0x0020, mpx_audio_modes[t->mpxmode].system);
mpx_write(client, 0x12, 0x0000, mpx_audio_modes[t->mpxmode].volume);
if (mpx_audio_modes[t->mpxmode].audio_mode == AUD_A2)
mpx_write(client, 0x10, 0x0022,
t->audmode == V4L2_TUNER_MODE_MONO ? 0x07f0 : 0x0190);
#ifdef MPX_DEBUG
{
u8 buf1[3], buf2[2];
struct i2c_msg msgs[2];
printk(KERN_DEBUG "wis-sony-tuner: MPX registers: %04x %04x "
"%04x %04x %04x %04x %04x %04x\n",
mpx_audio_modes[t->mpxmode].modus,
source,
mpx_audio_modes[t->mpxmode].acb,
mpx_audio_modes[t->mpxmode].fm_prescale,
mpx_audio_modes[t->mpxmode].nicam_prescale,
mpx_audio_modes[t->mpxmode].scart_prescale,
mpx_audio_modes[t->mpxmode].system,
mpx_audio_modes[t->mpxmode].volume);
buf1[0] = 0x11;
buf1[1] = 0x00;
buf1[2] = 0x7e;
msgs[0].addr = MPX_I2C_ADDR;
msgs[0].flags = 0;
msgs[0].len = 3;
msgs[0].buf = buf1;
msgs[1].addr = MPX_I2C_ADDR;
msgs[1].flags = I2C_M_RD;
msgs[1].len = 2;
msgs[1].buf = buf2;
i2c_transfer(client->adapter, msgs, 2);
printk(KERN_DEBUG "wis-sony-tuner: MPX system: %02x%02x\n",
buf2[0], buf2[1]);
buf1[0] = 0x11;
buf1[1] = 0x02;
buf1[2] = 0x00;
i2c_transfer(client->adapter, msgs, 2);
printk(KERN_DEBUG "wis-sony-tuner: MPX status: %02x%02x\n",
buf2[0], buf2[1]);
}
#endif
return 0;
}
/*
* IF configuration values for the BTF-PG472Z:
*
* B/G: 0x94 0x70 0x49
* I: 0x14 0x70 0x4a
* D/K: 0x14 0x70 0x4b
* L: 0x04 0x70 0x4b
* L': 0x44 0x70 0x53
* M: 0x50 0x30 0x4c
*/
static int set_if(struct i2c_client *client)
{
struct wis_sony_tuner *t = i2c_get_clientdata(client);
u8 buffer[4];
struct i2c_msg msg;
int default_mpx_mode = 0;
/* configure IF */
buffer[0] = 0;
if (t->std & V4L2_STD_PAL_BG) {
buffer[1] = 0x94;
buffer[2] = 0x70;
buffer[3] = 0x49;
default_mpx_mode = 1;
} else if (t->std & V4L2_STD_PAL_I) {
buffer[1] = 0x14;
buffer[2] = 0x70;
buffer[3] = 0x4a;
default_mpx_mode = 4;
} else if (t->std & V4L2_STD_PAL_DK) {
buffer[1] = 0x14;
buffer[2] = 0x70;
buffer[3] = 0x4b;
default_mpx_mode = 6;
} else if (t->std & V4L2_STD_SECAM_L) {
buffer[1] = 0x04;
buffer[2] = 0x70;
buffer[3] = 0x4b;
default_mpx_mode = 11;
}
msg.addr = IF_I2C_ADDR;
msg.flags = 0;
msg.len = 4;
msg.buf = buffer;
i2c_transfer(client->adapter, &msg, 1);
/* Select MPX mode if not forced by the user */
if (force_mpx_mode >= 0 || force_mpx_mode < MPX_NUM_MODES)
t->mpxmode = force_mpx_mode;
else
t->mpxmode = default_mpx_mode;
printk(KERN_DEBUG "wis-sony-tuner: setting MPX to mode %d\n",
t->mpxmode);
mpx_setup(client);
return 0;
}
static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
struct wis_sony_tuner *t = i2c_get_clientdata(client);
switch (cmd) {
#ifdef TUNER_SET_TYPE_ADDR
case TUNER_SET_TYPE_ADDR:
{
struct tuner_setup *tun_setup = arg;
int *type = &tun_setup->type;
#else
case TUNER_SET_TYPE:
{
int *type = arg;
#endif
if (t->type >= 0) {
if (t->type != *type)
printk(KERN_ERR "wis-sony-tuner: type already "
"set to %d, ignoring request for %d\n",
t->type, *type);
break;
}
t->type = *type;
switch (t->type) {
case TUNER_SONY_BTF_PG472Z:
switch (force_band_str[0]) {
case 'b':
case 'B':
case 'g':
case 'G':
printk(KERN_INFO "wis-sony-tuner: forcing "
"tuner to PAL-B/G bands\n");
force_band = V4L2_STD_PAL_BG;
break;
case 'i':
case 'I':
printk(KERN_INFO "wis-sony-tuner: forcing "
"tuner to PAL-I band\n");
force_band = V4L2_STD_PAL_I;
break;
case 'd':
case 'D':
case 'k':
case 'K':
printk(KERN_INFO "wis-sony-tuner: forcing "
"tuner to PAL-D/K bands\n");
force_band = V4L2_STD_PAL_I;
break;
case 'l':
case 'L':
printk(KERN_INFO "wis-sony-tuner: forcing "
"tuner to SECAM-L band\n");
force_band = V4L2_STD_SECAM_L;
break;
default:
force_band = 0;
break;
}
if (force_band)
t->std = force_band;
else
t->std = V4L2_STD_PAL_BG;
set_if(client);
break;
case TUNER_SONY_BTF_PK467Z:
t->std = V4L2_STD_NTSC_M_JP;
break;
case TUNER_SONY_BTF_PB463Z:
t->std = V4L2_STD_NTSC_M;
break;
default:
printk(KERN_ERR "wis-sony-tuner: tuner type %d is not "
"supported by this module\n", *type);
break;
}
if (type >= 0)
printk(KERN_INFO
"wis-sony-tuner: type set to %d (%s)\n",
t->type, sony_tuners[t->type - 200].name);
break;
}
case VIDIOC_G_FREQUENCY:
{
struct v4l2_frequency *f = arg;
f->frequency = t->freq;
break;
}
case VIDIOC_S_FREQUENCY:
{
struct v4l2_frequency *f = arg;
t->freq = f->frequency;
set_freq(client, t->freq);
break;
}
case VIDIOC_ENUMSTD:
{
struct v4l2_standard *std = arg;
switch (t->type) {
case TUNER_SONY_BTF_PG472Z:
switch (std->index) {
case 0:
v4l2_video_std_construct(std,
V4L2_STD_PAL_BG, "PAL-B/G");
break;
case 1:
v4l2_video_std_construct(std,
V4L2_STD_PAL_I, "PAL-I");
break;
case 2:
v4l2_video_std_construct(std,
V4L2_STD_PAL_DK, "PAL-D/K");
break;
case 3:
v4l2_video_std_construct(std,
V4L2_STD_SECAM_L, "SECAM-L");
break;
default:
std->id = 0; /* hack to indicate EINVAL */
break;
}
break;
case TUNER_SONY_BTF_PK467Z:
if (std->index != 0) {
std->id = 0; /* hack to indicate EINVAL */
break;
}
v4l2_video_std_construct(std,
V4L2_STD_NTSC_M_JP, "NTSC-J");
break;
case TUNER_SONY_BTF_PB463Z:
if (std->index != 0) {
std->id = 0; /* hack to indicate EINVAL */
break;
}
v4l2_video_std_construct(std, V4L2_STD_NTSC_M, "NTSC");
break;
}
break;
}
case VIDIOC_G_STD:
{
v4l2_std_id *std = arg;
*std = t->std;
break;
}
case VIDIOC_S_STD:
{
v4l2_std_id *std = arg;
v4l2_std_id old = t->std;
switch (t->type) {
case TUNER_SONY_BTF_PG472Z:
if (force_band && (*std & force_band) != *std &&
*std != V4L2_STD_PAL &&
*std != V4L2_STD_SECAM) {
printk(KERN_DEBUG "wis-sony-tuner: ignoring "
"requested TV standard in "
"favor of force_band value\n");
t->std = force_band;
} else if (*std & V4L2_STD_PAL_BG) { /* default */
t->std = V4L2_STD_PAL_BG;
} else if (*std & V4L2_STD_PAL_I) {
t->std = V4L2_STD_PAL_I;
} else if (*std & V4L2_STD_PAL_DK) {
t->std = V4L2_STD_PAL_DK;
} else if (*std & V4L2_STD_SECAM_L) {
t->std = V4L2_STD_SECAM_L;
} else {
printk(KERN_ERR "wis-sony-tuner: TV standard "
"not supported\n");
*std = 0; /* hack to indicate EINVAL */
break;
}
if (old != t->std)
set_if(client);
break;
case TUNER_SONY_BTF_PK467Z:
if (!(*std & V4L2_STD_NTSC_M_JP)) {
printk(KERN_ERR "wis-sony-tuner: TV standard "
"not supported\n");
*std = 0; /* hack to indicate EINVAL */
}
break;
case TUNER_SONY_BTF_PB463Z:
if (!(*std & V4L2_STD_NTSC_M)) {
printk(KERN_ERR "wis-sony-tuner: TV standard "
"not supported\n");
*std = 0; /* hack to indicate EINVAL */
}
break;
}
break;
}
case VIDIOC_QUERYSTD:
{
v4l2_std_id *std = arg;
switch (t->type) {
case TUNER_SONY_BTF_PG472Z:
if (force_band)
*std = force_band;
else
*std = V4L2_STD_PAL_BG | V4L2_STD_PAL_I |
V4L2_STD_PAL_DK | V4L2_STD_SECAM_L;
break;
case TUNER_SONY_BTF_PK467Z:
*std = V4L2_STD_NTSC_M_JP;
break;
case TUNER_SONY_BTF_PB463Z:
*std = V4L2_STD_NTSC_M;
break;
}
break;
}
case VIDIOC_G_TUNER:
{
struct v4l2_tuner *tun = arg;
memset(t, 0, sizeof(*tun));
strcpy(tun->name, "Television");
tun->type = V4L2_TUNER_ANALOG_TV;
tun->rangelow = 0UL; /* does anything use these? */
tun->rangehigh = 0xffffffffUL;
switch (t->type) {
case TUNER_SONY_BTF_PG472Z:
tun->capability = V4L2_TUNER_CAP_NORM |
V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
V4L2_TUNER_CAP_LANG2;
tun->rxsubchans = V4L2_TUNER_SUB_MONO |
V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 |
V4L2_TUNER_SUB_LANG2;
break;
case TUNER_SONY_BTF_PK467Z:
case TUNER_SONY_BTF_PB463Z:
tun->capability = V4L2_TUNER_CAP_STEREO;
tun->rxsubchans = V4L2_TUNER_SUB_MONO |
V4L2_TUNER_SUB_STEREO;
break;
}
tun->audmode = t->audmode;
return 0;
}
case VIDIOC_S_TUNER:
{
struct v4l2_tuner *tun = arg;
switch (t->type) {
case TUNER_SONY_BTF_PG472Z:
if (tun->audmode != t->audmode) {
t->audmode = tun->audmode;
mpx_setup(client);
}
break;
case TUNER_SONY_BTF_PK467Z:
case TUNER_SONY_BTF_PB463Z:
break;
}
return 0;
}
default:
break;
}
return 0;
}
static struct i2c_driver wis_sony_tuner_driver;
static struct i2c_client wis_sony_tuner_client_templ = {
.name = "Sony TV Tuner (WIS)",
.driver = &wis_sony_tuner_driver,
};
static int wis_sony_tuner_detect(struct i2c_adapter *adapter,
int addr, int kind)
{
struct i2c_client *client;
struct wis_sony_tuner *t;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == NULL)
return -ENOMEM;
memcpy(client, &wis_sony_tuner_client_templ,
sizeof(wis_sony_tuner_client_templ));
client->adapter = adapter;
client->addr = addr;
t = kmalloc(sizeof(struct wis_sony_tuner), GFP_KERNEL);
if (t == NULL) {
kfree(client);
return -ENOMEM;
}
t->type = -1;
t->freq = 0;
t->mpxmode = 0;
t->audmode = V4L2_TUNER_MODE_STEREO;
i2c_set_clientdata(client, t);
printk(KERN_DEBUG
"wis-sony-tuner: initializing tuner at address %d on %s\n",
addr, adapter->name);
i2c_attach_client(client);
return 0;
}
static int wis_sony_tuner_detach(struct i2c_client *client)
{
struct wis_sony_tuner *t = i2c_get_clientdata(client);
int r;
r = i2c_detach_client(client);
if (r < 0)
return r;
kfree(t);
kfree(client);
return 0;
}
static struct i2c_driver wis_sony_tuner_driver = {
.driver = {
.name = "WIS Sony TV Tuner I2C driver",
},
.id = I2C_DRIVERID_WIS_SONY_TUNER,
.detach_client = wis_sony_tuner_detach,
.command = tuner_command,
};
static int __init wis_sony_tuner_init(void)
{
int r;
r = i2c_add_driver(&wis_sony_tuner_driver);
if (r < 0)
return r;
return wis_i2c_add_driver(wis_sony_tuner_driver.id,
wis_sony_tuner_detect);
}
static void __exit wis_sony_tuner_cleanup(void)
{
wis_i2c_del_driver(wis_sony_tuner_detect);
i2c_del_driver(&wis_sony_tuner_driver);
}
module_init(wis_sony_tuner_init);
module_exit(wis_sony_tuner_cleanup);
MODULE_LICENSE("GPL v2");
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/i2c.h>
#include <linux/videodev.h>
#include <linux/video_decoder.h>
#include <linux/ioctl.h>
#include "wis-i2c.h"
struct wis_tw2804 {
int channel;
int norm;
int brightness;
int contrast;
int saturation;
int hue;
};
static u8 global_registers[] =
{
0x39, 0x00,
0x3a, 0xff,
0x3b, 0x84,
0x3c, 0x80,
0x3d, 0x80,
0x3e, 0x82,
0x3f, 0x82,
0xff, 0xff, /* Terminator (reg 0xff does not exist) */
};
static u8 channel_registers[] =
{
0x01, 0xc4,
0x02, 0xa5,
0x03, 0x20,
0x04, 0xd0,
0x05, 0x20,
0x06, 0xd0,
0x07, 0x88,
0x08, 0x20,
0x09, 0x07,
0x0a, 0xf0,
0x0b, 0x07,
0x0c, 0xf0,
0x0d, 0x40,
0x0e, 0xd2,
0x0f, 0x80,
0x10, 0x80,
0x11, 0x80,
0x12, 0x80,
0x13, 0x1f,
0x14, 0x00,
0x15, 0x00,
0x16, 0x00,
0x17, 0x00,
0x18, 0xff,
0x19, 0xff,
0x1a, 0xff,
0x1b, 0xff,
0x1c, 0xff,
0x1d, 0xff,
0x1e, 0xff,
0x1f, 0xff,
0x20, 0x07,
0x21, 0x07,
0x22, 0x00,
0x23, 0x91,
0x24, 0x51,
0x25, 0x03,
0x26, 0x00,
0x27, 0x00,
0x28, 0x00,
0x29, 0x00,
0x2a, 0x00,
0x2b, 0x00,
0x2c, 0x00,
0x2d, 0x00,
0x2e, 0x00,
0x2f, 0x00,
0x30, 0x00,
0x31, 0x00,
0x32, 0x00,
0x33, 0x00,
0x34, 0x00,
0x35, 0x00,
0x36, 0x00,
0x37, 0x00,
0xff, 0xff, /* Terminator (reg 0xff does not exist) */
};
static int write_reg(struct i2c_client *client, u8 reg, u8 value, int channel)
{
return i2c_smbus_write_byte_data(client, reg | (channel << 6), value);
}
static int write_regs(struct i2c_client *client, u8 *regs, int channel)
{
int i;
for (i = 0; regs[i] != 0xff; i += 2)
if (i2c_smbus_write_byte_data(client,
regs[i] | (channel << 6), regs[i + 1]) < 0)
return -1;
return 0;
}
static int wis_tw2804_command(struct i2c_client *client,
unsigned int cmd, void *arg)
{
struct wis_tw2804 *dec = i2c_get_clientdata(client);
if (cmd == DECODER_SET_CHANNEL) {
int *input = arg;
if (*input < 0 || *input > 3) {
printk(KERN_ERR "wis-tw2804: channel %d is not "
"between 0 and 3!\n", *input);
return 0;
}
dec->channel = *input;
printk(KERN_DEBUG "wis-tw2804: initializing TW2804 "
"channel %d\n", dec->channel);
if (dec->channel == 0 &&
write_regs(client, global_registers, 0) < 0) {
printk(KERN_ERR "wis-tw2804: error initializing "
"TW2804 global registers\n");
return 0;
}
if (write_regs(client, channel_registers, dec->channel) < 0) {
printk(KERN_ERR "wis-tw2804: error initializing "
"TW2804 channel %d\n", dec->channel);
return 0;
}
return 0;
}
if (dec->channel < 0) {
printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until "
"channel number is set\n", cmd);
return 0;
}
switch (cmd) {
case DECODER_SET_NORM:
{
int *input = arg;
u8 regs[] = {
0x01, *input == VIDEO_MODE_NTSC ? 0xc4 : 0x84,
0x09, *input == VIDEO_MODE_NTSC ? 0x07 : 0x04,
0x0a, *input == VIDEO_MODE_NTSC ? 0xf0 : 0x20,
0x0b, *input == VIDEO_MODE_NTSC ? 0x07 : 0x04,
0x0c, *input == VIDEO_MODE_NTSC ? 0xf0 : 0x20,
0x0d, *input == VIDEO_MODE_NTSC ? 0x40 : 0x4a,
0x16, *input == VIDEO_MODE_NTSC ? 0x00 : 0x40,
0x17, *input == VIDEO_MODE_NTSC ? 0x00 : 0x40,
0x20, *input == VIDEO_MODE_NTSC ? 0x07 : 0x0f,
0x21, *input == VIDEO_MODE_NTSC ? 0x07 : 0x0f,
0xff, 0xff,
};
write_regs(client, regs, dec->channel);
dec->norm = *input;
break;
}
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *ctrl = arg;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
ctrl->minimum = 0;
ctrl->maximum = 255;
ctrl->step = 1;
ctrl->default_value = 128;
ctrl->flags = 0;
break;
case V4L2_CID_CONTRAST:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
ctrl->minimum = 0;
ctrl->maximum = 255;
ctrl->step = 1;
ctrl->default_value = 128;
ctrl->flags = 0;
break;
case V4L2_CID_SATURATION:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
ctrl->minimum = 0;
ctrl->maximum = 255;
ctrl->step = 1;
ctrl->default_value = 128;
ctrl->flags = 0;
break;
case V4L2_CID_HUE:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
ctrl->minimum = 0;
ctrl->maximum = 255;
ctrl->step = 1;
ctrl->default_value = 128;
ctrl->flags = 0;
break;
}
break;
}
case VIDIOC_S_CTRL:
{
struct v4l2_control *ctrl = arg;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value > 255)
dec->brightness = 255;
else if (ctrl->value < 0)
dec->brightness = 0;
else
dec->brightness = ctrl->value;
write_reg(client, 0x12, dec->brightness, dec->channel);
break;
case V4L2_CID_CONTRAST:
if (ctrl->value > 255)
dec->contrast = 255;
else if (ctrl->value < 0)
dec->contrast = 0;
else
dec->contrast = ctrl->value;
write_reg(client, 0x11, dec->contrast, dec->channel);
break;
case V4L2_CID_SATURATION:
if (ctrl->value > 255)
dec->saturation = 255;
else if (ctrl->value < 0)
dec->saturation = 0;
else
dec->saturation = ctrl->value;
write_reg(client, 0x10, dec->saturation, dec->channel);
break;
case V4L2_CID_HUE:
if (ctrl->value > 255)
dec->hue = 255;
else if (ctrl->value < 0)
dec->hue = 0;
else
dec->hue = ctrl->value;
write_reg(client, 0x0f, dec->hue, dec->channel);
break;
}
break;
}
case VIDIOC_G_CTRL:
{
struct v4l2_control *ctrl = arg;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->value = dec->brightness;
break;
case V4L2_CID_CONTRAST:
ctrl->value = dec->contrast;
break;
case V4L2_CID_SATURATION:
ctrl->value = dec->saturation;
break;
case V4L2_CID_HUE:
ctrl->value = dec->hue;
break;
}
break;
}
default:
break;
}
return 0;
}
static struct i2c_driver wis_tw2804_driver;
static struct i2c_client wis_tw2804_client_templ = {
.name = "TW2804 (WIS)",
.driver = &wis_tw2804_driver,
};
static int wis_tw2804_detect(struct i2c_adapter *adapter, int addr, int kind)
{
struct i2c_client *client;
struct wis_tw2804 *dec;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == NULL)
return -ENOMEM;
memcpy(client, &wis_tw2804_client_templ,
sizeof(wis_tw2804_client_templ));
client->adapter = adapter;
client->addr = addr;
dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL);
if (dec == NULL) {
kfree(client);
return -ENOMEM;
}
dec->channel = -1;
dec->norm = VIDEO_MODE_NTSC;
dec->brightness = 128;
dec->contrast = 128;
dec->saturation = 128;
dec->hue = 128;
i2c_set_clientdata(client, dec);
printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n",
addr, adapter->name);
i2c_attach_client(client);
return 0;
}
static int wis_tw2804_detach(struct i2c_client *client)
{
struct wis_tw2804 *dec = i2c_get_clientdata(client);
int r;
r = i2c_detach_client(client);
if (r < 0)
return r;
kfree(client);
kfree(dec);
return 0;
}
static struct i2c_driver wis_tw2804_driver = {
.driver = {
.name = "WIS TW2804 I2C driver",
},
.id = I2C_DRIVERID_WIS_TW2804,
.detach_client = wis_tw2804_detach,
.command = wis_tw2804_command,
};
static int __init wis_tw2804_init(void)
{
int r;
r = i2c_add_driver(&wis_tw2804_driver);
if (r < 0)
return r;
return wis_i2c_add_driver(wis_tw2804_driver.id, wis_tw2804_detect);
}
static void __exit wis_tw2804_cleanup(void)
{
wis_i2c_del_driver(wis_tw2804_detect);
i2c_del_driver(&wis_tw2804_driver);
}
module_init(wis_tw2804_init);
module_exit(wis_tw2804_cleanup);
MODULE_LICENSE("GPL v2");
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/i2c.h>
#include <linux/videodev.h>
#include <linux/video_decoder.h>
#include <linux/ioctl.h>
#include "wis-i2c.h"
struct wis_tw9903 {
int norm;
int brightness;
int contrast;
int hue;
};
static u8 initial_registers[] =
{
0x02, 0x44, /* input 1, composite */
0x03, 0x92, /* correct digital format */
0x04, 0x00,
0x05, 0x80, /* or 0x00 for PAL */
0x06, 0x40, /* second internal current reference */
0x07, 0x02, /* window */
0x08, 0x14, /* window */
0x09, 0xf0, /* window */
0x0a, 0x81, /* window */
0x0b, 0xd0, /* window */
0x0c, 0x8c,
0x0d, 0x00, /* scaling */
0x0e, 0x11, /* scaling */
0x0f, 0x00, /* scaling */
0x10, 0x00, /* brightness */
0x11, 0x60, /* contrast */
0x12, 0x01, /* sharpness */
0x13, 0x7f, /* U gain */
0x14, 0x5a, /* V gain */
0x15, 0x00, /* hue */
0x16, 0xc3, /* sharpness */
0x18, 0x00,
0x19, 0x58, /* vbi */
0x1a, 0x80,
0x1c, 0x0f, /* video norm */
0x1d, 0x7f, /* video norm */
0x20, 0xa0, /* clamping gain (working 0x50) */
0x21, 0x22,
0x22, 0xf0,
0x23, 0xfe,
0x24, 0x3c,
0x25, 0x38,
0x26, 0x44,
0x27, 0x20,
0x28, 0x00,
0x29, 0x15,
0x2a, 0xa0,
0x2b, 0x44,
0x2c, 0x37,
0x2d, 0x00,
0x2e, 0xa5, /* burst PLL control (working: a9) */
0x2f, 0xe0, /* 0xea is blue test frame -- 0xe0 for normal */
0x31, 0x00,
0x33, 0x22,
0x34, 0x11,
0x35, 0x35,
0x3b, 0x05,
0x06, 0xc0, /* reset device */
0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
};
static int write_reg(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
static int write_regs(struct i2c_client *client, u8 *regs)
{
int i;
for (i = 0; regs[i] != 0x00; i += 2)
if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
return -1;
return 0;
}
static int wis_tw9903_command(struct i2c_client *client,
unsigned int cmd, void *arg)
{
struct wis_tw9903 *dec = i2c_get_clientdata(client);
switch (cmd) {
case DECODER_SET_INPUT:
{
int *input = arg;
i2c_smbus_write_byte_data(client, 0x02, 0x40 | (*input << 1));
break;
}
#if 0 /* The scaler on this thing seems to be horribly broken */
case DECODER_SET_RESOLUTION:
{
struct video_decoder_resolution *res = arg;
/*int hscale = 256 * 720 / res->width;*/
int hscale = 256 * 720 / (res->width - (res->width > 704 ? 0 : 8));
int vscale = 256 * (dec->norm == VIDEO_MODE_NTSC ? 240 : 288)
/ res->height;
u8 regs[] = {
0x0d, vscale & 0xff,
0x0f, hscale & 0xff,
0x0e, ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8),
0x06, 0xc0, /* reset device */
0, 0,
};
printk(KERN_DEBUG "vscale is %04x, hscale is %04x\n",
vscale, hscale);
/*write_regs(client, regs);*/
break;
}
#endif
case DECODER_SET_NORM:
{
int *input = arg;
u8 regs[] = {
0x05, *input == VIDEO_MODE_NTSC ? 0x80 : 0x00,
0x07, *input == VIDEO_MODE_NTSC ? 0x02 : 0x12,
0x08, *input == VIDEO_MODE_NTSC ? 0x14 : 0x18,
0x09, *input == VIDEO_MODE_NTSC ? 0xf0 : 0x20,
0, 0,
};
write_regs(client, regs);
dec->norm = *input;
break;
}
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *ctrl = arg;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
ctrl->minimum = -128;
ctrl->maximum = 127;
ctrl->step = 1;
ctrl->default_value = 0x00;
ctrl->flags = 0;
break;
case V4L2_CID_CONTRAST:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
ctrl->minimum = 0;
ctrl->maximum = 255;
ctrl->step = 1;
ctrl->default_value = 0x60;
ctrl->flags = 0;
break;
#if 0
/* I don't understand how the Chroma Gain registers work... */
case V4L2_CID_SATURATION:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
ctrl->minimum = 0;
ctrl->maximum = 127;
ctrl->step = 1;
ctrl->default_value = 64;
ctrl->flags = 0;
break;
#endif
case V4L2_CID_HUE:
ctrl->type = V4L2_CTRL_TYPE_INTEGER;
strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
ctrl->minimum = -128;
ctrl->maximum = 127;
ctrl->step = 1;
ctrl->default_value = 0;
ctrl->flags = 0;
break;
}
break;
}
case VIDIOC_S_CTRL:
{
struct v4l2_control *ctrl = arg;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value > 127)
dec->brightness = 127;
else if (ctrl->value < -128)
dec->brightness = -128;
else
dec->brightness = ctrl->value;
write_reg(client, 0x10, dec->brightness);
break;
case V4L2_CID_CONTRAST:
if (ctrl->value > 255)
dec->contrast = 255;
else if (ctrl->value < 0)
dec->contrast = 0;
else
dec->contrast = ctrl->value;
write_reg(client, 0x11, dec->contrast);
break;
#if 0
case V4L2_CID_SATURATION:
if (ctrl->value > 127)
dec->saturation = 127;
else if (ctrl->value < 0)
dec->saturation = 0;
else
dec->saturation = ctrl->value;
/*write_reg(client, 0x0c, dec->saturation);*/
break;
#endif
case V4L2_CID_HUE:
if (ctrl->value > 127)
dec->hue = 127;
else if (ctrl->value < -128)
dec->hue = -128;
else
dec->hue = ctrl->value;
write_reg(client, 0x15, dec->hue);
break;
}
break;
}
case VIDIOC_G_CTRL:
{
struct v4l2_control *ctrl = arg;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->value = dec->brightness;
break;
case V4L2_CID_CONTRAST:
ctrl->value = dec->contrast;
break;
#if 0
case V4L2_CID_SATURATION:
ctrl->value = dec->saturation;
break;
#endif
case V4L2_CID_HUE:
ctrl->value = dec->hue;
break;
}
break;
}
default:
break;
}
return 0;
}
static struct i2c_driver wis_tw9903_driver;
static struct i2c_client wis_tw9903_client_templ = {
.name = "TW9903 (WIS)",
.driver = &wis_tw9903_driver,
};
static int wis_tw9903_detect(struct i2c_adapter *adapter, int addr, int kind)
{
struct i2c_client *client;
struct wis_tw9903 *dec;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == NULL)
return -ENOMEM;
memcpy(client, &wis_tw9903_client_templ,
sizeof(wis_tw9903_client_templ));
client->adapter = adapter;
client->addr = addr;
dec = kmalloc(sizeof(struct wis_tw9903), GFP_KERNEL);
if (dec == NULL) {
kfree(client);
return -ENOMEM;
}
dec->norm = VIDEO_MODE_NTSC;
dec->brightness = 0;
dec->contrast = 0x60;
dec->hue = 0;
i2c_set_clientdata(client, dec);
printk(KERN_DEBUG
"wis-tw9903: initializing TW9903 at address %d on %s\n",
addr, adapter->name);
if (write_regs(client, initial_registers) < 0) {
printk(KERN_ERR "wis-tw9903: error initializing TW9903\n");
kfree(client);
kfree(dec);
return 0;
}
i2c_attach_client(client);
return 0;
}
static int wis_tw9903_detach(struct i2c_client *client)
{
struct wis_tw9903 *dec = i2c_get_clientdata(client);
int r;
r = i2c_detach_client(client);
if (r < 0)
return r;
kfree(client);
kfree(dec);
return 0;
}
static struct i2c_driver wis_tw9903_driver = {
.driver = {
.name = "WIS TW9903 I2C driver",
},
.id = I2C_DRIVERID_WIS_TW9903,
.detach_client = wis_tw9903_detach,
.command = wis_tw9903_command,
};
static int __init wis_tw9903_init(void)
{
int r;
r = i2c_add_driver(&wis_tw9903_driver);
if (r < 0)
return r;
return wis_i2c_add_driver(wis_tw9903_driver.id, wis_tw9903_detect);
}
static void __exit wis_tw9903_cleanup(void)
{
wis_i2c_del_driver(wis_tw9903_detect);
i2c_del_driver(&wis_tw9903_driver);
}
module_init(wis_tw9903_init);
module_exit(wis_tw9903_cleanup);
MODULE_LICENSE("GPL v2");
/*
* Copyright (C) 2005-2006 Micronas USA Inc.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/i2c.h>
#include <linux/videodev.h>
#include <media/tvaudio.h>
#include <media/v4l2-common.h>
#include "wis-i2c.h"
static int write_reg(struct i2c_client *client, int reg, int value)
{
/* UDA1342 wants MSB first, but SMBus sends LSB first */
i2c_smbus_write_word_data(client, reg, swab16(value));
return 0;
}
static int wis_uda1342_command(struct i2c_client *client,
unsigned int cmd, void *arg)
{
switch (cmd) {
case VIDIOC_S_AUDIO:
{
int *inp = arg;
switch (*inp) {
case TVAUDIO_INPUT_TUNER:
write_reg(client, 0x00, 0x1441); /* select input 2 */
break;
case TVAUDIO_INPUT_EXTERN:
write_reg(client, 0x00, 0x1241); /* select input 1 */
break;
default:
printk(KERN_ERR "wis-uda1342: input %d not supported\n",
*inp);
break;
}
break;
}
default:
break;
}
return 0;
}
static struct i2c_driver wis_uda1342_driver;
static struct i2c_client wis_uda1342_client_templ = {
.name = "UDA1342 (WIS)",
.driver = &wis_uda1342_driver,
};
static int wis_uda1342_detect(struct i2c_adapter *adapter, int addr, int kind)
{
struct i2c_client *client;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == NULL)
return -ENOMEM;
memcpy(client, &wis_uda1342_client_templ,
sizeof(wis_uda1342_client_templ));
client->adapter = adapter;
client->addr = addr;
printk(KERN_DEBUG
"wis-uda1342: initializing UDA1342 at address %d on %s\n",
addr, adapter->name);
write_reg(client, 0x00, 0x8000); /* reset registers */
write_reg(client, 0x00, 0x1241); /* select input 1 */
i2c_attach_client(client);
return 0;
}
static int wis_uda1342_detach(struct i2c_client *client)
{
int r;
r = i2c_detach_client(client);
if (r < 0)
return r;
kfree(client);
return 0;
}
static struct i2c_driver wis_uda1342_driver = {
.driver = {
.name = "WIS UDA1342 I2C driver",
},
.id = I2C_DRIVERID_WIS_UDA1342,
.detach_client = wis_uda1342_detach,
.command = wis_uda1342_command,
};
static int __init wis_uda1342_init(void)
{
int r;
r = i2c_add_driver(&wis_uda1342_driver);
if (r < 0)
return r;
return wis_i2c_add_driver(wis_uda1342_driver.id, wis_uda1342_detect);
}
static void __exit wis_uda1342_cleanup(void)
{
wis_i2c_del_driver(wis_uda1342_detect);
i2c_del_driver(&wis_uda1342_driver);
}
module_init(wis_uda1342_init);
module_exit(wis_uda1342_cleanup);
MODULE_LICENSE("GPL v2");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment