/*
* 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.
*/
#include <linux/module.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/mutex.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/tuner.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include "go7007-priv.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) {
v4l2_err(&go->v4l2_dev, "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[] = "go7007/go7007fw.bin";
void *bounce;
int fw_len, rv = 0;
u16 intr_val, intr_data;
if (go->boot_fw == NULL) {
if (request_firmware(&fw_entry, fw_name, go->dev)) {
v4l2_err(go, "unable to load firmware from file \"%s\"\n", fw_name);
return -1;
}
if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) {
v4l2_err(go, "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 = kmemdup(fw_entry->data + 16, fw_len, GFP_KERNEL);
if (bounce == NULL) {
v4l2_err(go, "unable to allocate %d bytes for firmware transfer\n", fw_len);
release_firmware(fw_entry);
return -1;
}
release_firmware(fw_entry);
go->boot_fw_len = fw_len;
go->boot_fw = bounce;
}
if (go7007_interface_reset(go) < 0 ||
go7007_send_firmware(go, go->boot_fw, go->boot_fw_len) < 0 ||
go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
(intr_val & ~0x1) != 0x5a5a) {
v4l2_err(go, "error transferring firmware\n");
rv = -1;
}
return rv;
}
MODULE_FIRMWARE("go7007/go7007fw.bin");
/*
* 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;
mutex_lock(&go->hw_lock);
ret = go7007_load_encoder(go);
mutex_unlock(&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);
}
switch (go->board_id) {
case 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);
break;
case GO7007_BOARDID_ADLINK_MPG24:
/* set GPIO5 to be an output, currently low */
go7007_write_addr(go, 0x3c82, 0x0000);
go7007_write_addr(go, 0x3c80, 0x00df);
break;
case GO7007_BOARDID_ADS_USBAV_709:
/* GPIO pin 0: audio clock control */
/* pin 2: TW9906 reset */
/* pin 3: capture LED */
go7007_write_addr(go, 0x3c82, 0x000d);
go7007_write_addr(go, 0x3c80, 0x00f2);
break;
}
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, const struct go_i2c *const i2c)
{
struct go7007 *go = i2c_get_adapdata(adapter);
struct v4l2_device *v4l2_dev = &go->v4l2_dev;
struct v4l2_subdev *sd;
struct i2c_board_info info;
memset(&info, 0, sizeof(info));
strlcpy(info.type, i2c->type, sizeof(info.type));
info.addr = i2c->addr;
info.flags = i2c->flags;
sd = v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, NULL);
if (sd) {
if (i2c->is_video)
go->sd_video = sd;
if (i2c->is_audio)
go->sd_audio = sd;
return 0;
}
pr_info("go7007: probing for module i2c:%s failed\n", i2c->type);
return -EINVAL;
}
/*
* 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.
*/
static void go7007_remove(struct v4l2_device *v4l2_dev)
{
struct go7007 *go = container_of(v4l2_dev, struct go7007, v4l2_dev);
v4l2_device_unregister(v4l2_dev);
if (go->hpi_ops->release)
go->hpi_ops->release(go);
if (go->i2c_adapter_online) {
i2c_del_adapter(&go->i2c_adapter);
go->i2c_adapter_online = 0;
}
kfree(go->boot_fw);
go7007_v4l2_remove(go);
kfree(go);
}
/*
* 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, unsigned num_i2c_devs)
{
int i, ret;
dev_info(go->dev, "go7007: registering new %s\n", go->name);
go->v4l2_dev.release = go7007_remove;
ret = v4l2_device_register(go->dev, &go->v4l2_dev);
if (ret < 0)
return ret;
mutex_lock(&go->hw_lock);
ret = go7007_init_encoder(go);
mutex_unlock(&go->hw_lock);
if (ret < 0)
return ret;
ret = go7007_v4l2_ctrl_init(go);
if (ret < 0)
return ret;
if (!go->i2c_adapter_online &&
go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) {
ret = go7007_i2c_init(go);
if (ret < 0)
return ret;
go->i2c_adapter_online = 1;
}
if (go->i2c_adapter_online) {
if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) {
/* Reset the TW9906 */
go7007_write_addr(go, 0x3c82, 0x0009);
msleep(50);
go7007_write_addr(go, 0x3c82, 0x000d);
}
for (i = 0; i < num_i2c_devs; ++i)
init_i2c_module(&go->i2c_adapter, &go->board_info->i2c_devs[i]);
if (go->tuner_type >= 0) {
struct tu