Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

65
drivers/media/usb/Kconfig Normal file
View file

@ -0,0 +1,65 @@
if USB && MEDIA_SUPPORT
menuconfig MEDIA_USB_SUPPORT
bool "Media USB Adapters"
help
Enable media drivers for USB bus.
If you have such devices, say Y.
if MEDIA_USB_SUPPORT
if MEDIA_CAMERA_SUPPORT
comment "Webcam devices"
source "drivers/media/usb/uvc/Kconfig"
source "drivers/media/usb/gspca/Kconfig"
source "drivers/media/usb/pwc/Kconfig"
source "drivers/media/usb/cpia2/Kconfig"
source "drivers/media/usb/zr364xx/Kconfig"
source "drivers/media/usb/stkwebcam/Kconfig"
source "drivers/media/usb/s2255/Kconfig"
source "drivers/media/usb/usbtv/Kconfig"
endif
if MEDIA_ANALOG_TV_SUPPORT
comment "Analog TV USB devices"
source "drivers/media/usb/pvrusb2/Kconfig"
source "drivers/media/usb/hdpvr/Kconfig"
source "drivers/media/usb/tlg2300/Kconfig"
source "drivers/media/usb/usbvision/Kconfig"
source "drivers/media/usb/stk1160/Kconfig"
source "drivers/media/usb/go7007/Kconfig"
endif
if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT)
comment "Analog/digital TV USB devices"
source "drivers/media/usb/au0828/Kconfig"
source "drivers/media/usb/cx231xx/Kconfig"
source "drivers/media/usb/tm6000/Kconfig"
endif
if I2C && MEDIA_DIGITAL_TV_SUPPORT
comment "Digital TV USB devices"
source "drivers/media/usb/dvb-usb/Kconfig"
source "drivers/media/usb/dvb-usb-v2/Kconfig"
source "drivers/media/usb/ttusb-budget/Kconfig"
source "drivers/media/usb/ttusb-dec/Kconfig"
source "drivers/media/usb/siano/Kconfig"
source "drivers/media/usb/b2c2/Kconfig"
source "drivers/media/usb/as102/Kconfig"
endif
if (MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT)
comment "Webcam, TV (analog/digital) USB devices"
source "drivers/media/usb/em28xx/Kconfig"
endif
if MEDIA_SDR_SUPPORT
comment "Software defined radio USB devices"
source "drivers/media/usb/airspy/Kconfig"
source "drivers/media/usb/hackrf/Kconfig"
source "drivers/media/usb/msi2500/Kconfig"
endif
endif #MEDIA_USB_SUPPORT
endif #USB

View file

@ -0,0 +1,27 @@
#
# Makefile for the USB media device drivers
#
# DVB USB-only drivers
obj-y += ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ b2c2/
obj-y += zr364xx/ stkwebcam/ s2255/
obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/
obj-$(CONFIG_USB_GSPCA) += gspca/
obj-$(CONFIG_USB_PWC) += pwc/
obj-$(CONFIG_USB_AIRSPY) += airspy/
obj-$(CONFIG_USB_HACKRF) += hackrf/
obj-$(CONFIG_USB_MSI2500) += msi2500/
obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
obj-$(CONFIG_VIDEO_AU0828) += au0828/
obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_STK1160) += stk1160/
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
obj-$(CONFIG_VIDEO_TM6000) += tm6000/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
obj-$(CONFIG_VIDEO_USBTV) += usbtv/
obj-$(CONFIG_VIDEO_GO7007) += go7007/
obj-$(CONFIG_DVB_AS102) += as102/

View file

@ -0,0 +1,10 @@
config USB_AIRSPY
tristate "AirSpy"
depends on VIDEO_V4L2
select VIDEOBUF2_VMALLOC
---help---
This is a video4linux2 driver for AirSpy SDR device.
To compile this driver as a module, choose M here: the
module will be called airspy

View file

@ -0,0 +1 @@
obj-$(CONFIG_USB_AIRSPY) += airspy.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,8 @@
config DVB_AS102
tristate "Abilis AS102 DVB receiver"
depends on DVB_CORE && USB && I2C && INPUT
select FW_LOADER
help
Choose Y or M here if you have a device containing an AS102
To compile this driver as a module, choose M here

View file

@ -0,0 +1,7 @@
dvb-as102-objs := as102_drv.o as102_fw.o as10x_cmd.o as10x_cmd_stream.o \
as102_usb_drv.o as10x_cmd_cfg.o
obj-$(CONFIG_DVB_AS102) += dvb-as102.o
ccflags-y += -Idrivers/media/dvb-core
ccflags-y += -Idrivers/media/dvb-frontends

View file

@ -0,0 +1,401 @@
/*
* Abilis Systems Single DVB-T Receiver
* Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
* Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/kref.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
/* header file for usb device driver*/
#include "as102_drv.h"
#include "as10x_cmd.h"
#include "as102_fe.h"
#include "as102_fw.h"
#include "dvbdev.h"
int dual_tuner;
module_param_named(dual_tuner, dual_tuner, int, 0644);
MODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner config (default: off)");
static int fw_upload = 1;
module_param_named(fw_upload, fw_upload, int, 0644);
MODULE_PARM_DESC(fw_upload, "Turn on/off default FW upload (default: on)");
static int pid_filtering;
module_param_named(pid_filtering, pid_filtering, int, 0644);
MODULE_PARM_DESC(pid_filtering, "Activate HW PID filtering (default: off)");
static int ts_auto_disable;
module_param_named(ts_auto_disable, ts_auto_disable, int, 0644);
MODULE_PARM_DESC(ts_auto_disable, "Stream Auto Enable on FW (default: off)");
int elna_enable = 1;
module_param_named(elna_enable, elna_enable, int, 0644);
MODULE_PARM_DESC(elna_enable, "Activate eLNA (default: on)");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static void as102_stop_stream(struct as102_dev_t *dev)
{
struct as10x_bus_adapter_t *bus_adap;
if (dev != NULL)
bus_adap = &dev->bus_adap;
else
return;
if (bus_adap->ops->stop_stream != NULL)
bus_adap->ops->stop_stream(dev);
if (ts_auto_disable) {
if (mutex_lock_interruptible(&dev->bus_adap.lock))
return;
if (as10x_cmd_stop_streaming(bus_adap) < 0)
dev_dbg(&dev->bus_adap.usb_dev->dev,
"as10x_cmd_stop_streaming failed\n");
mutex_unlock(&dev->bus_adap.lock);
}
}
static int as102_start_stream(struct as102_dev_t *dev)
{
struct as10x_bus_adapter_t *bus_adap;
int ret = -EFAULT;
if (dev != NULL)
bus_adap = &dev->bus_adap;
else
return ret;
if (bus_adap->ops->start_stream != NULL)
ret = bus_adap->ops->start_stream(dev);
if (ts_auto_disable) {
if (mutex_lock_interruptible(&dev->bus_adap.lock))
return -EFAULT;
ret = as10x_cmd_start_streaming(bus_adap);
mutex_unlock(&dev->bus_adap.lock);
}
return ret;
}
static int as10x_pid_filter(struct as102_dev_t *dev,
int index, u16 pid, int onoff) {
struct as10x_bus_adapter_t *bus_adap = &dev->bus_adap;
int ret = -EFAULT;
if (mutex_lock_interruptible(&dev->bus_adap.lock)) {
dev_dbg(&dev->bus_adap.usb_dev->dev,
"amutex_lock_interruptible(lock) failed !\n");
return -EBUSY;
}
switch (onoff) {
case 0:
ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid);
dev_dbg(&dev->bus_adap.usb_dev->dev,
"DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n",
index, pid, ret);
break;
case 1:
{
struct as10x_ts_filter filter;
filter.type = TS_PID_TYPE_TS;
filter.idx = 0xFF;
filter.pid = pid;
ret = as10x_cmd_add_PID_filter(bus_adap, &filter);
dev_dbg(&dev->bus_adap.usb_dev->dev,
"ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n",
index, filter.idx, filter.pid, ret);
break;
}
}
mutex_unlock(&dev->bus_adap.lock);
return ret;
}
static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
int ret = 0;
struct dvb_demux *demux = dvbdmxfeed->demux;
struct as102_dev_t *as102_dev = demux->priv;
if (mutex_lock_interruptible(&as102_dev->sem))
return -ERESTARTSYS;
if (pid_filtering)
as10x_pid_filter(as102_dev, dvbdmxfeed->index,
dvbdmxfeed->pid, 1);
if (as102_dev->streaming++ == 0)
ret = as102_start_stream(as102_dev);
mutex_unlock(&as102_dev->sem);
return ret;
}
static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct dvb_demux *demux = dvbdmxfeed->demux;
struct as102_dev_t *as102_dev = demux->priv;
if (mutex_lock_interruptible(&as102_dev->sem))
return -ERESTARTSYS;
if (--as102_dev->streaming == 0)
as102_stop_stream(as102_dev);
if (pid_filtering)
as10x_pid_filter(as102_dev, dvbdmxfeed->index,
dvbdmxfeed->pid, 0);
mutex_unlock(&as102_dev->sem);
return 0;
}
static int as102_set_tune(void *priv, struct as10x_tune_args *tune_args)
{
struct as10x_bus_adapter_t *bus_adap = priv;
int ret;
/* Set frontend arguments */
if (mutex_lock_interruptible(&bus_adap->lock))
return -EBUSY;
ret = as10x_cmd_set_tune(bus_adap, tune_args);
if (ret != 0)
dev_dbg(&bus_adap->usb_dev->dev,
"as10x_cmd_set_tune failed. (err = %d)\n", ret);
mutex_unlock(&bus_adap->lock);
return ret;
}
static int as102_get_tps(void *priv, struct as10x_tps *tps)
{
struct as10x_bus_adapter_t *bus_adap = priv;
int ret;
if (mutex_lock_interruptible(&bus_adap->lock))
return -EBUSY;
/* send abilis command: GET_TPS */
ret = as10x_cmd_get_tps(bus_adap, tps);
mutex_unlock(&bus_adap->lock);
return ret;
}
static int as102_get_status(void *priv, struct as10x_tune_status *tstate)
{
struct as10x_bus_adapter_t *bus_adap = priv;
int ret;
if (mutex_lock_interruptible(&bus_adap->lock))
return -EBUSY;
/* send abilis command: GET_TUNE_STATUS */
ret = as10x_cmd_get_tune_status(bus_adap, tstate);
if (ret < 0) {
dev_dbg(&bus_adap->usb_dev->dev,
"as10x_cmd_get_tune_status failed (err = %d)\n",
ret);
}
mutex_unlock(&bus_adap->lock);
return ret;
}
static int as102_get_stats(void *priv, struct as10x_demod_stats *demod_stats)
{
struct as10x_bus_adapter_t *bus_adap = priv;
int ret;
if (mutex_lock_interruptible(&bus_adap->lock))
return -EBUSY;
/* send abilis command: GET_TUNE_STATUS */
ret = as10x_cmd_get_demod_stats(bus_adap, demod_stats);
if (ret < 0) {
dev_dbg(&bus_adap->usb_dev->dev,
"as10x_cmd_get_demod_stats failed (probably not tuned)\n");
} else {
dev_dbg(&bus_adap->usb_dev->dev,
"demod status: fc: 0x%08x, bad fc: 0x%08x, bytes corrected: 0x%08x , MER: 0x%04x\n",
demod_stats->frame_count,
demod_stats->bad_frame_count,
demod_stats->bytes_fixed_by_rs,
demod_stats->mer);
}
mutex_unlock(&bus_adap->lock);
return ret;
}
static int as102_stream_ctrl(void *priv, int acquire, uint32_t elna_cfg)
{
struct as10x_bus_adapter_t *bus_adap = priv;
int ret;
if (mutex_lock_interruptible(&bus_adap->lock))
return -EBUSY;
if (acquire) {
if (elna_enable)
as10x_cmd_set_context(bus_adap,
CONTEXT_LNA, elna_cfg);
ret = as10x_cmd_turn_on(bus_adap);
} else {
ret = as10x_cmd_turn_off(bus_adap);
}
mutex_unlock(&bus_adap->lock);
return ret;
}
static const struct as102_fe_ops as102_fe_ops = {
.set_tune = as102_set_tune,
.get_tps = as102_get_tps,
.get_status = as102_get_status,
.get_stats = as102_get_stats,
.stream_ctrl = as102_stream_ctrl,
};
int as102_dvb_register(struct as102_dev_t *as102_dev)
{
struct device *dev = &as102_dev->bus_adap.usb_dev->dev;
int ret;
ret = dvb_register_adapter(&as102_dev->dvb_adap,
as102_dev->name, THIS_MODULE,
dev, adapter_nr);
if (ret < 0) {
dev_err(dev, "%s: dvb_register_adapter() failed: %d\n",
__func__, ret);
return ret;
}
as102_dev->dvb_dmx.priv = as102_dev;
as102_dev->dvb_dmx.filternum = pid_filtering ? 16 : 256;
as102_dev->dvb_dmx.feednum = 256;
as102_dev->dvb_dmx.start_feed = as102_dvb_dmx_start_feed;
as102_dev->dvb_dmx.stop_feed = as102_dvb_dmx_stop_feed;
as102_dev->dvb_dmx.dmx.capabilities = DMX_TS_FILTERING |
DMX_SECTION_FILTERING;
as102_dev->dvb_dmxdev.filternum = as102_dev->dvb_dmx.filternum;
as102_dev->dvb_dmxdev.demux = &as102_dev->dvb_dmx.dmx;
as102_dev->dvb_dmxdev.capabilities = 0;
ret = dvb_dmx_init(&as102_dev->dvb_dmx);
if (ret < 0) {
dev_err(dev, "%s: dvb_dmx_init() failed: %d\n", __func__, ret);
goto edmxinit;
}
ret = dvb_dmxdev_init(&as102_dev->dvb_dmxdev, &as102_dev->dvb_adap);
if (ret < 0) {
dev_err(dev, "%s: dvb_dmxdev_init() failed: %d\n",
__func__, ret);
goto edmxdinit;
}
/* Attach the frontend */
as102_dev->dvb_fe = dvb_attach(as102_attach, as102_dev->name,
&as102_fe_ops,
&as102_dev->bus_adap,
as102_dev->elna_cfg);
if (!as102_dev->dvb_fe) {
dev_err(dev, "%s: as102_attach() failed: %d",
__func__, ret);
goto efereg;
}
ret = dvb_register_frontend(&as102_dev->dvb_adap, as102_dev->dvb_fe);
if (ret < 0) {
dev_err(dev, "%s: as102_dvb_register_frontend() failed: %d",
__func__, ret);
goto efereg;
}
/* init bus mutex for token locking */
mutex_init(&as102_dev->bus_adap.lock);
/* init start / stop stream mutex */
mutex_init(&as102_dev->sem);
/*
* try to load as102 firmware. If firmware upload failed, we'll be
* able to upload it later.
*/
if (fw_upload)
try_then_request_module(as102_fw_upload(&as102_dev->bus_adap),
"firmware_class");
pr_info("Registered device %s", as102_dev->name);
return 0;
efereg:
dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
edmxdinit:
dvb_dmx_release(&as102_dev->dvb_dmx);
edmxinit:
dvb_unregister_adapter(&as102_dev->dvb_adap);
return ret;
}
void as102_dvb_unregister(struct as102_dev_t *as102_dev)
{
/* unregister as102 frontend */
dvb_unregister_frontend(as102_dev->dvb_fe);
/* detach frontend */
dvb_frontend_detach(as102_dev->dvb_fe);
/* unregister demux device */
dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
dvb_dmx_release(&as102_dev->dvb_dmx);
/* unregister dvb adapter */
dvb_unregister_adapter(&as102_dev->dvb_adap);
pr_info("Unregistered device %s", as102_dev->name);
}
module_usb_driver(as102_usb_driver);
/* modinfo details */
MODULE_DESCRIPTION(DRIVER_FULL_NAME);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");

View file

@ -0,0 +1,83 @@
/*
* Abilis Systems Single DVB-T Receiver
* Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _AS102_DRV_H
#define _AS102_DRV_H
#include <linux/usb.h>
#include <dvb_demux.h>
#include <dvb_frontend.h>
#include <dmxdev.h>
#include "as10x_handle.h"
#include "as10x_cmd.h"
#include "as102_usb_drv.h"
#define DRIVER_FULL_NAME "Abilis Systems as10x usb driver"
#define DRIVER_NAME "as10x_usb"
#define debug as102_debug
extern struct usb_driver as102_usb_driver;
extern int elna_enable;
#define AS102_DEVICE_MAJOR 192
#define AS102_USB_BUF_SIZE 512
#define MAX_STREAM_URB 32
struct as10x_bus_adapter_t {
struct usb_device *usb_dev;
/* bus token lock */
struct mutex lock;
/* low level interface for bus adapter */
union as10x_bus_token_t {
/* usb token */
struct as10x_usb_token_cmd_t usb;
} token;
/* token cmd xfer id */
uint16_t cmd_xid;
/* as10x command and response for dvb interface*/
struct as10x_cmd_t *cmd, *rsp;
/* bus adapter private ops callback */
struct as102_priv_ops_t *ops;
};
struct as102_dev_t {
const char *name;
struct as10x_bus_adapter_t bus_adap;
struct list_head device_entry;
struct kref kref;
uint8_t elna_cfg;
struct dvb_adapter dvb_adap;
struct dvb_frontend *dvb_fe;
struct dvb_demux dvb_dmx;
struct dmxdev dvb_dmxdev;
/* timer handle to trig ts stream download */
struct timer_list timer_handle;
struct mutex sem;
dma_addr_t dma_addr;
void *stream;
int streaming;
struct urb *stream_urb[MAX_STREAM_URB];
};
int as102_dvb_register(struct as102_dev_t *dev);
void as102_dvb_unregister(struct as102_dev_t *dev);
#endif

View file

@ -0,0 +1,228 @@
/*
* Abilis Systems Single DVB-T Receiver
* Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
* Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include "as102_drv.h"
#include "as102_fw.h"
static const char as102_st_fw1[] = "as102_data1_st.hex";
static const char as102_st_fw2[] = "as102_data2_st.hex";
static const char as102_dt_fw1[] = "as102_data1_dt.hex";
static const char as102_dt_fw2[] = "as102_data2_dt.hex";
static unsigned char atohx(unsigned char *dst, char *src)
{
unsigned char value = 0;
char msb = tolower(*src) - '0';
char lsb = tolower(*(src + 1)) - '0';
if (msb > 9)
msb -= 7;
if (lsb > 9)
lsb -= 7;
*dst = value = ((msb & 0xF) << 4) | (lsb & 0xF);
return value;
}
/*
* Parse INTEL HEX firmware file to extract address and data.
*/
static int parse_hex_line(unsigned char *fw_data, unsigned char *addr,
unsigned char *data, int *dataLength,
unsigned char *addr_has_changed) {
int count = 0;
unsigned char *src, dst;
if (*fw_data++ != ':') {
pr_err("invalid firmware file\n");
return -EFAULT;
}
/* locate end of line */
for (src = fw_data; *src != '\n'; src += 2) {
atohx(&dst, src);
/* parse line to split addr / data */
switch (count) {
case 0:
*dataLength = dst;
break;
case 1:
addr[2] = dst;
break;
case 2:
addr[3] = dst;
break;
case 3:
/* check if data is an address */
if (dst == 0x04)
*addr_has_changed = 1;
else
*addr_has_changed = 0;
break;
case 4:
case 5:
if (*addr_has_changed)
addr[(count - 4)] = dst;
else
data[(count - 4)] = dst;
break;
default:
data[(count - 4)] = dst;
break;
}
count++;
}
/* return read value + ':' + '\n' */
return (count * 2) + 2;
}
static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap,
unsigned char *cmd,
const struct firmware *firmware) {
struct as10x_fw_pkt_t fw_pkt;
int total_read_bytes = 0, errno = 0;
unsigned char addr_has_changed = 0;
for (total_read_bytes = 0; total_read_bytes < firmware->size; ) {
int read_bytes = 0, data_len = 0;
/* parse intel hex line */
read_bytes = parse_hex_line(
(u8 *) (firmware->data + total_read_bytes),
fw_pkt.raw.address,
fw_pkt.raw.data,
&data_len,
&addr_has_changed);
if (read_bytes <= 0)
goto error;
/* detect the end of file */
total_read_bytes += read_bytes;
if (total_read_bytes == firmware->size) {
fw_pkt.u.request[0] = 0x00;
fw_pkt.u.request[1] = 0x03;
/* send EOF command */
errno = bus_adap->ops->upload_fw_pkt(bus_adap,
(uint8_t *)
&fw_pkt, 2, 0);
if (errno < 0)
goto error;
} else {
if (!addr_has_changed) {
/* prepare command to send */
fw_pkt.u.request[0] = 0x00;
fw_pkt.u.request[1] = 0x01;
data_len += sizeof(fw_pkt.u.request);
data_len += sizeof(fw_pkt.raw.address);
/* send cmd to device */
errno = bus_adap->ops->upload_fw_pkt(bus_adap,
(uint8_t *)
&fw_pkt,
data_len,
0);
if (errno < 0)
goto error;
}
}
}
error:
return (errno == 0) ? total_read_bytes : errno;
}
int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap)
{
int errno = -EFAULT;
const struct firmware *firmware = NULL;
unsigned char *cmd_buf = NULL;
const char *fw1, *fw2;
struct usb_device *dev = bus_adap->usb_dev;
/* select fw file to upload */
if (dual_tuner) {
fw1 = as102_dt_fw1;
fw2 = as102_dt_fw2;
} else {
fw1 = as102_st_fw1;
fw2 = as102_st_fw2;
}
/* allocate buffer to store firmware upload command and data */
cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL);
if (cmd_buf == NULL) {
errno = -ENOMEM;
goto error;
}
/* request kernel to locate firmware file: part1 */
errno = request_firmware(&firmware, fw1, &dev->dev);
if (errno < 0) {
pr_err("%s: unable to locate firmware file: %s\n",
DRIVER_NAME, fw1);
goto error;
}
/* initiate firmware upload */
errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
if (errno < 0) {
pr_err("%s: error during firmware upload part1\n",
DRIVER_NAME);
goto error;
}
pr_info("%s: firmware: %s loaded with success\n",
DRIVER_NAME, fw1);
release_firmware(firmware);
/* wait for boot to complete */
mdelay(100);
/* request kernel to locate firmware file: part2 */
errno = request_firmware(&firmware, fw2, &dev->dev);
if (errno < 0) {
pr_err("%s: unable to locate firmware file: %s\n",
DRIVER_NAME, fw2);
goto error;
}
/* initiate firmware upload */
errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
if (errno < 0) {
pr_err("%s: error during firmware upload part2\n",
DRIVER_NAME);
goto error;
}
pr_info("%s: firmware: %s loaded with success\n",
DRIVER_NAME, fw2);
error:
kfree(cmd_buf);
release_firmware(firmware);
return errno;
}

View file

@ -0,0 +1,34 @@
/*
* Abilis Systems Single DVB-T Receiver
* Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define MAX_FW_PKT_SIZE 64
extern int dual_tuner;
struct as10x_raw_fw_pkt {
unsigned char address[4];
unsigned char data[MAX_FW_PKT_SIZE - 6];
} __packed;
struct as10x_fw_pkt_t {
union {
unsigned char request[2];
unsigned char length[2];
} __packed u;
struct as10x_raw_fw_pkt raw;
} __packed;
#ifdef __KERNEL__
int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap);
#endif

View file

@ -0,0 +1,475 @@
/*
* Abilis Systems Single DVB-T Receiver
* Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
* Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/usb.h>
#include "as102_drv.h"
#include "as102_usb_drv.h"
#include "as102_fw.h"
static void as102_usb_disconnect(struct usb_interface *interface);
static int as102_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id);
static int as102_usb_start_stream(struct as102_dev_t *dev);
static void as102_usb_stop_stream(struct as102_dev_t *dev);
static int as102_open(struct inode *inode, struct file *file);
static int as102_release(struct inode *inode, struct file *file);
static struct usb_device_id as102_usb_id_table[] = {
{ USB_DEVICE(AS102_USB_DEVICE_VENDOR_ID, AS102_USB_DEVICE_PID_0001) },
{ USB_DEVICE(PCTV_74E_USB_VID, PCTV_74E_USB_PID) },
{ USB_DEVICE(ELGATO_EYETV_DTT_USB_VID, ELGATO_EYETV_DTT_USB_PID) },
{ USB_DEVICE(NBOX_DVBT_DONGLE_USB_VID, NBOX_DVBT_DONGLE_USB_PID) },
{ USB_DEVICE(SKY_IT_DIGITAL_KEY_USB_VID, SKY_IT_DIGITAL_KEY_USB_PID) },
{ } /* Terminating entry */
};
/* Note that this table must always have the same number of entries as the
as102_usb_id_table struct */
static const char * const as102_device_names[] = {
AS102_REFERENCE_DESIGN,
AS102_PCTV_74E,
AS102_ELGATO_EYETV_DTT_NAME,
AS102_NBOX_DVBT_DONGLE_NAME,
AS102_SKY_IT_DIGITAL_KEY_NAME,
NULL /* Terminating entry */
};
/* eLNA configuration: devices built on the reference design work best
with 0xA0, while custom designs seem to require 0xC0 */
static uint8_t const as102_elna_cfg[] = {
0xA0,
0xC0,
0xC0,
0xA0,
0xA0,
0x00 /* Terminating entry */
};
struct usb_driver as102_usb_driver = {
.name = DRIVER_FULL_NAME,
.probe = as102_usb_probe,
.disconnect = as102_usb_disconnect,
.id_table = as102_usb_id_table
};
static const struct file_operations as102_dev_fops = {
.owner = THIS_MODULE,
.open = as102_open,
.release = as102_release,
};
static struct usb_class_driver as102_usb_class_driver = {
.name = "aton2-%d",
.fops = &as102_dev_fops,
.minor_base = AS102_DEVICE_MAJOR,
};
static int as102_usb_xfer_cmd(struct as10x_bus_adapter_t *bus_adap,
unsigned char *send_buf, int send_buf_len,
unsigned char *recv_buf, int recv_buf_len)
{
int ret = 0;
if (send_buf != NULL) {
ret = usb_control_msg(bus_adap->usb_dev,
usb_sndctrlpipe(bus_adap->usb_dev, 0),
AS102_USB_DEVICE_TX_CTRL_CMD,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_DEVICE,
bus_adap->cmd_xid, /* value */
0, /* index */
send_buf, send_buf_len,
USB_CTRL_SET_TIMEOUT /* 200 */);
if (ret < 0) {
dev_dbg(&bus_adap->usb_dev->dev,
"usb_control_msg(send) failed, err %i\n", ret);
return ret;
}
if (ret != send_buf_len) {
dev_dbg(&bus_adap->usb_dev->dev,
"only wrote %d of %d bytes\n", ret, send_buf_len);
return -1;
}
}
if (recv_buf != NULL) {
#ifdef TRACE
dev_dbg(bus_adap->usb_dev->dev,
"want to read: %d bytes\n", recv_buf_len);
#endif
ret = usb_control_msg(bus_adap->usb_dev,
usb_rcvctrlpipe(bus_adap->usb_dev, 0),
AS102_USB_DEVICE_RX_CTRL_CMD,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_DEVICE,
bus_adap->cmd_xid, /* value */
0, /* index */
recv_buf, recv_buf_len,
USB_CTRL_GET_TIMEOUT /* 200 */);
if (ret < 0) {
dev_dbg(&bus_adap->usb_dev->dev,
"usb_control_msg(recv) failed, err %i\n", ret);
return ret;
}
#ifdef TRACE
dev_dbg(bus_adap->usb_dev->dev,
"read %d bytes\n", recv_buf_len);
#endif
}
return ret;
}
static int as102_send_ep1(struct as10x_bus_adapter_t *bus_adap,
unsigned char *send_buf,
int send_buf_len,
int swap32)
{
int ret, actual_len;
ret = usb_bulk_msg(bus_adap->usb_dev,
usb_sndbulkpipe(bus_adap->usb_dev, 1),
send_buf, send_buf_len, &actual_len, 200);
if (ret) {
dev_dbg(&bus_adap->usb_dev->dev,
"usb_bulk_msg(send) failed, err %i\n", ret);
return ret;
}
if (actual_len != send_buf_len) {
dev_dbg(&bus_adap->usb_dev->dev, "only wrote %d of %d bytes\n",
actual_len, send_buf_len);
return -1;
}
return actual_len;
}
static int as102_read_ep2(struct as10x_bus_adapter_t *bus_adap,
unsigned char *recv_buf, int recv_buf_len)
{
int ret, actual_len;
if (recv_buf == NULL)
return -EINVAL;
ret = usb_bulk_msg(bus_adap->usb_dev,
usb_rcvbulkpipe(bus_adap->usb_dev, 2),
recv_buf, recv_buf_len, &actual_len, 200);
if (ret) {
dev_dbg(&bus_adap->usb_dev->dev,
"usb_bulk_msg(recv) failed, err %i\n", ret);
return ret;
}
if (actual_len != recv_buf_len) {
dev_dbg(&bus_adap->usb_dev->dev, "only read %d of %d bytes\n",
actual_len, recv_buf_len);
return -1;
}
return actual_len;
}
static struct as102_priv_ops_t as102_priv_ops = {
.upload_fw_pkt = as102_send_ep1,
.xfer_cmd = as102_usb_xfer_cmd,
.as102_read_ep2 = as102_read_ep2,
.start_stream = as102_usb_start_stream,
.stop_stream = as102_usb_stop_stream,
};
static int as102_submit_urb_stream(struct as102_dev_t *dev, struct urb *urb)
{
int err;
usb_fill_bulk_urb(urb,
dev->bus_adap.usb_dev,
usb_rcvbulkpipe(dev->bus_adap.usb_dev, 0x2),
urb->transfer_buffer,
AS102_USB_BUF_SIZE,
as102_urb_stream_irq,
dev);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err)
dev_dbg(&urb->dev->dev,
"%s: usb_submit_urb failed\n", __func__);
return err;
}
void as102_urb_stream_irq(struct urb *urb)
{
struct as102_dev_t *as102_dev = urb->context;
if (urb->actual_length > 0) {
dvb_dmx_swfilter(&as102_dev->dvb_dmx,
urb->transfer_buffer,
urb->actual_length);
} else {
if (urb->actual_length == 0)
memset(urb->transfer_buffer, 0, AS102_USB_BUF_SIZE);
}
/* is not stopped, re-submit urb */
if (as102_dev->streaming)
as102_submit_urb_stream(as102_dev, urb);
}
static void as102_free_usb_stream_buffer(struct as102_dev_t *dev)
{
int i;
for (i = 0; i < MAX_STREAM_URB; i++)
usb_free_urb(dev->stream_urb[i]);
usb_free_coherent(dev->bus_adap.usb_dev,
MAX_STREAM_URB * AS102_USB_BUF_SIZE,
dev->stream,
dev->dma_addr);
}
static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev)
{
int i;
dev->stream = usb_alloc_coherent(dev->bus_adap.usb_dev,
MAX_STREAM_URB * AS102_USB_BUF_SIZE,
GFP_KERNEL,
&dev->dma_addr);
if (!dev->stream) {
dev_dbg(&dev->bus_adap.usb_dev->dev,
"%s: usb_buffer_alloc failed\n", __func__);
return -ENOMEM;
}
memset(dev->stream, 0, MAX_STREAM_URB * AS102_USB_BUF_SIZE);
/* init urb buffers */
for (i = 0; i < MAX_STREAM_URB; i++) {
struct urb *urb;
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (urb == NULL) {
dev_dbg(&dev->bus_adap.usb_dev->dev,
"%s: usb_alloc_urb failed\n", __func__);
as102_free_usb_stream_buffer(dev);
return -ENOMEM;
}
urb->transfer_buffer = dev->stream + (i * AS102_USB_BUF_SIZE);
urb->transfer_dma = dev->dma_addr + (i * AS102_USB_BUF_SIZE);
urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
urb->transfer_buffer_length = AS102_USB_BUF_SIZE;
dev->stream_urb[i] = urb;
}
return 0;
}
static void as102_usb_stop_stream(struct as102_dev_t *dev)
{
int i;
for (i = 0; i < MAX_STREAM_URB; i++)
usb_kill_urb(dev->stream_urb[i]);
}
static int as102_usb_start_stream(struct as102_dev_t *dev)
{
int i, ret = 0;
for (i = 0; i < MAX_STREAM_URB; i++) {
ret = as102_submit_urb_stream(dev, dev->stream_urb[i]);
if (ret) {
as102_usb_stop_stream(dev);
return ret;
}
}
return 0;
}
static void as102_usb_release(struct kref *kref)
{
struct as102_dev_t *as102_dev;
as102_dev = container_of(kref, struct as102_dev_t, kref);
if (as102_dev != NULL) {
usb_put_dev(as102_dev->bus_adap.usb_dev);
kfree(as102_dev);
}
}
static void as102_usb_disconnect(struct usb_interface *intf)
{
struct as102_dev_t *as102_dev;
/* extract as102_dev_t from usb_device private data */
as102_dev = usb_get_intfdata(intf);
/* unregister dvb layer */
as102_dvb_unregister(as102_dev);
/* free usb buffers */
as102_free_usb_stream_buffer(as102_dev);
usb_set_intfdata(intf, NULL);
/* usb unregister device */
usb_deregister_dev(intf, &as102_usb_class_driver);
/* decrement usage counter */
kref_put(&as102_dev->kref, as102_usb_release);
pr_info("%s: device has been disconnected\n", DRIVER_NAME);
}
static int as102_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
int ret;
struct as102_dev_t *as102_dev;
int i;
/* This should never actually happen */
if (ARRAY_SIZE(as102_usb_id_table) !=
(sizeof(as102_device_names) / sizeof(const char *))) {
pr_err("Device names table invalid size");
return -EINVAL;
}
as102_dev = kzalloc(sizeof(struct as102_dev_t), GFP_KERNEL);
if (as102_dev == NULL)
return -ENOMEM;
/* Assign the user-friendly device name */
for (i = 0; i < ARRAY_SIZE(as102_usb_id_table); i++) {
if (id == &as102_usb_id_table[i]) {
as102_dev->name = as102_device_names[i];
as102_dev->elna_cfg = as102_elna_cfg[i];
}
}
if (as102_dev->name == NULL)
as102_dev->name = "Unknown AS102 device";
/* set private callback functions */
as102_dev->bus_adap.ops = &as102_priv_ops;
/* init cmd token for usb bus */
as102_dev->bus_adap.cmd = &as102_dev->bus_adap.token.usb.c;
as102_dev->bus_adap.rsp = &as102_dev->bus_adap.token.usb.r;
/* init kernel device reference */
kref_init(&as102_dev->kref);
/* store as102 device to usb_device private data */
usb_set_intfdata(intf, (void *) as102_dev);
/* store in as102 device the usb_device pointer */
as102_dev->bus_adap.usb_dev = usb_get_dev(interface_to_usbdev(intf));
/* we can register the device now, as it is ready */
ret = usb_register_dev(intf, &as102_usb_class_driver);
if (ret < 0) {
/* something prevented us from registering this driver */
dev_err(&intf->dev,
"%s: usb_register_dev() failed (errno = %d)\n",
__func__, ret);
goto failed;
}
pr_info("%s: device has been detected\n", DRIVER_NAME);
/* request buffer allocation for streaming */
ret = as102_alloc_usb_stream_buffer(as102_dev);
if (ret != 0)
goto failed_stream;
/* register dvb layer */
ret = as102_dvb_register(as102_dev);
if (ret != 0)
goto failed_dvb;
return ret;
failed_dvb:
as102_free_usb_stream_buffer(as102_dev);
failed_stream:
usb_deregister_dev(intf, &as102_usb_class_driver);
failed:
usb_put_dev(as102_dev->bus_adap.usb_dev);
usb_set_intfdata(intf, NULL);
kfree(as102_dev);
return ret;
}
static int as102_open(struct inode *inode, struct file *file)
{
int ret = 0, minor = 0;
struct usb_interface *intf = NULL;
struct as102_dev_t *dev = NULL;
/* read minor from inode */
minor = iminor(inode);
/* fetch device from usb interface */
intf = usb_find_interface(&as102_usb_driver, minor);
if (intf == NULL) {
pr_err("%s: can't find device for minor %d\n",
__func__, minor);
ret = -ENODEV;
goto exit;
}
/* get our device */
dev = usb_get_intfdata(intf);
if (dev == NULL) {
ret = -EFAULT;
goto exit;
}
/* save our device object in the file's private structure */
file->private_data = dev;
/* increment our usage count for the device */
kref_get(&dev->kref);
exit:
return ret;
}
static int as102_release(struct inode *inode, struct file *file)
{
struct as102_dev_t *dev = NULL;
dev = file->private_data;
if (dev != NULL) {
/* decrement the count on our device */
kref_put(&dev->kref, as102_usb_release);
}
return 0;
}
MODULE_DEVICE_TABLE(usb, as102_usb_id_table);

View file

@ -0,0 +1,57 @@
/*
* Abilis Systems Single DVB-T Receiver
* Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
* Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _AS102_USB_DRV_H_
#define _AS102_USB_DRV_H_
#define AS102_USB_DEVICE_TX_CTRL_CMD 0xF1
#define AS102_USB_DEVICE_RX_CTRL_CMD 0xF2
/* define these values to match the supported devices */
/* Abilis system: "TITAN" */
#define AS102_REFERENCE_DESIGN "Abilis Systems DVB-Titan"
#define AS102_USB_DEVICE_VENDOR_ID 0x1BA6
#define AS102_USB_DEVICE_PID_0001 0x0001
/* PCTV Systems: PCTV picoStick (74e) */
#define AS102_PCTV_74E "PCTV Systems picoStick (74e)"
#define PCTV_74E_USB_VID 0x2013
#define PCTV_74E_USB_PID 0x0246
/* Elgato: EyeTV DTT Deluxe */
#define AS102_ELGATO_EYETV_DTT_NAME "Elgato EyeTV DTT Deluxe"
#define ELGATO_EYETV_DTT_USB_VID 0x0fd9
#define ELGATO_EYETV_DTT_USB_PID 0x002c
/* nBox: nBox DVB-T Dongle */
#define AS102_NBOX_DVBT_DONGLE_NAME "nBox DVB-T Dongle"
#define NBOX_DVBT_DONGLE_USB_VID 0x0b89
#define NBOX_DVBT_DONGLE_USB_PID 0x0007
/* Sky Italia: Digital Key (green led) */
#define AS102_SKY_IT_DIGITAL_KEY_NAME "Sky IT Digital Key (green led)"
#define SKY_IT_DIGITAL_KEY_USB_VID 0x2137
#define SKY_IT_DIGITAL_KEY_USB_PID 0x0001
void as102_urb_stream_irq(struct urb *urb);
struct as10x_usb_token_cmd_t {
/* token cmd */
struct as10x_cmd_t c;
/* token response */
struct as10x_cmd_t r;
};
#endif

View file

@ -0,0 +1,413 @@
/*
* Abilis Systems Single DVB-T Receiver
* Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
* Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include "as102_drv.h"
#include "as10x_cmd.h"
/**
* as10x_cmd_turn_on - send turn on command to AS10x
* @adap: pointer to AS10x bus adapter
*
* Return 0 when no error, < 0 in case of error.
*/
int as10x_cmd_turn_on(struct as10x_bus_adapter_t *adap)
{
int error = AS10X_CMD_ERROR;
struct as10x_cmd_t *pcmd, *prsp;
pcmd = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(pcmd, (++adap->cmd_xid),
sizeof(pcmd->body.turn_on.req));
/* fill command */
pcmd->body.turn_on.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNON);
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd,
sizeof(pcmd->body.turn_on.req) +
HEADER_SIZE,
(uint8_t *) prsp,
sizeof(prsp->body.turn_on.rsp) +
HEADER_SIZE);
}
if (error < 0)
goto out;
/* parse response */
error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNON_RSP);
out:
return error;
}
/**
* as10x_cmd_turn_off - send turn off command to AS10x
* @adap: pointer to AS10x bus adapter
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_turn_off(struct as10x_bus_adapter_t *adap)
{
int error = AS10X_CMD_ERROR;
struct as10x_cmd_t *pcmd, *prsp;
pcmd = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(pcmd, (++adap->cmd_xid),
sizeof(pcmd->body.turn_off.req));
/* fill command */
pcmd->body.turn_off.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNOFF);
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(
adap, (uint8_t *) pcmd,
sizeof(pcmd->body.turn_off.req) + HEADER_SIZE,
(uint8_t *) prsp,
sizeof(prsp->body.turn_off.rsp) + HEADER_SIZE);
}
if (error < 0)
goto out;
/* parse response */
error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNOFF_RSP);
out:
return error;
}
/**
* as10x_cmd_set_tune - send set tune command to AS10x
* @adap: pointer to AS10x bus adapter
* @ptune: tune parameters
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap,
struct as10x_tune_args *ptune)
{
int error = AS10X_CMD_ERROR;
struct as10x_cmd_t *preq, *prsp;
preq = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(preq, (++adap->cmd_xid),
sizeof(preq->body.set_tune.req));
/* fill command */
preq->body.set_tune.req.proc_id = cpu_to_le16(CONTROL_PROC_SETTUNE);
preq->body.set_tune.req.args.freq = (__force __u32)cpu_to_le32(ptune->freq);
preq->body.set_tune.req.args.bandwidth = ptune->bandwidth;
preq->body.set_tune.req.args.hier_select = ptune->hier_select;
preq->body.set_tune.req.args.modulation = ptune->modulation;
preq->body.set_tune.req.args.hierarchy = ptune->hierarchy;
preq->body.set_tune.req.args.interleaving_mode =
ptune->interleaving_mode;
preq->body.set_tune.req.args.code_rate = ptune->code_rate;
preq->body.set_tune.req.args.guard_interval = ptune->guard_interval;
preq->body.set_tune.req.args.transmission_mode =
ptune->transmission_mode;
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(adap,
(uint8_t *) preq,
sizeof(preq->body.set_tune.req)
+ HEADER_SIZE,
(uint8_t *) prsp,
sizeof(prsp->body.set_tune.rsp)
+ HEADER_SIZE);
}
if (error < 0)
goto out;
/* parse response */
error = as10x_rsp_parse(prsp, CONTROL_PROC_SETTUNE_RSP);
out:
return error;
}
/**
* as10x_cmd_get_tune_status - send get tune status command to AS10x
* @adap: pointer to AS10x bus adapter
* @pstatus: pointer to updated status structure of the current tune
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap,
struct as10x_tune_status *pstatus)
{
int error = AS10X_CMD_ERROR;
struct as10x_cmd_t *preq, *prsp;
preq = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(preq, (++adap->cmd_xid),
sizeof(preq->body.get_tune_status.req));
/* fill command */
preq->body.get_tune_status.req.proc_id =
cpu_to_le16(CONTROL_PROC_GETTUNESTAT);
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(
adap,
(uint8_t *) preq,
sizeof(preq->body.get_tune_status.req) + HEADER_SIZE,
(uint8_t *) prsp,
sizeof(prsp->body.get_tune_status.rsp) + HEADER_SIZE);
}
if (error < 0)
goto out;
/* parse response */
error = as10x_rsp_parse(prsp, CONTROL_PROC_GETTUNESTAT_RSP);
if (error < 0)
goto out;
/* Response OK -> get response data */
pstatus->tune_state = prsp->body.get_tune_status.rsp.sts.tune_state;
pstatus->signal_strength =
le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.signal_strength);
pstatus->PER = le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.PER);
pstatus->BER = le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.BER);
out:
return error;
}
/**
* as10x_cmd_get_tps - send get TPS command to AS10x
* @adap: pointer to AS10x handle
* @ptps: pointer to TPS parameters structure
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap, struct as10x_tps *ptps)
{
int error = AS10X_CMD_ERROR;
struct as10x_cmd_t *pcmd, *prsp;
pcmd = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(pcmd, (++adap->cmd_xid),
sizeof(pcmd->body.get_tps.req));
/* fill command */
pcmd->body.get_tune_status.req.proc_id =
cpu_to_le16(CONTROL_PROC_GETTPS);
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(adap,
(uint8_t *) pcmd,
sizeof(pcmd->body.get_tps.req) +
HEADER_SIZE,
(uint8_t *) prsp,
sizeof(prsp->body.get_tps.rsp) +
HEADER_SIZE);
}
if (error < 0)
goto out;
/* parse response */
error = as10x_rsp_parse(prsp, CONTROL_PROC_GETTPS_RSP);
if (error < 0)
goto out;
/* Response OK -> get response data */
ptps->modulation = prsp->body.get_tps.rsp.tps.modulation;
ptps->hierarchy = prsp->body.get_tps.rsp.tps.hierarchy;
ptps->interleaving_mode = prsp->body.get_tps.rsp.tps.interleaving_mode;
ptps->code_rate_HP = prsp->body.get_tps.rsp.tps.code_rate_HP;
ptps->code_rate_LP = prsp->body.get_tps.rsp.tps.code_rate_LP;
ptps->guard_interval = prsp->body.get_tps.rsp.tps.guard_interval;
ptps->transmission_mode = prsp->body.get_tps.rsp.tps.transmission_mode;
ptps->DVBH_mask_HP = prsp->body.get_tps.rsp.tps.DVBH_mask_HP;
ptps->DVBH_mask_LP = prsp->body.get_tps.rsp.tps.DVBH_mask_LP;
ptps->cell_ID = le16_to_cpu((__force __le16)prsp->body.get_tps.rsp.tps.cell_ID);
out:
return error;
}
/**
* as10x_cmd_get_demod_stats - send get demod stats command to AS10x
* @adap: pointer to AS10x bus adapter
* @pdemod_stats: pointer to demod stats parameters structure
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap,
struct as10x_demod_stats *pdemod_stats)
{
int error = AS10X_CMD_ERROR;
struct as10x_cmd_t *pcmd, *prsp;
pcmd = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(pcmd, (++adap->cmd_xid),
sizeof(pcmd->body.get_demod_stats.req));
/* fill command */
pcmd->body.get_demod_stats.req.proc_id =
cpu_to_le16(CONTROL_PROC_GET_DEMOD_STATS);
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(adap,
(uint8_t *) pcmd,
sizeof(pcmd->body.get_demod_stats.req)
+ HEADER_SIZE,
(uint8_t *) prsp,
sizeof(prsp->body.get_demod_stats.rsp)
+ HEADER_SIZE);
}
if (error < 0)
goto out;
/* parse response */
error = as10x_rsp_parse(prsp, CONTROL_PROC_GET_DEMOD_STATS_RSP);
if (error < 0)
goto out;
/* Response OK -> get response data */
pdemod_stats->frame_count =
le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.frame_count);
pdemod_stats->bad_frame_count =
le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.bad_frame_count);
pdemod_stats->bytes_fixed_by_rs =
le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.bytes_fixed_by_rs);
pdemod_stats->mer =
le16_to_cpu((__force __le16)prsp->body.get_demod_stats.rsp.stats.mer);
pdemod_stats->has_started =
prsp->body.get_demod_stats.rsp.stats.has_started;
out:
return error;
}
/**
* as10x_cmd_get_impulse_resp - send get impulse response command to AS10x
* @adap: pointer to AS10x bus adapter
* @is_ready: pointer to value indicating when impulse
* response data is ready
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_get_impulse_resp(struct as10x_bus_adapter_t *adap,
uint8_t *is_ready)
{
int error = AS10X_CMD_ERROR;
struct as10x_cmd_t *pcmd, *prsp;
pcmd = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(pcmd, (++adap->cmd_xid),
sizeof(pcmd->body.get_impulse_rsp.req));
/* fill command */
pcmd->body.get_impulse_rsp.req.proc_id =
cpu_to_le16(CONTROL_PROC_GET_IMPULSE_RESP);
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(adap,
(uint8_t *) pcmd,
sizeof(pcmd->body.get_impulse_rsp.req)
+ HEADER_SIZE,
(uint8_t *) prsp,
sizeof(prsp->body.get_impulse_rsp.rsp)
+ HEADER_SIZE);
}
if (error < 0)
goto out;
/* parse response */
error = as10x_rsp_parse(prsp, CONTROL_PROC_GET_IMPULSE_RESP_RSP);
if (error < 0)
goto out;
/* Response OK -> get response data */
*is_ready = prsp->body.get_impulse_rsp.rsp.is_ready;
out:
return error;
}
/**
* as10x_cmd_build - build AS10x command header
* @pcmd: pointer to AS10x command buffer
* @xid: sequence id of the command
* @cmd_len: length of the command
*/
void as10x_cmd_build(struct as10x_cmd_t *pcmd,
uint16_t xid, uint16_t cmd_len)
{
pcmd->header.req_id = cpu_to_le16(xid);
pcmd->header.prog = cpu_to_le16(SERVICE_PROG_ID);
pcmd->header.version = cpu_to_le16(SERVICE_PROG_VERSION);
pcmd->header.data_len = cpu_to_le16(cmd_len);
}
/**
* as10x_rsp_parse - Parse command response
* @prsp: pointer to AS10x command buffer
* @proc_id: id of the command
*
* Return 0 on success or negative value in case of error.
*/
int as10x_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id)
{
int error;
/* extract command error code */
error = prsp->body.common.rsp.error;
if ((error == 0) &&
(le16_to_cpu(prsp->body.common.rsp.proc_id) == proc_id)) {
return 0;
}
return AS10X_CMD_ERROR;
}

View file

@ -0,0 +1,523 @@
/*
* Abilis Systems Single DVB-T Receiver
* Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _AS10X_CMD_H_
#define _AS10X_CMD_H_
#include <linux/kernel.h>
#include "as102_fe_types.h"
/*********************************/
/* MACRO DEFINITIONS */
/*********************************/
#define AS10X_CMD_ERROR -1
#define SERVICE_PROG_ID 0x0002
#define SERVICE_PROG_VERSION 0x0001
#define HIER_NONE 0x00
#define HIER_LOW_PRIORITY 0x01
#define HEADER_SIZE (sizeof(struct as10x_cmd_header_t))
/* context request types */
#define GET_CONTEXT_DATA 1
#define SET_CONTEXT_DATA 2
/* ODSP suspend modes */
#define CFG_MODE_ODSP_RESUME 0
#define CFG_MODE_ODSP_SUSPEND 1
/* Dump memory size */
#define DUMP_BLOCK_SIZE_MAX 0x20
/*********************************/
/* TYPE DEFINITION */
/*********************************/
enum control_proc {
CONTROL_PROC_TURNON = 0x0001,
CONTROL_PROC_TURNON_RSP = 0x0100,
CONTROL_PROC_SET_REGISTER = 0x0002,
CONTROL_PROC_SET_REGISTER_RSP = 0x0200,
CONTROL_PROC_GET_REGISTER = 0x0003,
CONTROL_PROC_GET_REGISTER_RSP = 0x0300,
CONTROL_PROC_SETTUNE = 0x000A,
CONTROL_PROC_SETTUNE_RSP = 0x0A00,
CONTROL_PROC_GETTUNESTAT = 0x000B,
CONTROL_PROC_GETTUNESTAT_RSP = 0x0B00,
CONTROL_PROC_GETTPS = 0x000D,
CONTROL_PROC_GETTPS_RSP = 0x0D00,
CONTROL_PROC_SETFILTER = 0x000E,
CONTROL_PROC_SETFILTER_RSP = 0x0E00,
CONTROL_PROC_REMOVEFILTER = 0x000F,
CONTROL_PROC_REMOVEFILTER_RSP = 0x0F00,
CONTROL_PROC_GET_IMPULSE_RESP = 0x0012,
CONTROL_PROC_GET_IMPULSE_RESP_RSP = 0x1200,
CONTROL_PROC_START_STREAMING = 0x0013,
CONTROL_PROC_START_STREAMING_RSP = 0x1300,
CONTROL_PROC_STOP_STREAMING = 0x0014,
CONTROL_PROC_STOP_STREAMING_RSP = 0x1400,
CONTROL_PROC_GET_DEMOD_STATS = 0x0015,
CONTROL_PROC_GET_DEMOD_STATS_RSP = 0x1500,
CONTROL_PROC_ELNA_CHANGE_MODE = 0x0016,
CONTROL_PROC_ELNA_CHANGE_MODE_RSP = 0x1600,
CONTROL_PROC_ODSP_CHANGE_MODE = 0x0017,
CONTROL_PROC_ODSP_CHANGE_MODE_RSP = 0x1700,
CONTROL_PROC_AGC_CHANGE_MODE = 0x0018,
CONTROL_PROC_AGC_CHANGE_MODE_RSP = 0x1800,
CONTROL_PROC_CONTEXT = 0x00FC,
CONTROL_PROC_CONTEXT_RSP = 0xFC00,
CONTROL_PROC_DUMP_MEMORY = 0x00FD,
CONTROL_PROC_DUMP_MEMORY_RSP = 0xFD00,
CONTROL_PROC_DUMPLOG_MEMORY = 0x00FE,
CONTROL_PROC_DUMPLOG_MEMORY_RSP = 0xFE00,
CONTROL_PROC_TURNOFF = 0x00FF,
CONTROL_PROC_TURNOFF_RSP = 0xFF00
};
union as10x_turn_on {
/* request */
struct {
/* request identifier */
__le16 proc_id;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* error */
uint8_t error;
} __packed rsp;
} __packed;
union as10x_turn_off {
/* request */
struct {
/* request identifier */
__le16 proc_id;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* error */
uint8_t err;
} __packed rsp;
} __packed;
union as10x_set_tune {
/* request */
struct {
/* request identifier */
__le16 proc_id;
/* tune params */
struct as10x_tune_args args;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* response error */
uint8_t error;
} __packed rsp;
} __packed;
union as10x_get_tune_status {
/* request */
struct {
/* request identifier */
__le16 proc_id;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* response error */
uint8_t error;
/* tune status */
struct as10x_tune_status sts;
} __packed rsp;
} __packed;
union as10x_get_tps {
/* request */
struct {
/* request identifier */
__le16 proc_id;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* response error */
uint8_t error;
/* tps details */
struct as10x_tps tps;
} __packed rsp;
} __packed;
union as10x_common {
/* request */
struct {
/* request identifier */
__le16 proc_id;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* response error */
uint8_t error;
} __packed rsp;
} __packed;
union as10x_add_pid_filter {
/* request */
struct {
/* request identifier */
__le16 proc_id;
/* PID to filter */
__le16 pid;
/* stream type (MPE, PSI/SI or PES )*/
uint8_t stream_type;
/* PID index in filter table */
uint8_t idx;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* response error */
uint8_t error;
/* Filter id */
uint8_t filter_id;
} __packed rsp;
} __packed;
union as10x_del_pid_filter {
/* request */
struct {
/* request identifier */
__le16 proc_id;
/* PID to remove */
__le16 pid;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* response error */
uint8_t error;
} __packed rsp;
} __packed;
union as10x_start_streaming {
/* request */
struct {
/* request identifier */
__le16 proc_id;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* error */
uint8_t error;
} __packed rsp;
} __packed;
union as10x_stop_streaming {
/* request */
struct {
/* request identifier */
__le16 proc_id;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* error */
uint8_t error;
} __packed rsp;
} __packed;
union as10x_get_demod_stats {
/* request */
struct {
/* request identifier */
__le16 proc_id;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* error */
uint8_t error;
/* demod stats */
struct as10x_demod_stats stats;
} __packed rsp;
} __packed;
union as10x_get_impulse_resp {
/* request */
struct {
/* request identifier */
__le16 proc_id;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* error */
uint8_t error;
/* impulse response ready */
uint8_t is_ready;
} __packed rsp;
} __packed;
union as10x_fw_context {
/* request */
struct {
/* request identifier */
__le16 proc_id;
/* value to write (for set context)*/
struct as10x_register_value reg_val;
/* context tag */
__le16 tag;
/* context request type */
__le16 type;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* value read (for get context) */
struct as10x_register_value reg_val;
/* context request type */
__le16 type;
/* error */
uint8_t error;
} __packed rsp;
} __packed;
union as10x_set_register {
/* request */
struct {
/* response identifier */
__le16 proc_id;
/* register description */
struct as10x_register_addr reg_addr;
/* register content */
struct as10x_register_value reg_val;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* error */
uint8_t error;
} __packed rsp;
} __packed;
union as10x_get_register {
/* request */
struct {
/* response identifier */
__le16 proc_id;
/* register description */
struct as10x_register_addr reg_addr;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* error */
uint8_t error;
/* register content */
struct as10x_register_value reg_val;
} __packed rsp;
} __packed;
union as10x_cfg_change_mode {
/* request */
struct {
/* request identifier */
__le16 proc_id;
/* mode */
uint8_t mode;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* error */
uint8_t error;
} __packed rsp;
} __packed;
struct as10x_cmd_header_t {
__le16 req_id;
__le16 prog;
__le16 version;
__le16 data_len;
} __packed;
#define DUMP_BLOCK_SIZE 16
union as10x_dump_memory {
/* request */
struct {
/* request identifier */
__le16 proc_id;
/* dump memory type request */
uint8_t dump_req;
/* register description */
struct as10x_register_addr reg_addr;
/* nb blocks to read */
__le16 num_blocks;
} __packed req;
/* response */
struct {
/* response identifier */
__le16 proc_id;
/* error */
uint8_t error;
/* dump response */
uint8_t dump_rsp;
/* data */
union {
uint8_t data8[DUMP_BLOCK_SIZE];
__le16 data16[DUMP_BLOCK_SIZE / sizeof(__le16)];
__le32 data32[DUMP_BLOCK_SIZE / sizeof(__le32)];
} __packed u;
} __packed rsp;
} __packed;
union as10x_dumplog_memory {
struct {
/* request identifier */
__le16 proc_id;
/* dump memory type request */
uint8_t dump_req;
} __packed req;
struct {
/* request identifier */
__le16 proc_id;
/* error */
uint8_t error;
/* dump response */
uint8_t dump_rsp;
/* dump data */
uint8_t data[DUMP_BLOCK_SIZE];
} __packed rsp;
} __packed;
union as10x_raw_data {
/* request */
struct {
__le16 proc_id;
uint8_t data[64 - sizeof(struct as10x_cmd_header_t)
- 2 /* proc_id */];
} __packed req;
/* response */
struct {
__le16 proc_id;
uint8_t error;
uint8_t data[64 - sizeof(struct as10x_cmd_header_t)
- 2 /* proc_id */ - 1 /* rc */];
} __packed rsp;
} __packed;
struct as10x_cmd_t {
struct as10x_cmd_header_t header;
union {
union as10x_turn_on turn_on;
union as10x_turn_off turn_off;
union as10x_set_tune set_tune;
union as10x_get_tune_status get_tune_status;
union as10x_get_tps get_tps;
union as10x_common common;
union as10x_add_pid_filter add_pid_filter;
union as10x_del_pid_filter del_pid_filter;
union as10x_start_streaming start_streaming;
union as10x_stop_streaming stop_streaming;
union as10x_get_demod_stats get_demod_stats;
union as10x_get_impulse_resp get_impulse_rsp;
union as10x_fw_context context;
union as10x_set_register set_register;
union as10x_get_register get_register;
union as10x_cfg_change_mode cfg_change_mode;
union as10x_dump_memory dump_memory;
union as10x_dumplog_memory dumplog_memory;
union as10x_raw_data raw_data;
} __packed body;
} __packed;
struct as10x_token_cmd_t {
/* token cmd */
struct as10x_cmd_t c;
/* token response */
struct as10x_cmd_t r;
} __packed;
/**************************/
/* FUNCTION DECLARATION */
/**************************/
void as10x_cmd_build(struct as10x_cmd_t *pcmd, uint16_t proc_id,
uint16_t cmd_len);
int as10x_rsp_parse(struct as10x_cmd_t *r, uint16_t proc_id);
/* as10x cmd */
int as10x_cmd_turn_on(struct as10x_bus_adapter_t *adap);
int as10x_cmd_turn_off(struct as10x_bus_adapter_t *adap);
int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap,
struct as10x_tune_args *ptune);
int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap,
struct as10x_tune_status *pstatus);
int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap,
struct as10x_tps *ptps);
int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap,
struct as10x_demod_stats *pdemod_stats);
int as10x_cmd_get_impulse_resp(struct as10x_bus_adapter_t *adap,
uint8_t *is_ready);
/* as10x cmd stream */
int as10x_cmd_add_PID_filter(struct as10x_bus_adapter_t *adap,
struct as10x_ts_filter *filter);
int as10x_cmd_del_PID_filter(struct as10x_bus_adapter_t *adap,
uint16_t pid_value);
int as10x_cmd_start_streaming(struct as10x_bus_adapter_t *adap);
int as10x_cmd_stop_streaming(struct as10x_bus_adapter_t *adap);
/* as10x cmd cfg */
int as10x_cmd_set_context(struct as10x_bus_adapter_t *adap,
uint16_t tag,
uint32_t value);
int as10x_cmd_get_context(struct as10x_bus_adapter_t *adap,
uint16_t tag,
uint32_t *pvalue);
int as10x_cmd_eLNA_change_mode(struct as10x_bus_adapter_t *adap, uint8_t mode);
int as10x_context_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id);
#endif

View file

@ -0,0 +1,201 @@
/*
* Abilis Systems Single DVB-T Receiver
* Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include "as102_drv.h"
#include "as10x_cmd.h"
/***************************/
/* FUNCTION DEFINITION */
/***************************/
/**
* as10x_cmd_get_context - Send get context command to AS10x
* @adap: pointer to AS10x bus adapter
* @tag: context tag
* @pvalue: pointer where to store context value read
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_get_context(struct as10x_bus_adapter_t *adap, uint16_t tag,
uint32_t *pvalue)
{
int error;
struct as10x_cmd_t *pcmd, *prsp;
pcmd = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(pcmd, (++adap->cmd_xid),
sizeof(pcmd->body.context.req));
/* fill command */
pcmd->body.context.req.proc_id = cpu_to_le16(CONTROL_PROC_CONTEXT);
pcmd->body.context.req.tag = cpu_to_le16(tag);
pcmd->body.context.req.type = cpu_to_le16(GET_CONTEXT_DATA);
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(adap,
(uint8_t *) pcmd,
sizeof(pcmd->body.context.req)
+ HEADER_SIZE,
(uint8_t *) prsp,
sizeof(prsp->body.context.rsp)
+ HEADER_SIZE);
} else {
error = AS10X_CMD_ERROR;
}
if (error < 0)
goto out;
/* parse response: context command do not follow the common response */
/* structure -> specific handling response parse required */
error = as10x_context_rsp_parse(prsp, CONTROL_PROC_CONTEXT_RSP);
if (error == 0) {
/* Response OK -> get response data */
*pvalue = le32_to_cpu((__force __le32)prsp->body.context.rsp.reg_val.u.value32);
/* value returned is always a 32-bit value */
}
out:
return error;
}
/**
* as10x_cmd_set_context - send set context command to AS10x
* @adap: pointer to AS10x bus adapter
* @tag: context tag
* @value: value to set in context
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_set_context(struct as10x_bus_adapter_t *adap, uint16_t tag,
uint32_t value)
{
int error;
struct as10x_cmd_t *pcmd, *prsp;
pcmd = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(pcmd, (++adap->cmd_xid),
sizeof(pcmd->body.context.req));
/* fill command */
pcmd->body.context.req.proc_id = cpu_to_le16(CONTROL_PROC_CONTEXT);
/* pcmd->body.context.req.reg_val.mode initialization is not required */
pcmd->body.context.req.reg_val.u.value32 = (__force u32)cpu_to_le32(value);
pcmd->body.context.req.tag = cpu_to_le16(tag);
pcmd->body.context.req.type = cpu_to_le16(SET_CONTEXT_DATA);
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(adap,
(uint8_t *) pcmd,
sizeof(pcmd->body.context.req)
+ HEADER_SIZE,
(uint8_t *) prsp,
sizeof(prsp->body.context.rsp)
+ HEADER_SIZE);
} else {
error = AS10X_CMD_ERROR;
}
if (error < 0)
goto out;
/* parse response: context command do not follow the common response */
/* structure -> specific handling response parse required */
error = as10x_context_rsp_parse(prsp, CONTROL_PROC_CONTEXT_RSP);
out:
return error;
}
/**
* as10x_cmd_eLNA_change_mode - send eLNA change mode command to AS10x
* @adap: pointer to AS10x bus adapter
* @mode: mode selected:
* - ON : 0x0 => eLNA always ON
* - OFF : 0x1 => eLNA always OFF
* - AUTO : 0x2 => eLNA follow hysteresis parameters
* to be ON or OFF
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_eLNA_change_mode(struct as10x_bus_adapter_t *adap, uint8_t mode)
{
int error;
struct as10x_cmd_t *pcmd, *prsp;
pcmd = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(pcmd, (++adap->cmd_xid),
sizeof(pcmd->body.cfg_change_mode.req));
/* fill command */
pcmd->body.cfg_change_mode.req.proc_id =
cpu_to_le16(CONTROL_PROC_ELNA_CHANGE_MODE);
pcmd->body.cfg_change_mode.req.mode = mode;
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd,
sizeof(pcmd->body.cfg_change_mode.req)
+ HEADER_SIZE, (uint8_t *) prsp,
sizeof(prsp->body.cfg_change_mode.rsp)
+ HEADER_SIZE);
} else {
error = AS10X_CMD_ERROR;
}
if (error < 0)
goto out;
/* parse response */
error = as10x_rsp_parse(prsp, CONTROL_PROC_ELNA_CHANGE_MODE_RSP);
out:
return error;
}
/**
* as10x_context_rsp_parse - Parse context command response
* @prsp: pointer to AS10x command response buffer
* @proc_id: id of the command
*
* Since the contex command response does not follow the common
* response, a specific parse function is required.
* Return 0 on success or negative value in case of error.
*/
int as10x_context_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id)
{
int err;
err = prsp->body.context.rsp.error;
if ((err == 0) &&
(le16_to_cpu(prsp->body.context.rsp.proc_id) == proc_id)) {
return 0;
}
return AS10X_CMD_ERROR;
}

View file

@ -0,0 +1,207 @@
/*
* Abilis Systems Single DVB-T Receiver
* Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include "as102_drv.h"
#include "as10x_cmd.h"
/**
* as10x_cmd_add_PID_filter - send add filter command to AS10x
* @adap: pointer to AS10x bus adapter
* @filter: TSFilter filter for DVB-T
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_add_PID_filter(struct as10x_bus_adapter_t *adap,
struct as10x_ts_filter *filter)
{
int error;
struct as10x_cmd_t *pcmd, *prsp;
pcmd = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(pcmd, (++adap->cmd_xid),
sizeof(pcmd->body.add_pid_filter.req));
/* fill command */
pcmd->body.add_pid_filter.req.proc_id =
cpu_to_le16(CONTROL_PROC_SETFILTER);
pcmd->body.add_pid_filter.req.pid = cpu_to_le16(filter->pid);
pcmd->body.add_pid_filter.req.stream_type = filter->type;
if (filter->idx < 16)
pcmd->body.add_pid_filter.req.idx = filter->idx;
else
pcmd->body.add_pid_filter.req.idx = 0xFF;
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd,
sizeof(pcmd->body.add_pid_filter.req)
+ HEADER_SIZE, (uint8_t *) prsp,
sizeof(prsp->body.add_pid_filter.rsp)
+ HEADER_SIZE);
} else {
error = AS10X_CMD_ERROR;
}
if (error < 0)
goto out;
/* parse response */
error = as10x_rsp_parse(prsp, CONTROL_PROC_SETFILTER_RSP);
if (error == 0) {
/* Response OK -> get response data */
filter->idx = prsp->body.add_pid_filter.rsp.filter_id;
}
out:
return error;
}
/**
* as10x_cmd_del_PID_filter - Send delete filter command to AS10x
* @adap: pointer to AS10x bus adapte
* @pid_value: PID to delete
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_del_PID_filter(struct as10x_bus_adapter_t *adap,
uint16_t pid_value)
{
int error;
struct as10x_cmd_t *pcmd, *prsp;
pcmd = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(pcmd, (++adap->cmd_xid),
sizeof(pcmd->body.del_pid_filter.req));
/* fill command */
pcmd->body.del_pid_filter.req.proc_id =
cpu_to_le16(CONTROL_PROC_REMOVEFILTER);
pcmd->body.del_pid_filter.req.pid = cpu_to_le16(pid_value);
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd,
sizeof(pcmd->body.del_pid_filter.req)
+ HEADER_SIZE, (uint8_t *) prsp,
sizeof(prsp->body.del_pid_filter.rsp)
+ HEADER_SIZE);
} else {
error = AS10X_CMD_ERROR;
}
if (error < 0)
goto out;
/* parse response */
error = as10x_rsp_parse(prsp, CONTROL_PROC_REMOVEFILTER_RSP);
out:
return error;
}
/**
* as10x_cmd_start_streaming - Send start streaming command to AS10x
* @adap: pointer to AS10x bus adapter
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_start_streaming(struct as10x_bus_adapter_t *adap)
{
int error;
struct as10x_cmd_t *pcmd, *prsp;
pcmd = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(pcmd, (++adap->cmd_xid),
sizeof(pcmd->body.start_streaming.req));
/* fill command */
pcmd->body.start_streaming.req.proc_id =
cpu_to_le16(CONTROL_PROC_START_STREAMING);
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd,
sizeof(pcmd->body.start_streaming.req)
+ HEADER_SIZE, (uint8_t *) prsp,
sizeof(prsp->body.start_streaming.rsp)
+ HEADER_SIZE);
} else {
error = AS10X_CMD_ERROR;
}
if (error < 0)
goto out;
/* parse response */
error = as10x_rsp_parse(prsp, CONTROL_PROC_START_STREAMING_RSP);
out:
return error;
}
/**
* as10x_cmd_stop_streaming - Send stop streaming command to AS10x
* @adap: pointer to AS10x bus adapter
*
* Return 0 on success or negative value in case of error.
*/
int as10x_cmd_stop_streaming(struct as10x_bus_adapter_t *adap)
{
int8_t error;
struct as10x_cmd_t *pcmd, *prsp;
pcmd = adap->cmd;
prsp = adap->rsp;
/* prepare command */
as10x_cmd_build(pcmd, (++adap->cmd_xid),
sizeof(pcmd->body.stop_streaming.req));
/* fill command */
pcmd->body.stop_streaming.req.proc_id =
cpu_to_le16(CONTROL_PROC_STOP_STREAMING);
/* send command */
if (adap->ops->xfer_cmd) {
error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd,
sizeof(pcmd->body.stop_streaming.req)
+ HEADER_SIZE, (uint8_t *) prsp,
sizeof(prsp->body.stop_streaming.rsp)
+ HEADER_SIZE);
} else {
error = AS10X_CMD_ERROR;
}
if (error < 0)
goto out;
/* parse response */
error = as10x_rsp_parse(prsp, CONTROL_PROC_STOP_STREAMING_RSP);
out:
return error;
}

View file

@ -0,0 +1,51 @@
/*
* Abilis Systems Single DVB-T Receiver
* Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _AS10X_HANDLE_H
#define _AS10X_HANDLE_H
struct as10x_bus_adapter_t;
struct as102_dev_t;
#include "as10x_cmd.h"
/* values for "mode" field */
#define REGMODE8 8
#define REGMODE16 16
#define REGMODE32 32
struct as102_priv_ops_t {
int (*upload_fw_pkt)(struct as10x_bus_adapter_t *bus_adap,
unsigned char *buf, int buflen, int swap32);
int (*send_cmd)(struct as10x_bus_adapter_t *bus_adap,
unsigned char *buf, int buflen);
int (*xfer_cmd)(struct as10x_bus_adapter_t *bus_adap,
unsigned char *send_buf, int send_buf_len,
unsigned char *recv_buf, int recv_buf_len);
int (*start_stream)(struct as102_dev_t *dev);
void (*stop_stream)(struct as102_dev_t *dev);
int (*reset_target)(struct as10x_bus_adapter_t *bus_adap);
int (*read_write)(struct as10x_bus_adapter_t *bus_adap, uint8_t mode,
uint32_t rd_addr, uint16_t rd_len,
uint32_t wr_addr, uint16_t wr_len);
int (*as102_read_ep2)(struct as10x_bus_adapter_t *bus_adap,
unsigned char *recv_buf,
int recv_buf_len);
};
#endif

View file

@ -0,0 +1,36 @@
config VIDEO_AU0828
tristate "Auvitek AU0828 support"
depends on I2C && INPUT && DVB_CORE && USB
select I2C_ALGOBIT
select VIDEO_TVEEPROM
select VIDEOBUF_VMALLOC
select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
---help---
This is a hybrid analog/digital tv capture driver for
Auvitek's AU0828 USB device.
To compile this driver as a module, choose M here: the
module will be called au0828
config VIDEO_AU0828_V4L2
bool "Auvitek AU0828 v4l2 analog video support"
depends on VIDEO_AU0828 && VIDEO_V4L2
select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT
select VIDEO_TUNER
default y
---help---
This is a video4linux driver for Auvitek's USB device.
Choose Y here to include support for v4l2 analog video
capture within the au0828 driver.
config VIDEO_AU0828_RC
bool "AU0828 Remote Controller support"
depends on RC_CORE
depends on VIDEO_AU0828
---help---
Enables Remote Controller support on au0828 driver.

View file

@ -0,0 +1,17 @@
au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o
ifeq ($(CONFIG_VIDEO_AU0828_V4L2),y)
au0828-objs += au0828-video.o au0828-vbi.o
endif
ifeq ($(CONFIG_VIDEO_AU0828_RC),y)
au0828-objs += au0828-input.o
endif
obj-$(CONFIG_VIDEO_AU0828) += au0828.o
ccflags-y += -Idrivers/media/tuners
ccflags-y += -Idrivers/media/dvb-core
ccflags-y += -Idrivers/media/dvb-frontends
ccflags-y += $(extra-cflags-y) $(extra-cflags-m)

View file

@ -0,0 +1,362 @@
/*
* Driver for the Auvitek USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "au0828.h"
#include "au0828-cards.h"
#include "au8522.h"
#include "media/tuner.h"
#include "media/v4l2-common.h"
static void hvr950q_cs5340_audio(void *priv, int enable)
{
/* Because the HVR-950q shares an i2s bus between the cs5340 and the
au8522, we need to hold cs5340 in reset when using the au8522 */
struct au0828_dev *dev = priv;
if (enable == 1)
au0828_set(dev, REG_000, 0x10);
else
au0828_clear(dev, REG_000, 0x10);
}
/*
* WARNING: There's a quirks table at sound/usb/quirks-table.h
* that should also be updated every time a new device with V4L2 support
* is added here.
*/
struct au0828_board au0828_boards[] = {
[AU0828_BOARD_UNKNOWN] = {
.name = "Unknown board",
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
},
[AU0828_BOARD_HAUPPAUGE_HVR850] = {
.name = "Hauppauge HVR850",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
.has_ir_i2c = 1,
.has_analog = 1,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
.input = {
{
.type = AU0828_VMUX_TELEVISION,
.vmux = AU8522_COMPOSITE_CH4_SIF,
.amux = AU8522_AUDIO_SIF,
},
{
.type = AU0828_VMUX_COMPOSITE,
.vmux = AU8522_COMPOSITE_CH1,
.amux = AU8522_AUDIO_NONE,
.audio_setup = hvr950q_cs5340_audio,
},
{
.type = AU0828_VMUX_SVIDEO,
.vmux = AU8522_SVIDEO_CH13,
.amux = AU8522_AUDIO_NONE,
.audio_setup = hvr950q_cs5340_audio,
},
},
},
[AU0828_BOARD_HAUPPAUGE_HVR950Q] = {
.name = "Hauppauge HVR950Q",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
.has_ir_i2c = 1,
.has_analog = 1,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
.input = {
{
.type = AU0828_VMUX_TELEVISION,
.vmux = AU8522_COMPOSITE_CH4_SIF,
.amux = AU8522_AUDIO_SIF,
},
{
.type = AU0828_VMUX_COMPOSITE,
.vmux = AU8522_COMPOSITE_CH1,
.amux = AU8522_AUDIO_NONE,
.audio_setup = hvr950q_cs5340_audio,
},
{
.type = AU0828_VMUX_SVIDEO,
.vmux = AU8522_SVIDEO_CH13,
.amux = AU8522_AUDIO_NONE,
.audio_setup = hvr950q_cs5340_audio,
},
},
},
[AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = {
.name = "Hauppauge HVR950Q rev xxF8",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
},
[AU0828_BOARD_DVICO_FUSIONHDTV7] = {
.name = "DViCO FusionHDTV USB",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
},
[AU0828_BOARD_HAUPPAUGE_WOODBURY] = {
.name = "Hauppauge Woodbury",
.tuner_type = TUNER_NXP_TDA18271,
.tuner_addr = 0x60,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
},
};
/* Tuner callback function for au0828 boards. Currently only needed
* for HVR1500Q, which has an xc5000 tuner.
*/
int au0828_tuner_callback(void *priv, int component, int command, int arg)
{
struct au0828_dev *dev = priv;
dprintk(1, "%s()\n", __func__);
switch (dev->boardnr) {
case AU0828_BOARD_HAUPPAUGE_HVR850:
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
case AU0828_BOARD_DVICO_FUSIONHDTV7:
if (command == 0) {
/* Tuner Reset Command from xc5000 */
/* Drive the tuner into reset and out */
au0828_clear(dev, REG_001, 2);
mdelay(10);
au0828_set(dev, REG_001, 2);
mdelay(10);
return 0;
} else {
pr_err("%s(): Unknown command.\n", __func__);
return -EINVAL;
}
break;
}
return 0; /* Should never be here */
}
static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data)
{
struct tveeprom tv;
tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data);
dev->board.tuner_type = tv.tuner_type;
/* Make sure we support the board model */
switch (tv.model) {
case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */
case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
case 72101: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
case 72201: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */
case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
case 72261: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */
case 72271: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */
case 72281: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */
case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */
case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */
break;
default:
pr_warn("%s: warning: unknown hauppauge model #%d\n",
__func__, tv.model);
break;
}
pr_info("%s: hauppauge eeprom: model=%d\n",
__func__, tv.model);
}
void au0828_card_analog_fe_setup(struct au0828_dev *dev);
void au0828_card_setup(struct au0828_dev *dev)
{
static u8 eeprom[256];
dprintk(1, "%s()\n", __func__);
dev->board = au0828_boards[dev->boardnr];
if (dev->i2c_rc == 0) {
dev->i2c_client.addr = 0xa0 >> 1;
tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom));
}
switch (dev->boardnr) {
case AU0828_BOARD_HAUPPAUGE_HVR850:
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
case AU0828_BOARD_HAUPPAUGE_WOODBURY:
if (dev->i2c_rc == 0)
hauppauge_eeprom(dev, eeprom+0xa0);
break;
}
au0828_card_analog_fe_setup(dev);
}
void au0828_card_analog_fe_setup(struct au0828_dev *dev)
{
#ifdef CONFIG_VIDEO_AU0828_V4L2
struct tuner_setup tun_setup;
struct v4l2_subdev *sd;
unsigned int mode_mask = T_ANALOG_TV;
if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) {
/* Load the analog demodulator driver (note this would need to
be abstracted out if we ever need to support a different
demod) */
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"au8522", 0x8e >> 1, NULL);
if (sd == NULL)
pr_err("analog subdev registration failed\n");
}
/* Setup tuners */
if (dev->board.tuner_type != TUNER_ABSENT && dev->board.has_analog) {
/* Load the tuner module, which does the attach */
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tuner", dev->board.tuner_addr, NULL);
if (sd == NULL)
pr_err("tuner subdev registration fail\n");
tun_setup.mode_mask = mode_mask;
tun_setup.type = dev->board.tuner_type;
tun_setup.addr = dev->board.tuner_addr;
tun_setup.tuner_callback = au0828_tuner_callback;
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr,
&tun_setup);
}
#endif
}
/*
* The bridge has between 8 and 12 gpios.
* Regs 1 and 0 deal with output enables.
* Regs 3 and 2 deal with direction.
*/
void au0828_gpio_setup(struct au0828_dev *dev)
{
dprintk(1, "%s()\n", __func__);
switch (dev->boardnr) {
case AU0828_BOARD_HAUPPAUGE_HVR850:
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
case AU0828_BOARD_HAUPPAUGE_WOODBURY:
/* GPIO's
* 4 - CS5340
* 5 - AU8522 Demodulator
* 6 - eeprom W/P
* 7 - power supply
* 9 - XC5000 Tuner
*/
/* Set relevant GPIOs as outputs (leave the EEPROM W/P
as an input since we will never touch it and it has
a pullup) */
au0828_write(dev, REG_003, 0x02);
au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
/* Into reset */
au0828_write(dev, REG_001, 0x0);
au0828_write(dev, REG_000, 0x0);
msleep(50);
/* Bring power supply out of reset */
au0828_write(dev, REG_000, 0x80);
msleep(50);
/* Bring xc5000 and au8522 out of reset (leave the
cs5340 in reset until needed) */
au0828_write(dev, REG_001, 0x02); /* xc5000 */
au0828_write(dev, REG_000, 0x80 | 0x20); /* PS + au8522 */
msleep(250);
break;
case AU0828_BOARD_DVICO_FUSIONHDTV7:
/* GPIO's
* 6 - ?
* 8 - AU8522 Demodulator
* 9 - XC5000 Tuner
*/
/* Into reset */
au0828_write(dev, REG_003, 0x02);
au0828_write(dev, REG_002, 0xa0);
au0828_write(dev, REG_001, 0x0);
au0828_write(dev, REG_000, 0x0);
msleep(100);
/* Out of reset */
au0828_write(dev, REG_003, 0x02);
au0828_write(dev, REG_002, 0xa0);
au0828_write(dev, REG_001, 0x02);
au0828_write(dev, REG_000, 0xa0);
msleep(250);
break;
}
}
/* table of devices that work with this driver */
struct usb_device_id au0828_usb_id_table[] = {
{ USB_DEVICE(0x2040, 0x7200),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7240),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 },
{ USB_DEVICE(0x0fe9, 0xd620),
.driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 },
{ USB_DEVICE(0x2040, 0x7210),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7217),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x721b),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x721e),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x721f),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7280),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x0fd9, 0x0008),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7201),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
{ USB_DEVICE(0x2040, 0x7211),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
{ USB_DEVICE(0x2040, 0x7281),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
{ USB_DEVICE(0x05e1, 0x0480),
.driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY },
{ USB_DEVICE(0x2040, 0x8200),
.driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY },
{ USB_DEVICE(0x2040, 0x7260),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7213),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7270),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ },
};
MODULE_DEVICE_TABLE(usb, au0828_usb_id_table);

View file

@ -0,0 +1,27 @@
/*
* Driver for the Auvitek USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define AU0828_BOARD_UNKNOWN 0
#define AU0828_BOARD_HAUPPAUGE_HVR950Q 1
#define AU0828_BOARD_HAUPPAUGE_HVR850 2
#define AU0828_BOARD_DVICO_FUSIONHDTV7 3
#define AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL 4
#define AU0828_BOARD_HAUPPAUGE_WOODBURY 5

View file

@ -0,0 +1,376 @@
/*
* Driver for the Auvitek USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "au0828.h"
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/mutex.h>
/*
* 1 = General debug messages
* 2 = USB handling
* 4 = I2C related
* 8 = Bridge related
* 16 = IR related
*/
int au0828_debug;
module_param_named(debug, au0828_debug, int, 0644);
MODULE_PARM_DESC(debug,
"set debug bitmask: 1=general, 2=USB, 4=I2C, 8=bridge, 16=IR");
static unsigned int disable_usb_speed_check;
module_param(disable_usb_speed_check, int, 0444);
MODULE_PARM_DESC(disable_usb_speed_check,
"override min bandwidth requirement of 480M bps");
#define _AU0828_BULKPIPE 0x03
#define _BULKPIPESIZE 0xffff
static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
u16 index);
static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
u16 index, unsigned char *cp, u16 size);
/* USB Direction */
#define CMD_REQUEST_IN 0x00
#define CMD_REQUEST_OUT 0x01
u32 au0828_readreg(struct au0828_dev *dev, u16 reg)
{
u8 result = 0;
recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, &result, 1);
dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, result);
return result;
}
u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val)
{
dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
return send_control_msg(dev, CMD_REQUEST_OUT, val, reg);
}
static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
u16 index)
{
int status = -ENODEV;
if (dev->usbdev) {
/* cp must be memory that has been allocated by kmalloc */
status = usb_control_msg(dev->usbdev,
usb_sndctrlpipe(dev->usbdev, 0),
request,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_DEVICE,
value, index, NULL, 0, 1000);
status = min(status, 0);
if (status < 0) {
pr_err("%s() Failed sending control message, error %d.\n",
__func__, status);
}
}
return status;
}
static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
u16 index, unsigned char *cp, u16 size)
{
int status = -ENODEV;
mutex_lock(&dev->mutex);
if (dev->usbdev) {
status = usb_control_msg(dev->usbdev,
usb_rcvctrlpipe(dev->usbdev, 0),
request,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index,
dev->ctrlmsg, size, 1000);
status = min(status, 0);
if (status < 0) {
pr_err("%s() Failed receiving control message, error %d.\n",
__func__, status);
}
/* the host controller requires heap allocated memory, which
is why we didn't just pass "cp" into usb_control_msg */
memcpy(cp, dev->ctrlmsg, size);
}
mutex_unlock(&dev->mutex);
return status;
}
static void au0828_usb_release(struct au0828_dev *dev)
{
/* I2C */
au0828_i2c_unregister(dev);
kfree(dev);
}
#ifdef CONFIG_VIDEO_AU0828_V4L2
static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev)
{
struct au0828_dev *dev =
container_of(v4l2_dev, struct au0828_dev, v4l2_dev);
v4l2_ctrl_handler_free(&dev->v4l2_ctrl_hdl);
v4l2_device_unregister(&dev->v4l2_dev);
au0828_usb_release(dev);
}
#endif
static void au0828_usb_disconnect(struct usb_interface *interface)
{
struct au0828_dev *dev = usb_get_intfdata(interface);
dprintk(1, "%s()\n", __func__);
au0828_rc_unregister(dev);
/* Digital TV */
au0828_dvb_unregister(dev);
usb_set_intfdata(interface, NULL);
mutex_lock(&dev->mutex);
dev->usbdev = NULL;
mutex_unlock(&dev->mutex);
#ifdef CONFIG_VIDEO_AU0828_V4L2
if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) {
au0828_analog_unregister(dev);
v4l2_device_disconnect(&dev->v4l2_dev);
v4l2_device_put(&dev->v4l2_dev);
return;
}
#endif
au0828_usb_release(dev);
}
static int au0828_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
int ifnum;
int retval = 0;
struct au0828_dev *dev;
struct usb_device *usbdev = interface_to_usbdev(interface);
ifnum = interface->altsetting->desc.bInterfaceNumber;
if (ifnum != 0)
return -ENODEV;
dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__,
le16_to_cpu(usbdev->descriptor.idVendor),
le16_to_cpu(usbdev->descriptor.idProduct),
ifnum);
/*
* Make sure we have 480 Mbps of bandwidth, otherwise things like
* video stream wouldn't likely work, since 12 Mbps is generally
* not enough even for most Digital TV streams.
*/
if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
pr_err("au0828: Device initialization failed.\n");
pr_err("au0828: Device must be connected to a high-speed USB 2.0 port.\n");
return -ENODEV;
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
pr_err("%s() Unable to allocate memory\n", __func__);
return -ENOMEM;
}
mutex_init(&dev->lock);
mutex_lock(&dev->lock);
mutex_init(&dev->mutex);
mutex_init(&dev->dvb.lock);
dev->usbdev = usbdev;
dev->boardnr = id->driver_info;
#ifdef CONFIG_VIDEO_AU0828_V4L2
dev->v4l2_dev.release = au0828_usb_v4l2_release;
/* Create the v4l2_device */
retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
if (retval) {
pr_err("%s() v4l2_device_register failed\n",
__func__);
mutex_unlock(&dev->lock);
kfree(dev);
return retval;
}
/* This control handler will inherit the controls from au8522 */
retval = v4l2_ctrl_handler_init(&dev->v4l2_ctrl_hdl, 4);
if (retval) {
pr_err("%s() v4l2_ctrl_handler_init failed\n",
__func__);
mutex_unlock(&dev->lock);
kfree(dev);
return retval;
}
dev->v4l2_dev.ctrl_handler = &dev->v4l2_ctrl_hdl;
#endif
/* Power Up the bridge */
au0828_write(dev, REG_600, 1 << 4);
/* Bring up the GPIO's and supporting devices */
au0828_gpio_setup(dev);
/* I2C */
au0828_i2c_register(dev);
/* Setup */
au0828_card_setup(dev);
#ifdef CONFIG_VIDEO_AU0828_V4L2
/* Analog TV */
if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED)
au0828_analog_register(dev, interface);
#endif
/* Digital TV */
retval = au0828_dvb_register(dev);
if (retval)
pr_err("%s() au0282_dev_register failed\n",
__func__);
/* Remote controller */
au0828_rc_register(dev);
/*
* Store the pointer to the au0828_dev so it can be accessed in
* au0828_usb_disconnect
*/
usb_set_intfdata(interface, dev);
pr_info("Registered device AU0828 [%s]\n",
dev->board.name == NULL ? "Unset" : dev->board.name);
mutex_unlock(&dev->lock);
return retval;
}
static int au0828_suspend(struct usb_interface *interface,
pm_message_t message)
{
struct au0828_dev *dev = usb_get_intfdata(interface);
if (!dev)
return 0;
pr_info("Suspend\n");
au0828_rc_suspend(dev);
au0828_v4l2_suspend(dev);
au0828_dvb_suspend(dev);
/* FIXME: should suspend also ATV/DTV */
return 0;
}
static int au0828_resume(struct usb_interface *interface)
{
struct au0828_dev *dev = usb_get_intfdata(interface);
if (!dev)
return 0;
pr_info("Resume\n");
/* Power Up the bridge */
au0828_write(dev, REG_600, 1 << 4);
/* Bring up the GPIO's and supporting devices */
au0828_gpio_setup(dev);
au0828_rc_resume(dev);
au0828_v4l2_resume(dev);
au0828_dvb_resume(dev);
/* FIXME: should resume also ATV/DTV */
return 0;
}
static struct usb_driver au0828_usb_driver = {
.name = KBUILD_MODNAME,
.probe = au0828_usb_probe,
.disconnect = au0828_usb_disconnect,
.id_table = au0828_usb_id_table,
.suspend = au0828_suspend,
.resume = au0828_resume,
.reset_resume = au0828_resume,
};
static int __init au0828_init(void)
{
int ret;
if (au0828_debug & 1)
pr_info("%s() Debugging is enabled\n", __func__);
if (au0828_debug & 2)
pr_info("%s() USB Debugging is enabled\n", __func__);
if (au0828_debug & 4)
pr_info("%s() I2C Debugging is enabled\n", __func__);
if (au0828_debug & 8)
pr_info("%s() Bridge Debugging is enabled\n",
__func__);
if (au0828_debug & 16)
pr_info("%s() IR Debugging is enabled\n",
__func__);
pr_info("au0828 driver loaded\n");
ret = usb_register(&au0828_usb_driver);
if (ret)
pr_err("usb_register failed, error = %d\n", ret);
return ret;
}
static void __exit au0828_exit(void)
{
usb_deregister(&au0828_usb_driver);
}
module_init(au0828_init);
module_exit(au0828_exit);
MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products");
MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.0.3");

View file

@ -0,0 +1,658 @@
/*
* Driver for the Auvitek USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "au0828.h"
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
#include "au8522.h"
#include "xc5000.h"
#include "mxl5007t.h"
#include "tda18271.h"
static int preallocate_big_buffers;
module_param_named(preallocate_big_buffers, preallocate_big_buffers, int, 0644);
MODULE_PARM_DESC(preallocate_big_buffers, "Preallocate the larger transfer buffers at module load time");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define _AU0828_BULKPIPE 0x83
#define _BULKPIPESIZE 0xe522
static u8 hauppauge_hvr950q_led_states[] = {
0x00, /* off */
0x02, /* yellow */
0x04, /* green */
};
static struct au8522_led_config hauppauge_hvr950q_led_cfg = {
.gpio_output = 0x00e0,
.gpio_output_enable = 0x6006,
.gpio_output_disable = 0x0660,
.gpio_leds = 0x00e2,
.led_states = hauppauge_hvr950q_led_states,
.num_led_states = sizeof(hauppauge_hvr950q_led_states),
.vsb8_strong = 20 /* dB */ * 10,
.qam64_strong = 25 /* dB */ * 10,
.qam256_strong = 32 /* dB */ * 10,
};
static struct au8522_config hauppauge_hvr950q_config = {
.demod_address = 0x8e >> 1,
.status_mode = AU8522_DEMODLOCKING,
.qam_if = AU8522_IF_6MHZ,
.vsb_if = AU8522_IF_6MHZ,
.led_cfg = &hauppauge_hvr950q_led_cfg,
};
static struct au8522_config fusionhdtv7usb_config = {
.demod_address = 0x8e >> 1,
.status_mode = AU8522_DEMODLOCKING,
.qam_if = AU8522_IF_6MHZ,
.vsb_if = AU8522_IF_6MHZ,
};
static struct au8522_config hauppauge_woodbury_config = {
.demod_address = 0x8e >> 1,
.status_mode = AU8522_DEMODLOCKING,
.qam_if = AU8522_IF_4MHZ,
.vsb_if = AU8522_IF_3_25MHZ,
};
static struct xc5000_config hauppauge_xc5000a_config = {
.i2c_address = 0x61,
.if_khz = 6000,
.chip_id = XC5000A,
};
static struct xc5000_config hauppauge_xc5000c_config = {
.i2c_address = 0x61,
.if_khz = 6000,
.chip_id = XC5000C,
};
static struct mxl5007t_config mxl5007t_hvr950q_config = {
.xtal_freq_hz = MxL_XTAL_24_MHZ,
.if_freq_hz = MxL_IF_6_MHZ,
};
static struct tda18271_config hauppauge_woodbury_tunerconfig = {
.gate = TDA18271_GATE_DIGITAL,
};
static void au0828_restart_dvb_streaming(struct work_struct *work);
/*-------------------------------------------------------------------*/
static void urb_completion(struct urb *purb)
{
struct au0828_dev *dev = purb->context;
int ptype = usb_pipetype(purb->pipe);
unsigned char *ptr;
dprintk(2, "%s: %d\n", __func__, purb->actual_length);
if (!dev) {
dprintk(2, "%s: no dev!\n", __func__);
return;
}
if (!dev->urb_streaming) {
dprintk(2, "%s: not streaming!\n", __func__);
return;
}
if (ptype != PIPE_BULK) {
pr_err("%s: Unsupported URB type %d\n",
__func__, ptype);
return;
}
/* See if the stream is corrupted (to work around a hardware
bug where the stream gets misaligned */
ptr = purb->transfer_buffer;
if (purb->actual_length > 0 && ptr[0] != 0x47) {
dprintk(1, "Need to restart streaming %02x len=%d!\n",
ptr[0], purb->actual_length);
schedule_work(&dev->restart_streaming);
return;
}
/* Feed the transport payload into the kernel demux */
dvb_dmx_swfilter_packets(&dev->dvb.demux,
purb->transfer_buffer, purb->actual_length / 188);
/* Clean the buffer before we requeue */
memset(purb->transfer_buffer, 0, URB_BUFSIZE);
/* Requeue URB */
usb_submit_urb(purb, GFP_ATOMIC);
}
static int stop_urb_transfer(struct au0828_dev *dev)
{
int i;
dprintk(2, "%s()\n", __func__);
if (!dev->urb_streaming)
return 0;
dev->urb_streaming = false;
for (i = 0; i < URB_COUNT; i++) {
if (dev->urbs[i]) {
usb_kill_urb(dev->urbs[i]);
if (!preallocate_big_buffers)
kfree(dev->urbs[i]->transfer_buffer);
usb_free_urb(dev->urbs[i]);
}
}
return 0;
}
static int start_urb_transfer(struct au0828_dev *dev)
{
struct urb *purb;
int i, ret = -ENOMEM;
dprintk(2, "%s()\n", __func__);
if (dev->urb_streaming) {
dprintk(2, "%s: bulk xfer already running!\n", __func__);
return 0;
}
for (i = 0; i < URB_COUNT; i++) {
dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urbs[i])
goto err;
purb = dev->urbs[i];
if (preallocate_big_buffers)
purb->transfer_buffer = dev->dig_transfer_buffer[i];
else
purb->transfer_buffer = kzalloc(URB_BUFSIZE,
GFP_KERNEL);
if (!purb->transfer_buffer) {
usb_free_urb(purb);
dev->urbs[i] = NULL;
pr_err("%s: failed big buffer allocation, err = %d\n",
__func__, ret);
goto err;
}
purb->status = -EINPROGRESS;
usb_fill_bulk_urb(purb,
dev->usbdev,
usb_rcvbulkpipe(dev->usbdev,
_AU0828_BULKPIPE),
purb->transfer_buffer,
URB_BUFSIZE,
urb_completion,
dev);
}
for (i = 0; i < URB_COUNT; i++) {
ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC);
if (ret != 0) {
stop_urb_transfer(dev);
pr_err("%s: failed urb submission, err = %d\n",
__func__, ret);
return ret;
}
}
dev->urb_streaming = true;
ret = 0;
err:
return ret;
}
static void au0828_start_transport(struct au0828_dev *dev)
{
au0828_write(dev, 0x608, 0x90);
au0828_write(dev, 0x609, 0x72);
au0828_write(dev, 0x60a, 0x71);
au0828_write(dev, 0x60b, 0x01);
}
static void au0828_stop_transport(struct au0828_dev *dev, int full_stop)
{
if (full_stop) {
au0828_write(dev, 0x608, 0x00);
au0828_write(dev, 0x609, 0x00);
au0828_write(dev, 0x60a, 0x00);
}
au0828_write(dev, 0x60b, 0x00);
}
static int au0828_dvb_start_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct au0828_dev *dev = (struct au0828_dev *) demux->priv;
struct au0828_dvb *dvb = &dev->dvb;
int ret = 0;
dprintk(1, "%s()\n", __func__);
if (!demux->dmx.frontend)
return -EINVAL;
if (dvb->frontend) {
mutex_lock(&dvb->lock);
dvb->start_count++;
dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__,
dvb->start_count, dvb->stop_count);
if (dvb->feeding++ == 0) {
/* Start transport */
au0828_start_transport(dev);
ret = start_urb_transfer(dev);
if (ret < 0) {
au0828_stop_transport(dev, 0);
dvb->feeding--; /* We ran out of memory... */
}
}
mutex_unlock(&dvb->lock);
}
return ret;
}
static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct au0828_dev *dev = (struct au0828_dev *) demux->priv;
struct au0828_dvb *dvb = &dev->dvb;
int ret = 0;
dprintk(1, "%s()\n", __func__);
if (dvb->frontend) {
cancel_work_sync(&dev->restart_streaming);
mutex_lock(&dvb->lock);
dvb->stop_count++;
dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__,
dvb->start_count, dvb->stop_count);
if (dvb->feeding > 0) {
dvb->feeding--;
if (dvb->feeding == 0) {
/* Stop transport */
ret = stop_urb_transfer(dev);
au0828_stop_transport(dev, 0);
}
}
mutex_unlock(&dvb->lock);
}
return ret;
}
static void au0828_restart_dvb_streaming(struct work_struct *work)
{
struct au0828_dev *dev = container_of(work, struct au0828_dev,
restart_streaming);
struct au0828_dvb *dvb = &dev->dvb;
if (!dev->urb_streaming)
return;
dprintk(1, "Restarting streaming...!\n");
mutex_lock(&dvb->lock);
/* Stop transport */
stop_urb_transfer(dev);
au0828_stop_transport(dev, 1);
/* Start transport */
au0828_start_transport(dev);
start_urb_transfer(dev);
mutex_unlock(&dvb->lock);
}
static int au0828_set_frontend(struct dvb_frontend *fe)
{
struct au0828_dev *dev = fe->dvb->priv;
struct au0828_dvb *dvb = &dev->dvb;
int ret, was_streaming;
mutex_lock(&dvb->lock);
was_streaming = dev->urb_streaming;
if (was_streaming) {
au0828_stop_transport(dev, 1);
/*
* We can't hold a mutex here, as the restart_streaming
* kthread may also hold it.
*/
mutex_unlock(&dvb->lock);
cancel_work_sync(&dev->restart_streaming);
mutex_lock(&dvb->lock);
stop_urb_transfer(dev);
}
mutex_unlock(&dvb->lock);
ret = dvb->set_frontend(fe);
if (was_streaming) {
mutex_lock(&dvb->lock);
au0828_start_transport(dev);
start_urb_transfer(dev);
mutex_unlock(&dvb->lock);
}
return ret;
}
static int dvb_register(struct au0828_dev *dev)
{
struct au0828_dvb *dvb = &dev->dvb;
int result;
dprintk(1, "%s()\n", __func__);
if (preallocate_big_buffers) {
int i;
for (i = 0; i < URB_COUNT; i++) {
dev->dig_transfer_buffer[i] = kzalloc(URB_BUFSIZE,
GFP_KERNEL);
if (!dev->dig_transfer_buffer[i]) {
result = -ENOMEM;
pr_err("failed buffer allocation (errno = %d)\n",
result);
goto fail_adapter;
}
}
}
INIT_WORK(&dev->restart_streaming, au0828_restart_dvb_streaming);
/* register adapter */
result = dvb_register_adapter(&dvb->adapter,
KBUILD_MODNAME, THIS_MODULE,
&dev->usbdev->dev, adapter_nr);
if (result < 0) {
pr_err("dvb_register_adapter failed (errno = %d)\n",
result);
goto fail_adapter;
}
dvb->adapter.priv = dev;
/* register frontend */
result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
if (result < 0) {
pr_err("dvb_register_frontend failed (errno = %d)\n",
result);
goto fail_frontend;
}
/* Hook dvb frontend */
dvb->set_frontend = dvb->frontend->ops.set_frontend;
dvb->frontend->ops.set_frontend = au0828_set_frontend;
/* register demux stuff */
dvb->demux.dmx.capabilities =
DMX_TS_FILTERING | DMX_SECTION_FILTERING |
DMX_MEMORY_BASED_FILTERING;
dvb->demux.priv = dev;
dvb->demux.filternum = 256;
dvb->demux.feednum = 256;
dvb->demux.start_feed = au0828_dvb_start_feed;
dvb->demux.stop_feed = au0828_dvb_stop_feed;
result = dvb_dmx_init(&dvb->demux);
if (result < 0) {
pr_err("dvb_dmx_init failed (errno = %d)\n", result);
goto fail_dmx;
}
dvb->dmxdev.filternum = 256;
dvb->dmxdev.demux = &dvb->demux.dmx;
dvb->dmxdev.capabilities = 0;
result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
if (result < 0) {
pr_err("dvb_dmxdev_init failed (errno = %d)\n", result);
goto fail_dmxdev;
}
dvb->fe_hw.source = DMX_FRONTEND_0;
result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
pr_err("add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
result);
goto fail_fe_hw;
}
dvb->fe_mem.source = DMX_MEMORY_FE;
result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
if (result < 0) {
pr_err("add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
result);
goto fail_fe_mem;
}
result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
pr_err("connect_frontend failed (errno = %d)\n", result);
goto fail_fe_conn;
}
/* register network adapter */
dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
dvb->start_count = 0;
dvb->stop_count = 0;
return 0;
fail_fe_conn:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
fail_fe_mem:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
fail_fe_hw:
dvb_dmxdev_release(&dvb->dmxdev);
fail_dmxdev:
dvb_dmx_release(&dvb->demux);
fail_dmx:
dvb_unregister_frontend(dvb->frontend);
fail_frontend:
dvb_frontend_detach(dvb->frontend);
dvb_unregister_adapter(&dvb->adapter);
fail_adapter:
if (preallocate_big_buffers) {
int i;
for (i = 0; i < URB_COUNT; i++)
kfree(dev->dig_transfer_buffer[i]);
}
return result;
}
void au0828_dvb_unregister(struct au0828_dev *dev)
{
struct au0828_dvb *dvb = &dev->dvb;
dprintk(1, "%s()\n", __func__);
if (dvb->frontend == NULL)
return;
cancel_work_sync(&dev->restart_streaming);
dvb_net_release(&dvb->net);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
dvb_dmxdev_release(&dvb->dmxdev);
dvb_dmx_release(&dvb->demux);
dvb_unregister_frontend(dvb->frontend);
dvb_frontend_detach(dvb->frontend);
dvb_unregister_adapter(&dvb->adapter);
if (preallocate_big_buffers) {
int i;
for (i = 0; i < URB_COUNT; i++)
kfree(dev->dig_transfer_buffer[i]);
}
dvb->frontend = NULL;
}
/* All the DVB attach calls go here, this function get's modified
* for each new card. No other function in this file needs
* to change.
*/
int au0828_dvb_register(struct au0828_dev *dev)
{
struct au0828_dvb *dvb = &dev->dvb;
int ret;
dprintk(1, "%s()\n", __func__);
/* init frontend */
switch (dev->boardnr) {
case AU0828_BOARD_HAUPPAUGE_HVR850:
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
dvb->frontend = dvb_attach(au8522_attach,
&hauppauge_hvr950q_config,
&dev->i2c_adap);
if (dvb->frontend != NULL)
switch (dev->board.tuner_type) {
default:
case TUNER_XC5000:
dvb_attach(xc5000_attach, dvb->frontend,
&dev->i2c_adap,
&hauppauge_xc5000a_config);
break;
case TUNER_XC5000C:
dvb_attach(xc5000_attach, dvb->frontend,
&dev->i2c_adap,
&hauppauge_xc5000c_config);
break;
}
break;
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
dvb->frontend = dvb_attach(au8522_attach,
&hauppauge_hvr950q_config,
&dev->i2c_adap);
if (dvb->frontend != NULL)
dvb_attach(mxl5007t_attach, dvb->frontend,
&dev->i2c_adap, 0x60,
&mxl5007t_hvr950q_config);
break;
case AU0828_BOARD_HAUPPAUGE_WOODBURY:
dvb->frontend = dvb_attach(au8522_attach,
&hauppauge_woodbury_config,
&dev->i2c_adap);
if (dvb->frontend != NULL)
dvb_attach(tda18271_attach, dvb->frontend,
0x60, &dev->i2c_adap,
&hauppauge_woodbury_tunerconfig);
break;
case AU0828_BOARD_DVICO_FUSIONHDTV7:
dvb->frontend = dvb_attach(au8522_attach,
&fusionhdtv7usb_config,
&dev->i2c_adap);
if (dvb->frontend != NULL) {
dvb_attach(xc5000_attach, dvb->frontend,
&dev->i2c_adap,
&hauppauge_xc5000a_config);
}
break;
default:
pr_warn("The frontend of your DVB/ATSC card isn't supported yet\n");
break;
}
if (NULL == dvb->frontend) {
pr_err("%s() Frontend initialization failed\n",
__func__);
return -1;
}
/* define general-purpose callback pointer */
dvb->frontend->callback = au0828_tuner_callback;
/* register everything */
ret = dvb_register(dev);
if (ret < 0) {
if (dvb->frontend->ops.release)
dvb->frontend->ops.release(dvb->frontend);
dvb->frontend = NULL;
return ret;
}
return 0;
}
void au0828_dvb_suspend(struct au0828_dev *dev)
{
struct au0828_dvb *dvb = &dev->dvb;
int rc;
if (dvb->frontend) {
if (dev->urb_streaming) {
cancel_work_sync(&dev->restart_streaming);
/* Stop transport */
mutex_lock(&dvb->lock);
stop_urb_transfer(dev);
au0828_stop_transport(dev, 1);
mutex_unlock(&dvb->lock);
dev->need_urb_start = true;
}
/* suspend frontend - does tuner and fe to sleep */
rc = dvb_frontend_suspend(dvb->frontend);
pr_info("au0828_dvb_suspend(): Suspending DVB fe %d\n", rc);
}
}
void au0828_dvb_resume(struct au0828_dev *dev)
{
struct au0828_dvb *dvb = &dev->dvb;
int rc;
if (dvb->frontend) {
/* resume frontend - does fe and tuner init */
rc = dvb_frontend_resume(dvb->frontend);
pr_info("au0828_dvb_resume(): Resuming DVB fe %d\n", rc);
if (dev->need_urb_start) {
/* Start transport */
mutex_lock(&dvb->lock);
au0828_start_transport(dev);
start_urb_transfer(dev);
mutex_unlock(&dvb->lock);
}
}
}

View file

@ -0,0 +1,414 @@
/*
* Driver for the Auvitek AU0828 USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "au0828.h"
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/io.h>
#include "media/tuner.h"
#include <media/v4l2-common.h>
static int i2c_scan;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
#define I2C_WAIT_DELAY 25
#define I2C_WAIT_RETRY 1000
static inline int i2c_slave_did_write_ack(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_NO_WRITE_ACK ? 0 : 1;
}
static inline int i2c_slave_did_read_ack(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_NO_READ_ACK ? 0 : 1;
}
static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap)
{
int count;
for (count = 0; count < I2C_WAIT_RETRY; count++) {
if (!i2c_slave_did_read_ack(i2c_adap))
break;
udelay(I2C_WAIT_DELAY);
}
if (I2C_WAIT_RETRY == count)
return 0;
return 1;
}
static inline int i2c_is_read_busy(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_READ_DONE ? 0 : 1;
}
static int i2c_wait_read_done(struct i2c_adapter *i2c_adap)
{
int count;
for (count = 0; count < I2C_WAIT_RETRY; count++) {
if (!i2c_is_read_busy(i2c_adap))
break;
udelay(I2C_WAIT_DELAY);
}
if (I2C_WAIT_RETRY == count)
return 0;
return 1;
}
static inline int i2c_is_write_done(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_WRITE_DONE ? 1 : 0;
}
static int i2c_wait_write_done(struct i2c_adapter *i2c_adap)
{
int count;
for (count = 0; count < I2C_WAIT_RETRY; count++) {
if (i2c_is_write_done(i2c_adap))
break;
udelay(I2C_WAIT_DELAY);
}
if (I2C_WAIT_RETRY == count)
return 0;
return 1;
}
static inline int i2c_is_busy(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_BUSY ? 1 : 0;
}
static int i2c_wait_done(struct i2c_adapter *i2c_adap)
{
int count;
for (count = 0; count < I2C_WAIT_RETRY; count++) {
if (!i2c_is_busy(i2c_adap))
break;
udelay(I2C_WAIT_DELAY);
}
if (I2C_WAIT_RETRY == count)
return 0;
return 1;
}
/* FIXME: Implement join handling correctly */
static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg, int joined_rlen)
{
int i, strobe = 0;
struct au0828_dev *dev = i2c_adap->algo_data;
u8 i2c_speed = dev->board.i2c_clk_divider;
dprintk(4, "%s()\n", __func__);
au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
if (((dev->board.tuner_type == TUNER_XC5000) ||
(dev->board.tuner_type == TUNER_XC5000C)) &&
(dev->board.tuner_addr == msg->addr)) {
/*
* Due to I2C clock stretch, we need to use a lower speed
* on xc5000 for commands. However, firmware transfer can
* speed up to 400 KHz.
*/
if (msg->len == 64)
i2c_speed = AU0828_I2C_CLK_250KHZ;
else
i2c_speed = AU0828_I2C_CLK_20KHZ;
}
/* Set the I2C clock */
au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, i2c_speed);
/* Hardware needs 8 bit addresses */
au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1);
dprintk(4, "SEND: %02x\n", msg->addr);
/* Deal with i2c_scan */
if (msg->len == 0) {
/* The analog tuner detection code makes use of the SMBUS_QUICK
message (which involves a zero length i2c write). To avoid
checking the status register when we didn't strobe out any
actual bytes to the bus, just do a read check. This is
consistent with how I saw i2c device checking done in the
USB trace of the Windows driver */
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_READ);
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (i2c_wait_read_ack(i2c_adap))
return -EIO;
return 0;
}
for (i = 0; i < msg->len;) {
dprintk(4, " %02x\n", msg->buf[i]);
au0828_write(dev, AU0828_I2C_WRITE_FIFO_205, msg->buf[i]);
strobe++;
i++;
if ((strobe >= 4) || (i >= msg->len)) {
/* Strobe the byte into the bus */
if (i < msg->len)
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_WRITE |
AU0828_I2C_TRIGGER_HOLD);
else
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_WRITE);
/* Reset strobe trigger */
strobe = 0;
if (!i2c_wait_write_done(i2c_adap))
return -EIO;
}
}
if (!i2c_wait_done(i2c_adap))
return -EIO;
dprintk(4, "\n");
return msg->len;
}
/* FIXME: Implement join handling correctly */
static int i2c_readbytes(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg, int joined)
{
struct au0828_dev *dev = i2c_adap->algo_data;
u8 i2c_speed = dev->board.i2c_clk_divider;
int i;
dprintk(4, "%s()\n", __func__);
au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
/*
* Due to xc5000c clock stretch, we cannot use full speed at
* readings from xc5000, as otherwise they'll fail.
*/
if (((dev->board.tuner_type == TUNER_XC5000) ||
(dev->board.tuner_type == TUNER_XC5000C)) &&
(dev->board.tuner_addr == msg->addr))
i2c_speed = AU0828_I2C_CLK_20KHZ;
/* Set the I2C clock */
au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, i2c_speed);
/* Hardware needs 8 bit addresses */
au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1);
dprintk(4, " RECV:\n");
/* Deal with i2c_scan */
if (msg->len == 0) {
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_READ);
if (i2c_wait_read_ack(i2c_adap))
return -EIO;
return 0;
}
for (i = 0; i < msg->len;) {
i++;
if (i < msg->len)
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_READ |
AU0828_I2C_TRIGGER_HOLD);
else
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_READ);
if (!i2c_wait_read_done(i2c_adap))
return -EIO;
msg->buf[i-1] = au0828_read(dev, AU0828_I2C_READ_FIFO_209) &
0xff;
dprintk(4, " %02x\n", msg->buf[i-1]);
}
if (!i2c_wait_done(i2c_adap))
return -EIO;
dprintk(4, "\n");
return msg->len;
}
static int i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *msgs, int num)
{
int i, retval = 0;
dprintk(4, "%s(num = %d)\n", __func__, num);
for (i = 0; i < num; i++) {
dprintk(4, "%s(num = %d) addr = 0x%02x len = 0x%x\n",
__func__, num, msgs[i].addr, msgs[i].len);
if (msgs[i].flags & I2C_M_RD) {
/* read */
retval = i2c_readbytes(i2c_adap, &msgs[i], 0);
} else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
msgs[i].addr == msgs[i + 1].addr) {
/* write then read from same address */
retval = i2c_sendbytes(i2c_adap, &msgs[i],
msgs[i + 1].len);
if (retval < 0)
goto err;
i++;
retval = i2c_readbytes(i2c_adap, &msgs[i], 1);
} else {
/* write */
retval = i2c_sendbytes(i2c_adap, &msgs[i], 0);
}
if (retval < 0)
goto err;
}
return num;
err:
return retval;
}
static u32 au0828_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
}
static struct i2c_algorithm au0828_i2c_algo_template = {
.master_xfer = i2c_xfer,
.functionality = au0828_functionality,
};
/* ----------------------------------------------------------------------- */
static struct i2c_adapter au0828_i2c_adap_template = {
.name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.algo = &au0828_i2c_algo_template,
};
static struct i2c_client au0828_i2c_client_template = {
.name = "au0828 internal",
};
static char *i2c_devs[128] = {
[0x8e >> 1] = "au8522",
[0xa0 >> 1] = "eeprom",
[0xc2 >> 1] = "tuner/xc5000",
};
static void do_i2c_scan(char *name, struct i2c_client *c)
{
unsigned char buf;
int i, rc;
for (i = 0; i < 128; i++) {
c->addr = i;
rc = i2c_master_recv(c, &buf, 0);
if (rc < 0)
continue;
pr_info("%s: i2c scan: found device @ 0x%x [%s]\n",
name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
}
}
/* init + register i2c adapter */
int au0828_i2c_register(struct au0828_dev *dev)
{
dprintk(1, "%s()\n", __func__);
dev->i2c_adap = au0828_i2c_adap_template;
dev->i2c_algo = au0828_i2c_algo_template;
dev->i2c_client = au0828_i2c_client_template;
dev->i2c_adap.dev.parent = &dev->usbdev->dev;
strlcpy(dev->i2c_adap.name, KBUILD_MODNAME,
sizeof(dev->i2c_adap.name));
dev->i2c_adap.algo = &dev->i2c_algo;
dev->i2c_adap.algo_data = dev;
#ifdef CONFIG_VIDEO_AU0828_V4L2
i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
#else
i2c_set_adapdata(&dev->i2c_adap, dev);
#endif
i2c_add_adapter(&dev->i2c_adap);
dev->i2c_client.adapter = &dev->i2c_adap;
if (0 == dev->i2c_rc) {
pr_info("i2c bus registered\n");
if (i2c_scan)
do_i2c_scan(KBUILD_MODNAME, &dev->i2c_client);
} else
pr_info("i2c bus register FAILED\n");
return dev->i2c_rc;
}
int au0828_i2c_unregister(struct au0828_dev *dev)
{
i2c_del_adapter(&dev->i2c_adap);
return 0;
}

View file

@ -0,0 +1,406 @@
/*
handle au0828 IR remotes via linux kernel input layer.
Copyright (C) 2014 Mauro Carvalho Chehab <mchehab@samsung.com>
Copyright (c) 2014 Samsung Electronics Co., Ltd.
Based on em28xx-input.c.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "au0828.h"
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/usb.h>
#include <linux/slab.h>
#include <media/rc-core.h>
static int disable_ir;
module_param(disable_ir, int, 0444);
MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
struct au0828_rc {
struct au0828_dev *dev;
struct rc_dev *rc;
char name[32];
char phys[32];
/* poll decoder */
int polling;
struct delayed_work work;
/* i2c slave address of external device (if used) */
u16 i2c_dev_addr;
int (*get_key_i2c)(struct au0828_rc *ir);
};
/*
* AU8522 has a builtin IR receiver. Add functions to get IR from it
*/
static int au8522_rc_write(struct au0828_rc *ir, u16 reg, u8 data)
{
int rc;
char buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
struct i2c_msg msg = { .addr = ir->i2c_dev_addr, .flags = 0,
.buf = buf, .len = sizeof(buf) };
rc = i2c_transfer(ir->dev->i2c_client.adapter, &msg, 1);
if (rc < 0)
return rc;
return (rc == 1) ? 0 : -EIO;
}
static int au8522_rc_read(struct au0828_rc *ir, u16 reg, int val,
char *buf, int size)
{
int rc;
char obuf[3];
struct i2c_msg msg[2] = { { .addr = ir->i2c_dev_addr, .flags = 0,
.buf = obuf, .len = 2 },
{ .addr = ir->i2c_dev_addr, .flags = I2C_M_RD,
.buf = buf, .len = size } };
obuf[0] = 0x40 | reg >> 8;
obuf[1] = reg & 0xff;
if (val >= 0) {
obuf[2] = val;
msg[0].len++;
}
rc = i2c_transfer(ir->dev->i2c_client.adapter, msg, 2);
if (rc < 0)
return rc;
return (rc == 2) ? 0 : -EIO;
}
static int au8522_rc_andor(struct au0828_rc *ir, u16 reg, u8 mask, u8 value)
{
int rc;
char buf, oldbuf;
rc = au8522_rc_read(ir, reg, -1, &buf, 1);
if (rc < 0)
return rc;
oldbuf = buf;
buf = (buf & ~mask) | (value & mask);
/* Nothing to do, just return */
if (buf == oldbuf)
return 0;
return au8522_rc_write(ir, reg, buf);
}
#define au8522_rc_set(ir, reg, bit) au8522_rc_andor(ir, (reg), (bit), (bit))
#define au8522_rc_clear(ir, reg, bit) au8522_rc_andor(ir, (reg), (bit), 0)
/* Remote Controller time units */
#define AU8522_UNIT 200000 /* ns */
#define NEC_START_SPACE (4500000 / AU8522_UNIT)
#define NEC_START_PULSE (562500 * 16)
#define RC5_START_SPACE (4 * AU8522_UNIT)
#define RC5_START_PULSE 888888
static int au0828_get_key_au8522(struct au0828_rc *ir)
{
unsigned char buf[40];
DEFINE_IR_RAW_EVENT(rawir);
int i, j, rc;
int prv_bit, bit, width;
bool first = true;
/* Check IR int */
rc = au8522_rc_read(ir, 0xe1, -1, buf, 1);
if (rc < 0 || !(buf[0] & (1 << 4))) {
/* Be sure that IR is enabled */
au8522_rc_set(ir, 0xe0, 1 << 4);
return 0;
}
/* Something arrived. Get the data */
rc = au8522_rc_read(ir, 0xe3, 0x11, buf, sizeof(buf));
if (rc < 0)
return rc;
/* Disable IR */
au8522_rc_clear(ir, 0xe0, 1 << 4);
/* Enable IR */
au8522_rc_set(ir, 0xe0, 1 << 4);
dprintk(16, "RC data received: %*ph\n", 40, buf);
prv_bit = (buf[0] >> 7) & 0x01;
width = 0;
for (i = 0; i < sizeof(buf); i++) {
for (j = 7; j >= 0; j--) {
bit = (buf[i] >> j) & 0x01;
if (bit == prv_bit) {
width++;
continue;
}
/*
* Fix an au8522 bug: the first pulse event
* is lost. So, we need to fake it, based on the
* protocol. That means that not all raw decoders
* will work, as we need to add a hack for each
* protocol, based on the first space.
* So, we only support RC5 and NEC.
*/
if (first) {
first = false;
init_ir_raw_event(&rawir);
rawir.pulse = true;
if (width > NEC_START_SPACE - 2 &&
width < NEC_START_SPACE + 2) {
/* NEC protocol */
rawir.duration = NEC_START_PULSE;
dprintk(16, "Storing NEC start %s with duration %d",
rawir.pulse ? "pulse" : "space",
rawir.duration);
} else {
/* RC5 protocol */
rawir.duration = RC5_START_PULSE;
dprintk(16, "Storing RC5 start %s with duration %d",
rawir.pulse ? "pulse" : "space",
rawir.duration);
}
ir_raw_event_store(ir->rc, &rawir);
}
init_ir_raw_event(&rawir);
rawir.pulse = prv_bit ? false : true;
rawir.duration = AU8522_UNIT * width;
dprintk(16, "Storing %s with duration %d",
rawir.pulse ? "pulse" : "space",
rawir.duration);
ir_raw_event_store(ir->rc, &rawir);
width = 1;
prv_bit = bit;
}
}
init_ir_raw_event(&rawir);
rawir.pulse = prv_bit ? false : true;
rawir.duration = AU8522_UNIT * width;
dprintk(16, "Storing end %s with duration %d",
rawir.pulse ? "pulse" : "space",
rawir.duration);
ir_raw_event_store(ir->rc, &rawir);
ir_raw_event_handle(ir->rc);
return 1;
}
/*
* Generic IR code
*/
static void au0828_rc_work(struct work_struct *work)
{
struct au0828_rc *ir = container_of(work, struct au0828_rc, work.work);
int rc;
rc = ir->get_key_i2c(ir);
if (rc < 0)
pr_info("Error while getting RC scancode\n");
schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
}
static int au0828_rc_start(struct rc_dev *rc)
{
struct au0828_rc *ir = rc->priv;
INIT_DELAYED_WORK(&ir->work, au0828_rc_work);
/* Enable IR */
au8522_rc_set(ir, 0xe0, 1 << 4);
schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
return 0;
}
static void au0828_rc_stop(struct rc_dev *rc)
{
struct au0828_rc *ir = rc->priv;
cancel_delayed_work_sync(&ir->work);
/* Disable IR */
au8522_rc_clear(ir, 0xe0, 1 << 4);
}
static int au0828_probe_i2c_ir(struct au0828_dev *dev)
{
int i = 0;
const unsigned short addr_list[] = {
0x47, I2C_CLIENT_END
};
while (addr_list[i] != I2C_CLIENT_END) {
if (i2c_probe_func_quick_read(dev->i2c_client.adapter,
addr_list[i]) == 1)
return addr_list[i];
i++;
}
return -ENODEV;
}
int au0828_rc_register(struct au0828_dev *dev)
{
struct au0828_rc *ir;
struct rc_dev *rc;
int err = -ENOMEM;
u16 i2c_rc_dev_addr = 0;
if (!dev->board.has_ir_i2c || disable_ir)
return 0;
i2c_rc_dev_addr = au0828_probe_i2c_ir(dev);
if (!i2c_rc_dev_addr)
return -ENODEV;
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
rc = rc_allocate_device();
if (!ir || !rc)
goto error;
/* record handles to ourself */
ir->dev = dev;
dev->ir = ir;
ir->rc = rc;
rc->priv = ir;
rc->open = au0828_rc_start;
rc->close = au0828_rc_stop;
if (dev->board.has_ir_i2c) { /* external i2c device */
switch (dev->boardnr) {
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
rc->map_name = RC_MAP_HAUPPAUGE;
ir->get_key_i2c = au0828_get_key_au8522;
break;
default:
err = -ENODEV;
goto error;
}
ir->i2c_dev_addr = i2c_rc_dev_addr;
}
/* This is how often we ask the chip for IR information */
ir->polling = 100; /* ms */
/* init input device */
snprintf(ir->name, sizeof(ir->name), "au0828 IR (%s)",
dev->board.name);
usb_make_path(dev->usbdev, ir->phys, sizeof(ir->phys));
strlcat(ir->phys, "/input0", sizeof(ir->phys));
rc->input_name = ir->name;
rc->input_phys = ir->phys;
rc->input_id.bustype = BUS_USB;
rc->input_id.version = 1;
rc->input_id.vendor = le16_to_cpu(dev->usbdev->descriptor.idVendor);
rc->input_id.product = le16_to_cpu(dev->usbdev->descriptor.idProduct);
rc->dev.parent = &dev->usbdev->dev;
rc->driver_name = "au0828-input";
rc->driver_type = RC_DRIVER_IR_RAW;
rc->allowed_protocols = RC_BIT_NEC | RC_BIT_RC5;
/* all done */
err = rc_register_device(rc);
if (err)
goto error;
pr_info("Remote controller %s initalized\n", ir->name);
return 0;
error:
dev->ir = NULL;
rc_free_device(rc);
kfree(ir);
return err;
}
void au0828_rc_unregister(struct au0828_dev *dev)
{
struct au0828_rc *ir = dev->ir;
/* skip detach on non attached boards */
if (!ir)
return;
if (ir->rc)
rc_unregister_device(ir->rc);
/* done */
kfree(ir);
dev->ir = NULL;
}
int au0828_rc_suspend(struct au0828_dev *dev)
{
struct au0828_rc *ir = dev->ir;
if (!ir)
return 0;
pr_info("Stopping RC\n");
cancel_delayed_work_sync(&ir->work);
/* Disable IR */
au8522_rc_clear(ir, 0xe0, 1 << 4);
return 0;
}
int au0828_rc_resume(struct au0828_dev *dev)
{
struct au0828_rc *ir = dev->ir;
if (!ir)
return 0;
pr_info("Restarting RC\n");
/* Enable IR */
au8522_rc_set(ir, 0xe0, 1 << 4);
schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
return 0;
}

View file

@ -0,0 +1,66 @@
/*
* Driver for the Auvitek USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* We'll start to rename these registers once we have a better
* understanding of their meaning.
*/
#define REG_000 0x000
#define REG_001 0x001
#define REG_002 0x002
#define REG_003 0x003
#define AU0828_SENSORCTRL_100 0x100
#define AU0828_SENSORCTRL_VBI_103 0x103
/* I2C registers */
#define AU0828_I2C_TRIGGER_200 0x200
#define AU0828_I2C_STATUS_201 0x201
#define AU0828_I2C_CLK_DIVIDER_202 0x202
#define AU0828_I2C_DEST_ADDR_203 0x203
#define AU0828_I2C_WRITE_FIFO_205 0x205
#define AU0828_I2C_READ_FIFO_209 0x209
#define AU0828_I2C_MULTIBYTE_MODE_2FF 0x2ff
/* Audio registers */
#define AU0828_AUDIOCTRL_50C 0x50C
#define REG_600 0x600
/*********************************************************************/
/* Here are constants for values associated with the above registers */
/* I2C Trigger (Reg 0x200) */
#define AU0828_I2C_TRIGGER_WRITE 0x01
#define AU0828_I2C_TRIGGER_READ 0x20
#define AU0828_I2C_TRIGGER_HOLD 0x40
/* I2C Status (Reg 0x201) */
#define AU0828_I2C_STATUS_READ_DONE 0x01
#define AU0828_I2C_STATUS_NO_READ_ACK 0x02
#define AU0828_I2C_STATUS_WRITE_DONE 0x04
#define AU0828_I2C_STATUS_NO_WRITE_ACK 0x08
#define AU0828_I2C_STATUS_BUSY 0x10
/* I2C Clock Divider (Reg 0x202) */
#define AU0828_I2C_CLK_250KHZ 0x07
#define AU0828_I2C_CLK_100KHZ 0x14
#define AU0828_I2C_CLK_30KHZ 0x40
#define AU0828_I2C_CLK_20KHZ 0x60

View file

@ -0,0 +1,138 @@
/*
au0828-vbi.c - VBI driver for au0828
Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
This work was sponsored by GetWellNetwork Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "au0828.h"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
static unsigned int vbibufs = 5;
module_param(vbibufs, int, 0644);
MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
/* ------------------------------------------------------------------ */
static void
free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf)
{
struct au0828_fh *fh = vq->priv_data;
struct au0828_dev *dev = fh->dev;
unsigned long flags = 0;
if (in_interrupt())
BUG();
/* We used to wait for the buffer to finish here, but this didn't work
because, as we were keeping the state as VIDEOBUF_QUEUED,
videobuf_queue_cancel marked it as finished for us.
(Also, it could wedge forever if the hardware was misconfigured.)
This should be safe; by the time we get here, the buffer isn't
queued anymore. If we ever start marking the buffers as
VIDEOBUF_ACTIVE, it won't be, though.
*/
spin_lock_irqsave(&dev->slock, flags);
if (dev->isoc_ctl.vbi_buf == buf)
dev->isoc_ctl.vbi_buf = NULL;
spin_unlock_irqrestore(&dev->slock, flags);
videobuf_vmalloc_free(&buf->vb);
buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
static int
vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
{
struct au0828_fh *fh = q->priv_data;
struct au0828_dev *dev = fh->dev;
*size = dev->vbi_width * dev->vbi_height * 2;
if (0 == *count)
*count = vbibufs;
if (*count < 2)
*count = 2;
if (*count > 32)
*count = 32;
return 0;
}
static int
vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
enum v4l2_field field)
{
struct au0828_fh *fh = q->priv_data;
struct au0828_dev *dev = fh->dev;
struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
int rc = 0;
buf->vb.size = dev->vbi_width * dev->vbi_height * 2;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
buf->vb.width = dev->vbi_width;
buf->vb.height = dev->vbi_height;
buf->vb.field = field;
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
rc = videobuf_iolock(q, &buf->vb, NULL);
if (rc < 0)
goto fail;
}
buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
free_buffer(q, buf);
return rc;
}
static void
vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct au0828_buffer *buf = container_of(vb,
struct au0828_buffer,
vb);
struct au0828_fh *fh = vq->priv_data;
struct au0828_dev *dev = fh->dev;
struct au0828_dmaqueue *vbiq = &dev->vbiq;
buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue, &vbiq->active);
}
static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
free_buffer(q, buf);
}
struct videobuf_queue_ops au0828_vbi_qops = {
.buf_setup = vbi_setup,
.buf_prepare = vbi_prepare,
.buf_queue = vbi_queue,
.buf_release = vbi_release,
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,350 @@
/*
* Driver for the Auvitek AU0828 USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/usb.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <media/tveeprom.h>
/* Analog */
#include <linux/videodev2.h>
#include <media/videobuf-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
/* DVB */
#include "demux.h"
#include "dmxdev.h"
#include "dvb_demux.h"
#include "dvb_frontend.h"
#include "dvb_net.h"
#include "dvbdev.h"
#include "au0828-reg.h"
#include "au0828-cards.h"
#define URB_COUNT 16
#define URB_BUFSIZE (0xe522)
/* Analog constants */
#define NTSC_STD_W 720
#define NTSC_STD_H 480
#define AU0828_INTERLACED_DEFAULT 1
#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0)
/* Defination for AU0828 USB transfer */
#define AU0828_MAX_ISO_BUFS 12 /* maybe resize this value in the future */
#define AU0828_ISO_PACKETS_PER_URB 128
#define AU0828_MIN_BUF 4
#define AU0828_DEF_BUF 8
#define AU0828_MAX_INPUT 4
/* au0828 resource types (used for res_get/res_lock etc */
#define AU0828_RESOURCE_VIDEO 0x01
#define AU0828_RESOURCE_VBI 0x02
enum au0828_itype {
AU0828_VMUX_UNDEFINED = 0,
AU0828_VMUX_COMPOSITE,
AU0828_VMUX_SVIDEO,
AU0828_VMUX_CABLE,
AU0828_VMUX_TELEVISION,
AU0828_VMUX_DVB,
AU0828_VMUX_DEBUG
};
struct au0828_input {
enum au0828_itype type;
unsigned int vmux;
unsigned int amux;
void (*audio_setup) (void *priv, int enable);
};
struct au0828_board {
char *name;
unsigned int tuner_type;
unsigned char tuner_addr;
unsigned char i2c_clk_divider;
unsigned char has_ir_i2c:1;
unsigned char has_analog:1;
struct au0828_input input[AU0828_MAX_INPUT];
};
struct au0828_dvb {
struct mutex lock;
struct dvb_adapter adapter;
struct dvb_frontend *frontend;
struct dvb_demux demux;
struct dmxdev dmxdev;
struct dmx_frontend fe_hw;
struct dmx_frontend fe_mem;
struct dvb_net net;
int feeding;
int start_count;
int stop_count;
int (*set_frontend)(struct dvb_frontend *fe);
};
enum au0828_stream_state {
STREAM_OFF,
STREAM_INTERRUPT,
STREAM_ON
};
#define AUVI_INPUT(nr) (dev->board.input[nr])
/* device state */
enum au0828_dev_state {
DEV_INITIALIZED = 0x01,
DEV_DISCONNECTED = 0x02,
DEV_MISCONFIGURED = 0x04
};
struct au0828_fh {
/* must be the first field of this struct! */
struct v4l2_fh fh;
struct au0828_dev *dev;
unsigned int resources;
struct videobuf_queue vb_vidq;
struct videobuf_queue vb_vbiq;
enum v4l2_buf_type type;
};
struct au0828_usb_isoc_ctl {
/* max packet size of isoc transaction */
int max_pkt_size;
/* number of allocated urbs */
int num_bufs;
/* urb for isoc transfers */
struct urb **urb;
/* transfer buffers for isoc transfer */
char **transfer_buffer;
/* Last buffer command and region */
u8 cmd;
int pos, size, pktsize;
/* Last field: ODD or EVEN? */
int field;
/* Stores incomplete commands */
u32 tmp_buf;
int tmp_buf_len;
/* Stores already requested buffers */
struct au0828_buffer *buf;
struct au0828_buffer *vbi_buf;
/* Stores the number of received fields */
int nfields;
/* isoc urb callback */
int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb);
};
/* buffer for one video frame */
struct au0828_buffer {
/* common v4l buffer stuff -- must be first */
struct videobuf_buffer vb;
struct list_head frame;
int top_field;
int receiving;
};
struct au0828_dmaqueue {
struct list_head active;
struct list_head queued;
wait_queue_head_t wq;
/* Counters to control buffer fill */
int pos;
};
struct au0828_dev {
struct mutex mutex;
struct usb_device *usbdev;
int boardnr;
struct au0828_board board;
u8 ctrlmsg[64];
/* I2C */
struct i2c_adapter i2c_adap;
struct i2c_algorithm i2c_algo;
struct i2c_client i2c_client;
u32 i2c_rc;
/* Digital */
struct au0828_dvb dvb;
struct work_struct restart_streaming;
#ifdef CONFIG_VIDEO_AU0828_V4L2
/* Analog */
struct v4l2_device v4l2_dev;
struct v4l2_ctrl_handler v4l2_ctrl_hdl;
#endif
#ifdef CONFIG_VIDEO_AU0828_RC
struct au0828_rc *ir;
#endif
int users;
unsigned int resources; /* resources in use */
struct video_device *vdev;
struct video_device *vbi_dev;
struct timer_list vid_timeout;
int vid_timeout_running;
struct timer_list vbi_timeout;
int vbi_timeout_running;
int width;
int height;
int vbi_width;
int vbi_height;
u32 vbi_read;
v4l2_std_id std;
u32 field_size;
u32 frame_size;
u32 bytesperline;
int type;
u8 ctrl_ainput;
__u8 isoc_in_endpointaddr;
u8 isoc_init_ok;
int greenscreen_detected;
unsigned int frame_count;
int ctrl_freq;
int input_type;
int std_set_in_tuner_core;
unsigned int ctrl_input;
enum au0828_dev_state dev_state;
enum au0828_stream_state stream_state;
wait_queue_head_t open;
struct mutex lock;
/* Isoc control struct */
struct au0828_dmaqueue vidq;
struct au0828_dmaqueue vbiq;
struct au0828_usb_isoc_ctl isoc_ctl;
spinlock_t slock;
/* usb transfer */
int alt; /* alternate */
int max_pkt_size; /* max packet size of isoc transaction */
int num_alt; /* Number of alternative settings */
unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
struct urb *urb[AU0828_MAX_ISO_BUFS]; /* urb for isoc transfers */
char *transfer_buffer[AU0828_MAX_ISO_BUFS];/* transfer buffers for isoc
transfer */
/* DVB USB / URB Related */
bool urb_streaming, need_urb_start;
struct urb *urbs[URB_COUNT];
/* Preallocated transfer digital transfer buffers */
char *dig_transfer_buffer[URB_COUNT];
};
/* ----------------------------------------------------------- */
#define au0828_read(dev, reg) au0828_readreg(dev, reg)
#define au0828_write(dev, reg, value) au0828_writereg(dev, reg, value)
#define au0828_andor(dev, reg, mask, value) \
au0828_writereg(dev, reg, \
(au0828_readreg(dev, reg) & ~(mask)) | ((value) & (mask)))
#define au0828_set(dev, reg, bit) au0828_andor(dev, (reg), (bit), (bit))
#define au0828_clear(dev, reg, bit) au0828_andor(dev, (reg), (bit), 0)
/* ----------------------------------------------------------- */
/* au0828-core.c */
extern u32 au0828_read(struct au0828_dev *dev, u16 reg);
extern u32 au0828_write(struct au0828_dev *dev, u16 reg, u32 val);
extern int au0828_debug;
/* ----------------------------------------------------------- */
/* au0828-cards.c */
extern struct au0828_board au0828_boards[];
extern struct usb_device_id au0828_usb_id_table[];
extern void au0828_gpio_setup(struct au0828_dev *dev);
extern int au0828_tuner_callback(void *priv, int component,
int command, int arg);
extern void au0828_card_setup(struct au0828_dev *dev);
/* ----------------------------------------------------------- */
/* au0828-i2c.c */
extern int au0828_i2c_register(struct au0828_dev *dev);
extern int au0828_i2c_unregister(struct au0828_dev *dev);
/* ----------------------------------------------------------- */
/* au0828-video.c */
int au0828_analog_register(struct au0828_dev *dev,
struct usb_interface *interface);
int au0828_analog_stream_disable(struct au0828_dev *d);
void au0828_analog_unregister(struct au0828_dev *dev);
#ifdef CONFIG_VIDEO_AU0828_V4L2
void au0828_v4l2_suspend(struct au0828_dev *dev);
void au0828_v4l2_resume(struct au0828_dev *dev);
#else
static inline void au0828_v4l2_suspend(struct au0828_dev *dev) { };
static inline void au0828_v4l2_resume(struct au0828_dev *dev) { };
#endif
/* ----------------------------------------------------------- */
/* au0828-dvb.c */
extern int au0828_dvb_register(struct au0828_dev *dev);
extern void au0828_dvb_unregister(struct au0828_dev *dev);
void au0828_dvb_suspend(struct au0828_dev *dev);
void au0828_dvb_resume(struct au0828_dev *dev);
/* au0828-vbi.c */
extern struct videobuf_queue_ops au0828_vbi_qops;
#define dprintk(level, fmt, arg...)\
do { if (au0828_debug & level)\
printk(KERN_DEBUG pr_fmt(fmt), ## arg);\
} while (0)
/* au0828-input.c */
#ifdef CONFIG_VIDEO_AU0828_RC
extern int au0828_rc_register(struct au0828_dev *dev);
extern void au0828_rc_unregister(struct au0828_dev *dev);
extern int au0828_rc_suspend(struct au0828_dev *dev);
extern int au0828_rc_resume(struct au0828_dev *dev);
#else
static inline int au0828_rc_register(struct au0828_dev *dev) { return 0; }
static inline void au0828_rc_unregister(struct au0828_dev *dev) { }
static inline int au0828_rc_suspend(struct au0828_dev *dev) { return 0; }
static inline int au0828_rc_resume(struct au0828_dev *dev) { return 0; }
#endif

View file

@ -0,0 +1,15 @@
config DVB_B2C2_FLEXCOP_USB
tristate "Technisat/B2C2 Air/Sky/Cable2PC USB"
depends on DVB_CORE && I2C
help
Support for the Air/Sky/Cable2PC USB1.1 box (DVB/ATSC) by Technisat/B2C2,
Say Y if you own such a device and want to use it.
config DVB_B2C2_FLEXCOP_USB_DEBUG
bool "Enable debug for the B2C2 FlexCop drivers"
depends on DVB_B2C2_FLEXCOP_USB
select DVB_B2C2_FLEXCOP_DEBUG
help
Say Y if you want to enable the module option to control debug messages
of all B2C2 FlexCop drivers.

View file

@ -0,0 +1,5 @@
b2c2-flexcop-usb-objs := flexcop-usb.o
obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o
ccflags-y += -Idrivers/media/dvb-core/
ccflags-y += -Idrivers/media/common/b2c2/

View file

@ -0,0 +1,587 @@
/*
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop-usb.c - covers the USB part
* see flexcop.c for copyright information
*/
#define FC_LOG_PREFIX "flexcop_usb"
#include "flexcop-usb.h"
#include "flexcop-common.h"
/* Version information */
#define DRIVER_VERSION "0.1"
#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver"
#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
/* debug */
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define dprintk(level,args...) \
do { if ((debug & level)) printk(args); } while (0)
#define debug_dump(b, l, method) do {\
int i; \
for (i = 0; i < l; i++) \
method("%02x ", b[i]); \
method("\n"); \
} while (0)
#define DEBSTATUS ""
#else
#define dprintk(level, args...)
#define debug_dump(b, l, method)
#define DEBSTATUS " (debugging is not enabled)"
#endif
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,"
"ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);
#undef DEBSTATUS
#define deb_info(args...) dprintk(0x01, args)
#define deb_ts(args...) dprintk(0x02, args)
#define deb_ctrl(args...) dprintk(0x04, args)
#define deb_i2c(args...) dprintk(0x08, args)
#define deb_v8(args...) dprintk(0x10, args)
/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
* in the IBI address, to make the V8 code simpler.
* PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used)
* in general: 0000 0HHH 000L LL00
* IBI ADDRESS FORMAT: RHHH BLLL
*
* where R is the read(1)/write(0) bit, B is the busy bit
* and HHH and LLL are the two sets of three bits from the PCI address.
*/
#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \
(((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \
(((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
/*
* DKT 020228
* - forget about this VENDOR_BUFFER_SIZE, read and write register
* deal with DWORD or 4 bytes, that should be should from now on
* - from now on, we don't support anything older than firm 1.00
* I eliminated the write register as a 2 trip of writing hi word and lo word
* and force this to write only 4 bytes at a time.
* NOTE: this should work with all the firmware from 1.00 and newer
*/
static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read)
{
struct flexcop_usb *fc_usb = fc->bus_specific;
u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG;
u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;
u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) |
(read ? 0x80 : 0);
int len = usb_control_msg(fc_usb->udev,
read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,
request,
request_type, /* 0xc0 read or 0x40 write */
wAddress,
0,
val,
sizeof(u32),
B2C2_WAIT_FOR_OPERATION_RDW * HZ);
if (len != sizeof(u32)) {
err("error while %s dword from %d (%d).", read ? "reading" :
"writing", wAddress, wRegOffsPCI);
return -EIO;
}
return 0;
}
/*
* DKT 010817 - add support for V8 memory read/write and flash update
*/
static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,
flexcop_usb_request_t req, u8 page, u16 wAddress,
u8 *pbBuffer, u32 buflen)
{
u8 request_type = USB_TYPE_VENDOR;
u16 wIndex;
int nWaitTime, pipe, len;
wIndex = page << 8;
switch (req) {
case B2C2_USB_READ_V8_MEM:
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
request_type |= USB_DIR_IN;
pipe = B2C2_USB_CTRL_PIPE_IN;
break;
case B2C2_USB_WRITE_V8_MEM:
wIndex |= pbBuffer[0];
request_type |= USB_DIR_OUT;
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
pipe = B2C2_USB_CTRL_PIPE_OUT;
break;
case B2C2_USB_FLASH_BLOCK:
request_type |= USB_DIR_OUT;
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
pipe = B2C2_USB_CTRL_PIPE_OUT;
break;
default:
deb_info("unsupported request for v8_mem_req %x.\n", req);
return -EINVAL;
}
deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req,
wAddress, wIndex, buflen);
len = usb_control_msg(fc_usb->udev, pipe,
req,
request_type,
wAddress,
wIndex,
pbBuffer,
buflen,
nWaitTime * HZ);
debug_dump(pbBuffer, len, deb_v8);
return len == buflen ? 0 : -EIO;
}
#define bytes_left_to_read_on_page(paddr,buflen) \
((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \
? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))
static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,
flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start,
u32 addr, int extended, u8 *buf, u32 len)
{
int i,ret = 0;
u16 wMax;
u32 pagechunk = 0;
switch(req) {
case B2C2_USB_READ_V8_MEM:
wMax = USB_MEM_READ_MAX;
break;
case B2C2_USB_WRITE_V8_MEM:
wMax = USB_MEM_WRITE_MAX;
break;
case B2C2_USB_FLASH_BLOCK:
wMax = USB_FLASH_MAX;
break;
default:
return -EINVAL;
break;
}
for (i = 0; i < len;) {
pagechunk =
wMax < bytes_left_to_read_on_page(addr, len) ?
wMax :
bytes_left_to_read_on_page(addr, len);
deb_info("%x\n",
(addr & V8_MEMORY_PAGE_MASK) |
(V8_MEMORY_EXTENDED*extended));
ret = flexcop_usb_v8_memory_req(fc_usb, req,
page_start + (addr / V8_MEMORY_PAGE_SIZE),
(addr & V8_MEMORY_PAGE_MASK) |
(V8_MEMORY_EXTENDED*extended),
&buf[i], pagechunk);
if (ret < 0)
return ret;
addr += pagechunk;
len -= pagechunk;
}
return 0;
}
static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended)
{
return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM,
V8_MEMORY_PAGE_FLASH, 0x1f010, 1,
fc->dvb_adapter.proposed_mac, 6);
}
#if 0
static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set,
flexcop_usb_utility_function_t func, u8 extra, u16 wIndex,
u16 buflen, u8 *pvBuffer)
{
u16 wValue;
u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR;
int nWaitTime = 2,
pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN, len;
wValue = (func << 8) | extra;
len = usb_control_msg(fc_usb->udev,pipe,
B2C2_USB_UTILITY,
request_type,
wValue,
wIndex,
pvBuffer,
buflen,
nWaitTime * HZ);
return len == buflen ? 0 : -EIO;
}
#endif
/* usb i2c stuff */
static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c,
flexcop_usb_request_t req, flexcop_usb_i2c_function_t func,
u8 chipaddr, u8 addr, u8 *buf, u8 buflen)
{
struct flexcop_usb *fc_usb = i2c->fc->bus_specific;
u16 wValue, wIndex;
int nWaitTime,pipe,len;
u8 request_type = USB_TYPE_VENDOR;
switch (func) {
case USB_FUNC_I2C_WRITE:
case USB_FUNC_I2C_MULTIWRITE:
case USB_FUNC_I2C_REPEATWRITE:
/* DKT 020208 - add this to support special case of DiSEqC */
case USB_FUNC_I2C_CHECKWRITE:
pipe = B2C2_USB_CTRL_PIPE_OUT;
nWaitTime = 2;
request_type |= USB_DIR_OUT;
break;
case USB_FUNC_I2C_READ:
case USB_FUNC_I2C_REPEATREAD:
pipe = B2C2_USB_CTRL_PIPE_IN;
nWaitTime = 2;
request_type |= USB_DIR_IN;
break;
default:
deb_info("unsupported function for i2c_req %x\n", func);
return -EINVAL;
}
wValue = (func << 8) | (i2c->port << 4);
wIndex = (chipaddr << 8 ) | addr;
deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",
func, request_type, req,
wValue & 0xff, wValue >> 8,
wIndex & 0xff, wIndex >> 8);
len = usb_control_msg(fc_usb->udev,pipe,
req,
request_type,
wValue,
wIndex,
buf,
buflen,
nWaitTime * HZ);
return len == buflen ? 0 : -EREMOTEIO;
}
/* actual bus specific access functions,
make sure prototype are/will be equal to pci */
static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc,
flexcop_ibi_register reg)
{
flexcop_ibi_value val;
val.raw = 0;
flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1);
return val;
}
static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc,
flexcop_ibi_register reg, flexcop_ibi_value val)
{
return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0);
}
static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c,
flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
{
if (op == FC_READ)
return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
USB_FUNC_I2C_READ, chipaddr, addr, buf, len);
else
return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len);
}
static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb,
u8 *buffer, int buffer_length)
{
u8 *b;
int l;
deb_ts("tmp_buffer_length=%d, buffer_length=%d\n",
fc_usb->tmp_buffer_length, buffer_length);
if (fc_usb->tmp_buffer_length > 0) {
memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer,
buffer_length);
fc_usb->tmp_buffer_length += buffer_length;
b = fc_usb->tmp_buffer;
l = fc_usb->tmp_buffer_length;
} else {
b=buffer;
l=buffer_length;
}
while (l >= 190) {
if (*b == 0xff) {
switch (*(b+1) & 0x03) {
case 0x01: /* media packet */
if (*(b+2) == 0x47)
flexcop_pass_dmx_packets(
fc_usb->fc_dev, b+2, 1);
else
deb_ts("not ts packet %*ph\n", 4, b+2);
b += 190;
l -= 190;
break;
default:
deb_ts("wrong packet type\n");
l = 0;
break;
}
} else {
deb_ts("wrong header\n");
l = 0;
}
}
if (l>0)
memcpy(fc_usb->tmp_buffer, b, l);
fc_usb->tmp_buffer_length = l;
}
static void flexcop_usb_urb_complete(struct urb *urb)
{
struct flexcop_usb *fc_usb = urb->context;
int i;
if (urb->actual_length > 0)
deb_ts("urb completed, bufsize: %d actlen; %d\n",
urb->transfer_buffer_length, urb->actual_length);
for (i = 0; i < urb->number_of_packets; i++) {
if (urb->iso_frame_desc[i].status < 0) {
err("iso frame descriptor %d has an error: %d\n", i,
urb->iso_frame_desc[i].status);
} else
if (urb->iso_frame_desc[i].actual_length > 0) {
deb_ts("passed %d bytes to the demux\n",
urb->iso_frame_desc[i].actual_length);
flexcop_usb_process_frame(fc_usb,
urb->transfer_buffer +
urb->iso_frame_desc[i].offset,
urb->iso_frame_desc[i].actual_length);
}
urb->iso_frame_desc[i].status = 0;
urb->iso_frame_desc[i].actual_length = 0;
}
usb_submit_urb(urb,GFP_ATOMIC);
}
static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff)
{
/* submit/kill iso packets */
return 0;
}
static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
{
int i;
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
if (fc_usb->iso_urb[i] != NULL) {
deb_ts("unlinking/killing urb no. %d\n",i);
usb_kill_urb(fc_usb->iso_urb[i]);
usb_free_urb(fc_usb->iso_urb[i]);
}
if (fc_usb->iso_buffer != NULL)
usb_free_coherent(fc_usb->udev,
fc_usb->buffer_size, fc_usb->iso_buffer,
fc_usb->dma_addr);
}
static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
{
u16 frame_size = le16_to_cpu(
fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);
int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO *
frame_size, i, j, ret;
int buffer_offset = 0;
deb_ts("creating %d iso-urbs with %d frames "
"each of %d bytes size = %d.\n", B2C2_USB_NUM_ISO_URB,
B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);
fc_usb->iso_buffer = usb_alloc_coherent(fc_usb->udev,
bufsize, GFP_KERNEL, &fc_usb->dma_addr);
if (fc_usb->iso_buffer == NULL)
return -ENOMEM;
memset(fc_usb->iso_buffer, 0, bufsize);
fc_usb->buffer_size = bufsize;
/* creating iso urbs */
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,
GFP_ATOMIC);
if (fc_usb->iso_urb[i] == NULL) {
ret = -ENOMEM;
goto urb_error;
}
}
/* initialising and submitting iso urbs */
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
int frame_offset = 0;
struct urb *urb = fc_usb->iso_urb[i];
deb_ts("initializing and submitting urb no. %d "
"(buf_offset: %d).\n", i, buffer_offset);
urb->dev = fc_usb->udev;
urb->context = fc_usb;
urb->complete = flexcop_usb_urb_complete;
urb->pipe = B2C2_USB_DATA_PIPE;
urb->transfer_flags = URB_ISO_ASAP;
urb->interval = 1;
urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset;
buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",
i, j, frame_offset);
urb->iso_frame_desc[j].offset = frame_offset;
urb->iso_frame_desc[j].length = frame_size;
frame_offset += frame_size;
}
if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) {
err("submitting urb %d failed with %d.", i, ret);
goto urb_error;
}
deb_ts("submitted urb no. %d.\n",i);
}
/* SRAM */
flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA |
FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI,
FC_SRAM_DEST_TARGET_WAN_USB);
flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS);
flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1);
return 0;
urb_error:
flexcop_usb_transfer_exit(fc_usb);
return ret;
}
static int flexcop_usb_init(struct flexcop_usb *fc_usb)
{
/* use the alternate setting with the larges buffer */
usb_set_interface(fc_usb->udev,0,1);
switch (fc_usb->udev->speed) {
case USB_SPEED_LOW:
err("cannot handle USB speed because it is too slow.");
return -ENODEV;
break;
case USB_SPEED_FULL:
info("running at FULL speed.");
break;
case USB_SPEED_HIGH:
info("running at HIGH speed.");
break;
case USB_SPEED_UNKNOWN: /* fall through */
default:
err("cannot handle USB speed because it is unknown.");
return -ENODEV;
}
usb_set_intfdata(fc_usb->uintf, fc_usb);
return 0;
}
static void flexcop_usb_exit(struct flexcop_usb *fc_usb)
{
usb_set_intfdata(fc_usb->uintf, NULL);
}
static int flexcop_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct flexcop_usb *fc_usb = NULL;
struct flexcop_device *fc = NULL;
int ret;
if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {
err("out of memory\n");
return -ENOMEM;
}
/* general flexcop init */
fc_usb = fc->bus_specific;
fc_usb->fc_dev = fc;
fc->read_ibi_reg = flexcop_usb_read_ibi_reg;
fc->write_ibi_reg = flexcop_usb_write_ibi_reg;
fc->i2c_request = flexcop_usb_i2c_request;
fc->get_mac_addr = flexcop_usb_get_mac_addr;
fc->stream_control = flexcop_usb_stream_control;
fc->pid_filtering = 1;
fc->bus_type = FC_USB;
fc->dev = &udev->dev;
fc->owner = THIS_MODULE;
/* bus specific part */
fc_usb->udev = udev;
fc_usb->uintf = intf;
if ((ret = flexcop_usb_init(fc_usb)) != 0)
goto err_kfree;
/* init flexcop */
if ((ret = flexcop_device_initialize(fc)) != 0)
goto err_usb_exit;
/* xfer init */
if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)
goto err_fc_exit;
info("%s successfully initialized and connected.", DRIVER_NAME);
return 0;
err_fc_exit:
flexcop_device_exit(fc);
err_usb_exit:
flexcop_usb_exit(fc_usb);
err_kfree:
flexcop_device_kfree(fc);
return ret;
}
static void flexcop_usb_disconnect(struct usb_interface *intf)
{
struct flexcop_usb *fc_usb = usb_get_intfdata(intf);
flexcop_usb_transfer_exit(fc_usb);
flexcop_device_exit(fc_usb->fc_dev);
flexcop_usb_exit(fc_usb);
flexcop_device_kfree(fc_usb->fc_dev);
info("%s successfully deinitialized and disconnected.", DRIVER_NAME);
}
static struct usb_device_id flexcop_usb_table [] = {
{ USB_DEVICE(0x0af7, 0x0101) },
{ }
};
MODULE_DEVICE_TABLE (usb, flexcop_usb_table);
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver flexcop_usb_driver = {
.name = "b2c2_flexcop_usb",
.probe = flexcop_usb_probe,
.disconnect = flexcop_usb_disconnect,
.id_table = flexcop_usb_table,
};
module_usb_driver(flexcop_usb_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_NAME);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,111 @@
/*
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop-usb.h - header file for the USB part
* see flexcop.c for copyright information
*/
#ifndef __FLEXCOP_USB_H_INCLUDED__
#define __FLEXCOP_USB_H_INCLUDED__
#include <linux/usb.h>
/* transfer parameters */
#define B2C2_USB_FRAMES_PER_ISO 4
#define B2C2_USB_NUM_ISO_URB 4
#define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(fc_usb->udev, 0)
#define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(fc_usb->udev, 0)
#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(fc_usb->udev, 0x81)
struct flexcop_usb {
struct usb_device *udev;
struct usb_interface *uintf;
u8 *iso_buffer;
int buffer_size;
dma_addr_t dma_addr;
struct urb *iso_urb[B2C2_USB_NUM_ISO_URB];
struct flexcop_device *fc_dev;
u8 tmp_buffer[1023+190];
int tmp_buffer_length;
};
#if 0
/* request types TODO What is its use?*/
typedef enum {
} flexcop_usb_request_type_t;
#endif
/* request */
typedef enum {
B2C2_USB_WRITE_V8_MEM = 0x04,
B2C2_USB_READ_V8_MEM = 0x05,
B2C2_USB_READ_REG = 0x08,
B2C2_USB_WRITE_REG = 0x0A,
B2C2_USB_WRITEREGHI = 0x0B,
B2C2_USB_FLASH_BLOCK = 0x10,
B2C2_USB_I2C_REQUEST = 0x11,
B2C2_USB_UTILITY = 0x12,
} flexcop_usb_request_t;
/* function definition for I2C_REQUEST */
typedef enum {
USB_FUNC_I2C_WRITE = 0x01,
USB_FUNC_I2C_MULTIWRITE = 0x02,
USB_FUNC_I2C_READ = 0x03,
USB_FUNC_I2C_REPEATWRITE = 0x04,
USB_FUNC_GET_DESCRIPTOR = 0x05,
USB_FUNC_I2C_REPEATREAD = 0x06,
/* DKT 020208 - add this to support special case of DiSEqC */
USB_FUNC_I2C_CHECKWRITE = 0x07,
USB_FUNC_I2C_CHECKRESULT = 0x08,
} flexcop_usb_i2c_function_t;
/* function definition for UTILITY request 0x12
* DKT 020304 - new utility function */
typedef enum {
UTILITY_SET_FILTER = 0x01,
UTILITY_DATA_ENABLE = 0x02,
UTILITY_FLEX_MULTIWRITE = 0x03,
UTILITY_SET_BUFFER_SIZE = 0x04,
UTILITY_FLEX_OPERATOR = 0x05,
UTILITY_FLEX_RESET300_START = 0x06,
UTILITY_FLEX_RESET300_STOP = 0x07,
UTILITY_FLEX_RESET300 = 0x08,
UTILITY_SET_ISO_SIZE = 0x09,
UTILITY_DATA_RESET = 0x0A,
UTILITY_GET_DATA_STATUS = 0x10,
UTILITY_GET_V8_REG = 0x11,
/* DKT 020326 - add function for v1.14 */
UTILITY_SRAM_WRITE = 0x12,
UTILITY_SRAM_READ = 0x13,
UTILITY_SRAM_TESTFILL = 0x14,
UTILITY_SRAM_TESTSET = 0x15,
UTILITY_SRAM_TESTVERIFY = 0x16,
} flexcop_usb_utility_function_t;
#define B2C2_WAIT_FOR_OPERATION_RW (1*HZ)
#define B2C2_WAIT_FOR_OPERATION_RDW (3*HZ)
#define B2C2_WAIT_FOR_OPERATION_WDW (1*HZ)
#define B2C2_WAIT_FOR_OPERATION_V8READ (3*HZ)
#define B2C2_WAIT_FOR_OPERATION_V8WRITE (3*HZ)
#define B2C2_WAIT_FOR_OPERATION_V8FLASH (3*HZ)
typedef enum {
V8_MEMORY_PAGE_DVB_CI = 0x20,
V8_MEMORY_PAGE_DVB_DS = 0x40,
V8_MEMORY_PAGE_MULTI2 = 0x60,
V8_MEMORY_PAGE_FLASH = 0x80
} flexcop_usb_mem_page_t;
#define V8_MEMORY_EXTENDED (1 << 15)
#define USB_MEM_READ_MAX 32
#define USB_MEM_WRITE_MAX 1
#define USB_FLASH_MAX 8
#define V8_MEMORY_PAGE_SIZE 0x8000 /* 32K */
#define V8_MEMORY_PAGE_MASK 0x7FFF
#endif

View file

@ -0,0 +1,9 @@
config VIDEO_CPIA2
tristate "CPiA2 Video For Linux"
depends on VIDEO_DEV && USB && VIDEO_V4L2
---help---
This is the video4linux driver for cameras based on Vision's CPiA2
(Colour Processor Interface ASIC), such as the Digital Blue QX5
Microscope. If you have one of these cameras, say Y here
This driver is also available as a module (cpia2).

View file

@ -0,0 +1,3 @@
cpia2-objs := cpia2_v4l.o cpia2_usb.o cpia2_core.o
obj-$(CONFIG_VIDEO_CPIA2) += cpia2.o

View file

@ -0,0 +1,487 @@
/****************************************************************************
*
* Filename: cpia2.h
*
* Copyright 2001, STMicrolectronics, Inc.
*
* Contact: steve.miller@st.com
*
* Description:
* This is a USB driver for CPiA2 based video cameras.
*
* This driver is modelled on the cpia usb driver by
* Jochen Scharrlach and Johannes Erdfeldt.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
****************************************************************************/
#ifndef __CPIA2_H__
#define __CPIA2_H__
#include <linux/videodev2.h>
#include <linux/usb.h>
#include <linux/poll.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include "cpia2_registers.h"
/* define for verbose debug output */
//#define _CPIA2_DEBUG_
/***
* Image defines
***/
/* Misc constants */
#define ALLOW_CORRUPT 0 /* Causes collater to discard checksum */
/* USB Transfer mode */
#define XFER_ISOC 0
#define XFER_BULK 1
/* USB Alternates */
#define USBIF_CMDONLY 0
#define USBIF_BULK 1
#define USBIF_ISO_1 2 /* 128 bytes/ms */
#define USBIF_ISO_2 3 /* 384 bytes/ms */
#define USBIF_ISO_3 4 /* 640 bytes/ms */
#define USBIF_ISO_4 5 /* 768 bytes/ms */
#define USBIF_ISO_5 6 /* 896 bytes/ms */
#define USBIF_ISO_6 7 /* 1023 bytes/ms */
/* Flicker Modes */
#define NEVER_FLICKER 0
#define FLICKER_60 60
#define FLICKER_50 50
/* Debug flags */
#define DEBUG_NONE 0
#define DEBUG_REG 0x00000001
#define DEBUG_DUMP_PATCH 0x00000002
#define DEBUG_DUMP_REGS 0x00000004
/***
* Video frame sizes
***/
enum {
VIDEOSIZE_VGA = 0, /* 640x480 */
VIDEOSIZE_CIF, /* 352x288 */
VIDEOSIZE_QVGA, /* 320x240 */
VIDEOSIZE_QCIF, /* 176x144 */
VIDEOSIZE_288_216,
VIDEOSIZE_256_192,
VIDEOSIZE_224_168,
VIDEOSIZE_192_144,
};
#define STV_IMAGE_CIF_ROWS 288
#define STV_IMAGE_CIF_COLS 352
#define STV_IMAGE_QCIF_ROWS 144
#define STV_IMAGE_QCIF_COLS 176
#define STV_IMAGE_VGA_ROWS 480
#define STV_IMAGE_VGA_COLS 640
#define STV_IMAGE_QVGA_ROWS 240
#define STV_IMAGE_QVGA_COLS 320
#define JPEG_MARKER_COM (1<<6) /* Comment segment */
/***
* Enums
***/
/* Sensor types available with cpia2 asics */
enum sensors {
CPIA2_SENSOR_410,
CPIA2_SENSOR_500
};
/* Asic types available in the CPiA2 architecture */
#define CPIA2_ASIC_672 0x67
/* Device types (stv672, stv676, etc) */
#define DEVICE_STV_672 0x0001
#define DEVICE_STV_676 0x0002
enum frame_status {
FRAME_EMPTY,
FRAME_READING, /* In the process of being grabbed into */
FRAME_READY, /* Ready to be read */
FRAME_ERROR,
};
/***
* Register access (for USB request byte)
***/
enum {
CAMERAACCESS_SYSTEM = 0,
CAMERAACCESS_VC,
CAMERAACCESS_VP,
CAMERAACCESS_IDATA
};
#define CAMERAACCESS_TYPE_BLOCK 0x00
#define CAMERAACCESS_TYPE_RANDOM 0x04
#define CAMERAACCESS_TYPE_MASK 0x08
#define CAMERAACCESS_TYPE_REPEAT 0x0C
#define TRANSFER_READ 0
#define TRANSFER_WRITE 1
#define DEFAULT_ALT USBIF_ISO_6
#define DEFAULT_BRIGHTNESS 0x46
#define DEFAULT_CONTRAST 0x93
#define DEFAULT_SATURATION 0x7f
/* Power state */
#define HI_POWER_MODE CPIA2_SYSTEM_CONTROL_HIGH_POWER
#define LO_POWER_MODE CPIA2_SYSTEM_CONTROL_LOW_POWER
/********
* Commands
*******/
enum {
CPIA2_CMD_NONE = 0,
CPIA2_CMD_GET_VERSION,
CPIA2_CMD_GET_PNP_ID,
CPIA2_CMD_GET_ASIC_TYPE,
CPIA2_CMD_GET_SENSOR,
CPIA2_CMD_GET_VP_DEVICE,
CPIA2_CMD_GET_VP_BRIGHTNESS,
CPIA2_CMD_SET_VP_BRIGHTNESS,
CPIA2_CMD_GET_CONTRAST,
CPIA2_CMD_SET_CONTRAST,
CPIA2_CMD_GET_VP_SATURATION,
CPIA2_CMD_SET_VP_SATURATION,
CPIA2_CMD_GET_VP_GPIO_DIRECTION,
CPIA2_CMD_SET_VP_GPIO_DIRECTION,
CPIA2_CMD_GET_VP_GPIO_DATA,
CPIA2_CMD_SET_VP_GPIO_DATA,
CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION,
CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION,
CPIA2_CMD_GET_VC_MP_GPIO_DATA,
CPIA2_CMD_SET_VC_MP_GPIO_DATA,
CPIA2_CMD_ENABLE_PACKET_CTRL,
CPIA2_CMD_GET_FLICKER_MODES,
CPIA2_CMD_SET_FLICKER_MODES,
CPIA2_CMD_RESET_FIFO, /* clear fifo and enable stream block */
CPIA2_CMD_SET_HI_POWER,
CPIA2_CMD_SET_LOW_POWER,
CPIA2_CMD_CLEAR_V2W_ERR,
CPIA2_CMD_SET_USER_MODE,
CPIA2_CMD_GET_USER_MODE,
CPIA2_CMD_FRAMERATE_REQ,
CPIA2_CMD_SET_COMPRESSION_STATE,
CPIA2_CMD_GET_WAKEUP,
CPIA2_CMD_SET_WAKEUP,
CPIA2_CMD_GET_PW_CONTROL,
CPIA2_CMD_SET_PW_CONTROL,
CPIA2_CMD_GET_SYSTEM_CTRL,
CPIA2_CMD_SET_SYSTEM_CTRL,
CPIA2_CMD_GET_VP_SYSTEM_STATE,
CPIA2_CMD_GET_VP_SYSTEM_CTRL,
CPIA2_CMD_SET_VP_SYSTEM_CTRL,
CPIA2_CMD_GET_VP_EXP_MODES,
CPIA2_CMD_SET_VP_EXP_MODES,
CPIA2_CMD_GET_DEVICE_CONFIG,
CPIA2_CMD_SET_DEVICE_CONFIG,
CPIA2_CMD_SET_SERIAL_ADDR,
CPIA2_CMD_SET_SENSOR_CR1,
CPIA2_CMD_GET_VC_CONTROL,
CPIA2_CMD_SET_VC_CONTROL,
CPIA2_CMD_SET_TARGET_KB,
CPIA2_CMD_SET_DEF_JPEG_OPT,
CPIA2_CMD_REHASH_VP4,
CPIA2_CMD_GET_USER_EFFECTS,
CPIA2_CMD_SET_USER_EFFECTS
};
enum user_cmd {
COMMAND_NONE = 0x00000001,
COMMAND_SET_FPS = 0x00000002,
COMMAND_SET_COLOR_PARAMS = 0x00000004,
COMMAND_GET_COLOR_PARAMS = 0x00000008,
COMMAND_SET_FORMAT = 0x00000010, /* size, etc */
COMMAND_SET_FLICKER = 0x00000020
};
/***
* Some defines specific to the 676 chip
***/
#define CAMACC_CIF 0x01
#define CAMACC_VGA 0x02
#define CAMACC_QCIF 0x04
#define CAMACC_QVGA 0x08
struct cpia2_register {
u8 index;
u8 value;
};
struct cpia2_reg_mask {
u8 index;
u8 and_mask;
u8 or_mask;
u8 fill;
};
struct cpia2_command {
u32 command;
u8 req_mode; /* (Block or random) | registerBank */
u8 reg_count;
u8 direction;
u8 start;
union reg_types {
struct cpia2_register registers[32];
struct cpia2_reg_mask masks[16];
u8 block_data[64];
u8 *patch_data; /* points to function defined block */
} buffer;
};
struct camera_params {
struct {
u8 firmware_revision_hi; /* For system register set (bank 0) */
u8 firmware_revision_lo;
u8 asic_id; /* Video Compressor set (bank 1) */
u8 asic_rev;
u8 vp_device_hi; /* Video Processor set (bank 2) */
u8 vp_device_lo;
u8 sensor_flags;
u8 sensor_rev;
} version;
struct {
u32 device_type; /* enumerated from vendor/product ids.
* Currently, either STV_672 or STV_676 */
u16 vendor;
u16 product;
u16 device_revision;
} pnp_id;
struct {
u8 brightness; /* CPIA2_VP_EXPOSURE_TARGET */
u8 contrast; /* Note: this is CPIA2_VP_YRANGE */
u8 saturation; /* CPIA2_VP_SATURATION */
} color_params;
struct {
u8 cam_register;
u8 flicker_mode_req; /* 1 if flicker on, else never flicker */
} flicker_control;
struct {
u8 jpeg_options;
u8 creep_period;
u8 user_squeeze;
u8 inhibit_htables;
} compression;
struct {
u8 ohsize; /* output image size */
u8 ovsize;
u8 hcrop; /* cropping start_pos/4 */
u8 vcrop;
u8 hphase; /* scaling registers */
u8 vphase;
u8 hispan;
u8 vispan;
u8 hicrop;
u8 vicrop;
u8 hifraction;
u8 vifraction;
} image_size;
struct {
int width; /* actual window width */
int height; /* actual window height */
} roi;
struct {
u8 video_mode;
u8 frame_rate;
u8 video_size; /* Not a register, just a convenience for cropped sizes */
u8 gpio_direction;
u8 gpio_data;
u8 system_ctrl;
u8 system_state;
u8 lowlight_boost; /* Bool: 0 = off, 1 = on */
u8 device_config;
u8 exposure_modes;
u8 user_effects;
} vp_params;
struct {
u8 pw_control;
u8 wakeup;
u8 vc_control;
u8 vc_mp_direction;
u8 vc_mp_data;
u8 quality;
} vc_params;
struct {
u8 power_mode;
u8 system_ctrl;
u8 stream_mode; /* This is the current alternate for usb drivers */
u8 allow_corrupt;
} camera_state;
};
#define NUM_SBUF 2
struct cpia2_sbuf {
char *data;
struct urb *urb;
};
struct framebuf {
struct timeval timestamp;
unsigned long seq;
int num;
int length;
int max_length;
volatile enum frame_status status;
u8 *data;
struct framebuf *next;
};
struct camera_data {
/* locks */
struct v4l2_device v4l2_dev;
struct mutex v4l2_lock; /* serialize file operations */
struct v4l2_ctrl_handler hdl;
struct {
/* Lights control cluster */
struct v4l2_ctrl *top_light;
struct v4l2_ctrl *bottom_light;
};
struct v4l2_ctrl *usb_alt;
/* camera status */
int first_image_seen;
enum sensors sensor_type;
u8 flush;
struct v4l2_fh *stream_fh;
u8 mmapped;
int streaming; /* 0 = no, 1 = yes */
int xfer_mode; /* XFER_BULK or XFER_ISOC */
struct camera_params params; /* camera settings */
/* v4l */
int video_size; /* VIDEO_SIZE_ */
struct video_device vdev; /* v4l videodev */
u32 width;
u32 height; /* Its size */
__u32 pixelformat; /* Format fourcc */
/* USB */
struct usb_device *dev;
unsigned char iface;
unsigned int cur_alt;
unsigned int old_alt;
struct cpia2_sbuf sbuf[NUM_SBUF]; /* Double buffering */
wait_queue_head_t wq_stream;
/* Buffering */
u32 frame_size;
int num_frames;
unsigned long frame_count;
u8 *frame_buffer; /* frame buffer data */
struct framebuf *buffers;
struct framebuf * volatile curbuff;
struct framebuf *workbuff;
/* MJPEG Extension */
int APPn; /* Number of APP segment to be written, must be 0..15 */
int APP_len; /* Length of data in JPEG APPn segment */
char APP_data[60]; /* Data in the JPEG APPn segment. */
int COM_len; /* Length of data in JPEG COM segment */
char COM_data[60]; /* Data in JPEG COM segment */
};
/* v4l */
int cpia2_register_camera(struct camera_data *cam);
void cpia2_unregister_camera(struct camera_data *cam);
void cpia2_camera_release(struct v4l2_device *v4l2_dev);
/* core */
int cpia2_reset_camera(struct camera_data *cam);
int cpia2_set_low_power(struct camera_data *cam);
void cpia2_dbg_dump_registers(struct camera_data *cam);
int cpia2_match_video_size(int width, int height);
void cpia2_set_camera_state(struct camera_data *cam);
void cpia2_save_camera_state(struct camera_data *cam);
void cpia2_set_color_params(struct camera_data *cam);
void cpia2_set_brightness(struct camera_data *cam, unsigned char value);
void cpia2_set_contrast(struct camera_data *cam, unsigned char value);
void cpia2_set_saturation(struct camera_data *cam, unsigned char value);
int cpia2_set_flicker_mode(struct camera_data *cam, int mode);
void cpia2_set_format(struct camera_data *cam);
int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd);
int cpia2_do_command(struct camera_data *cam,
unsigned int command,
unsigned char direction, unsigned char param);
struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf);
int cpia2_init_camera(struct camera_data *cam);
int cpia2_allocate_buffers(struct camera_data *cam);
void cpia2_free_buffers(struct camera_data *cam);
long cpia2_read(struct camera_data *cam,
char __user *buf, unsigned long count, int noblock);
unsigned int cpia2_poll(struct camera_data *cam,
struct file *filp, poll_table *wait);
int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma);
void cpia2_set_property_flip(struct camera_data *cam, int prop_val);
void cpia2_set_property_mirror(struct camera_data *cam, int prop_val);
int cpia2_set_gpio(struct camera_data *cam, unsigned char setting);
int cpia2_set_fps(struct camera_data *cam, int framerate);
/* usb */
int cpia2_usb_init(void);
void cpia2_usb_cleanup(void);
int cpia2_usb_transfer_cmd(struct camera_data *cam, void *registers,
u8 request, u8 start, u8 count, u8 direction);
int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate);
int cpia2_usb_stream_stop(struct camera_data *cam);
int cpia2_usb_stream_pause(struct camera_data *cam);
int cpia2_usb_stream_resume(struct camera_data *cam);
int cpia2_usb_change_streaming_alternate(struct camera_data *cam,
unsigned int alt);
/* ----------------------- debug functions ---------------------- */
#ifdef _CPIA2_DEBUG_
#define ALOG(lev, fmt, args...) printk(lev "%s:%d %s(): " fmt, __FILE__, __LINE__, __func__, ## args)
#define LOG(fmt, args...) ALOG(KERN_INFO, fmt, ## args)
#define ERR(fmt, args...) ALOG(KERN_ERR, fmt, ## args)
#define DBG(fmt, args...) ALOG(KERN_DEBUG, fmt, ## args)
#else
#define ALOG(fmt,args...) printk(fmt,##args)
#define LOG(fmt,args...) ALOG(KERN_INFO "cpia2: "fmt,##args)
#define ERR(fmt,args...) ALOG(KERN_ERR "cpia2: "fmt,##args)
#define DBG(fmn,args...) do {} while(0)
#endif
/* No function or lineno, for shorter lines */
#define KINFO(fmt, args...) printk(KERN_INFO fmt,##args)
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,476 @@
/****************************************************************************
*
* Filename: cpia2registers.h
*
* Copyright 2001, STMicrolectronics, Inc.
*
* Description:
* Definitions for the CPia2 register set
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
****************************************************************************/
#ifndef CPIA2_REGISTER_HEADER
#define CPIA2_REGISTER_HEADER
/***
* System register set (Bank 0)
***/
#define CPIA2_SYSTEM_DEVICE_HI 0x00
#define CPIA2_SYSTEM_DEVICE_LO 0x01
#define CPIA2_SYSTEM_SYSTEM_CONTROL 0x02
#define CPIA2_SYSTEM_CONTROL_LOW_POWER 0x00
#define CPIA2_SYSTEM_CONTROL_HIGH_POWER 0x01
#define CPIA2_SYSTEM_CONTROL_SUSPEND 0x02
#define CPIA2_SYSTEM_CONTROL_V2W_ERR 0x10
#define CPIA2_SYSTEM_CONTROL_RB_ERR 0x10
#define CPIA2_SYSTEM_CONTROL_CLEAR_ERR 0x80
#define CPIA2_SYSTEM_INT_PACKET_CTRL 0x04
#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX 0x01
#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_EOF 0x02
#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_INT1 0x04
#define CPIA2_SYSTEM_CACHE_CTRL 0x05
#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_RESET 0x01
#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_FLUSH 0x02
#define CPIA2_SYSTEM_SERIAL_CTRL 0x06
#define CPIA2_SYSTEM_SERIAL_CTRL_NULL_CMD 0x00
#define CPIA2_SYSTEM_SERIAL_CTRL_START_CMD 0x01
#define CPIA2_SYSTEM_SERIAL_CTRL_STOP_CMD 0x02
#define CPIA2_SYSTEM_SERIAL_CTRL_WRITE_CMD 0x03
#define CPIA2_SYSTEM_SERIAL_CTRL_READ_ACK_CMD 0x04
#define CPIA2_SYSTEM_SERIAL_CTRL_READ_NACK_CMD 0x05
#define CPIA2_SYSTEM_SERIAL_DATA 0x07
#define CPIA2_SYSTEM_VP_SERIAL_ADDR 0x08
/***
* I2C addresses for various devices in CPiA2
***/
#define CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR 0x20
#define CPIA2_SYSTEM_VP_SERIAL_ADDR_VP 0x88
#define CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP 0x8A
#define CPIA2_SYSTEM_SPARE_REG1 0x09
#define CPIA2_SYSTEM_SPARE_REG2 0x0A
#define CPIA2_SYSTEM_SPARE_REG3 0x0B
#define CPIA2_SYSTEM_MC_PORT_0 0x0C
#define CPIA2_SYSTEM_MC_PORT_1 0x0D
#define CPIA2_SYSTEM_MC_PORT_2 0x0E
#define CPIA2_SYSTEM_MC_PORT_3 0x0F
#define CPIA2_SYSTEM_STATUS_PKT 0x20
#define CPIA2_SYSTEM_STATUS_PKT_END 0x27
#define CPIA2_SYSTEM_DESCRIP_VID_HI 0x30
#define CPIA2_SYSTEM_DESCRIP_VID_LO 0x31
#define CPIA2_SYSTEM_DESCRIP_PID_HI 0x32
#define CPIA2_SYSTEM_DESCRIP_PID_LO 0x33
#define CPIA2_SYSTEM_FW_VERSION_HI 0x34
#define CPIA2_SYSTEM_FW_VERSION_LO 0x35
#define CPIA2_SYSTEM_CACHE_START_INDEX 0x80
#define CPIA2_SYSTEM_CACHE_MAX_WRITES 0x10
/***
* VC register set (Bank 1)
***/
#define CPIA2_VC_ASIC_ID 0x80
#define CPIA2_VC_ASIC_REV 0x81
#define CPIA2_VC_PW_CTRL 0x82
#define CPIA2_VC_PW_CTRL_COLDSTART 0x01
#define CPIA2_VC_PW_CTRL_CP_CLK_EN 0x02
#define CPIA2_VC_PW_CTRL_VP_RESET_N 0x04
#define CPIA2_VC_PW_CTRL_VC_CLK_EN 0x08
#define CPIA2_VC_PW_CTRL_VC_RESET_N 0x10
#define CPIA2_VC_PW_CTRL_GOTO_SUSPEND 0x20
#define CPIA2_VC_PW_CTRL_UDC_SUSPEND 0x40
#define CPIA2_VC_PW_CTRL_PWR_DOWN 0x80
#define CPIA2_VC_WAKEUP 0x83
#define CPIA2_VC_WAKEUP_SW_ENABLE 0x01
#define CPIA2_VC_WAKEUP_XX_ENABLE 0x02
#define CPIA2_VC_WAKEUP_SW_ATWAKEUP 0x04
#define CPIA2_VC_WAKEUP_XX_ATWAKEUP 0x08
#define CPIA2_VC_CLOCK_CTRL 0x84
#define CPIA2_VC_CLOCK_CTRL_TESTUP72 0x01
#define CPIA2_VC_INT_ENABLE 0x88
#define CPIA2_VC_INT_ENABLE_XX_IE 0x01
#define CPIA2_VC_INT_ENABLE_SW_IE 0x02
#define CPIA2_VC_INT_ENABLE_VC_IE 0x04
#define CPIA2_VC_INT_ENABLE_USBDATA_IE 0x08
#define CPIA2_VC_INT_ENABLE_USBSETUP_IE 0x10
#define CPIA2_VC_INT_ENABLE_USBCFG_IE 0x20
#define CPIA2_VC_INT_FLAG 0x89
#define CPIA2_VC_INT_ENABLE_XX_FLAG 0x01
#define CPIA2_VC_INT_ENABLE_SW_FLAG 0x02
#define CPIA2_VC_INT_ENABLE_VC_FLAG 0x04
#define CPIA2_VC_INT_ENABLE_USBDATA_FLAG 0x08
#define CPIA2_VC_INT_ENABLE_USBSETUP_FLAG 0x10
#define CPIA2_VC_INT_ENABLE_USBCFG_FLAG 0x20
#define CPIA2_VC_INT_ENABLE_SET_RESET_BIT 0x80
#define CPIA2_VC_INT_STATE 0x8A
#define CPIA2_VC_INT_STATE_XX_STATE 0x01
#define CPIA2_VC_INT_STATE_SW_STATE 0x02
#define CPIA2_VC_MP_DIR 0x90
#define CPIA2_VC_MP_DIR_INPUT 0x00
#define CPIA2_VC_MP_DIR_OUTPUT 0x01
#define CPIA2_VC_MP_DATA 0x91
#define CPIA2_VC_DP_CTRL 0x98
#define CPIA2_VC_DP_CTRL_MODE_0 0x00
#define CPIA2_VC_DP_CTRL_MODE_A 0x01
#define CPIA2_VC_DP_CTRL_MODE_B 0x02
#define CPIA2_VC_DP_CTRL_MODE_C 0x03
#define CPIA2_VC_DP_CTRL_FAKE_FST 0x04
#define CPIA2_VC_AD_CTRL 0x99
#define CPIA2_VC_AD_CTRL_SRC_0 0x00
#define CPIA2_VC_AD_CTRL_SRC_DIGI_A 0x01
#define CPIA2_VC_AD_CTRL_SRC_REG 0x02
#define CPIA2_VC_AD_CTRL_DST_USB 0x00
#define CPIA2_VC_AD_CTRL_DST_REG 0x04
#define CPIA2_VC_AD_TEST_IN 0x9B
#define CPIA2_VC_AD_TEST_OUT 0x9C
#define CPIA2_VC_AD_STATUS 0x9D
#define CPIA2_VC_AD_STATUS_EMPTY 0x01
#define CPIA2_VC_AD_STATUS_FULL 0x02
#define CPIA2_VC_DP_DATA 0x9E
#define CPIA2_VC_ST_CTRL 0xA0
#define CPIA2_VC_ST_CTRL_SRC_VC 0x00
#define CPIA2_VC_ST_CTRL_SRC_DP 0x01
#define CPIA2_VC_ST_CTRL_SRC_REG 0x02
#define CPIA2_VC_ST_CTRL_RAW_SELECT 0x04
#define CPIA2_VC_ST_CTRL_DST_USB 0x00
#define CPIA2_VC_ST_CTRL_DST_DP 0x08
#define CPIA2_VC_ST_CTRL_DST_REG 0x10
#define CPIA2_VC_ST_CTRL_FIFO_ENABLE 0x20
#define CPIA2_VC_ST_CTRL_EOF_DETECT 0x40
#define CPIA2_VC_ST_TEST 0xA1
#define CPIA2_VC_ST_TEST_MODE_MANUAL 0x00
#define CPIA2_VC_ST_TEST_MODE_INCREMENT 0x02
#define CPIA2_VC_ST_TEST_AUTO_FILL 0x08
#define CPIA2_VC_ST_TEST_REPEAT_FIFO 0x10
#define CPIA2_VC_ST_TEST_IN 0xA2
#define CPIA2_VC_ST_TEST_OUT 0xA3
#define CPIA2_VC_ST_STATUS 0xA4
#define CPIA2_VC_ST_STATUS_EMPTY 0x01
#define CPIA2_VC_ST_STATUS_FULL 0x02
#define CPIA2_VC_ST_FRAME_DETECT_1 0xA5
#define CPIA2_VC_ST_FRAME_DETECT_2 0xA6
#define CPIA2_VC_USB_CTRL 0xA8
#define CPIA2_VC_USB_CTRL_CMD_STALLED 0x01
#define CPIA2_VC_USB_CTRL_CMD_READY 0x02
#define CPIA2_VC_USB_CTRL_CMD_STATUS 0x04
#define CPIA2_VC_USB_CTRL_CMD_STATUS_DIR 0x08
#define CPIA2_VC_USB_CTRL_CMD_NO_CLASH 0x10
#define CPIA2_VC_USB_CTRL_CMD_MICRO_ACCESS 0x80
#define CPIA2_VC_USB_STRM 0xA9
#define CPIA2_VC_USB_STRM_ISO_ENABLE 0x01
#define CPIA2_VC_USB_STRM_BLK_ENABLE 0x02
#define CPIA2_VC_USB_STRM_INT_ENABLE 0x04
#define CPIA2_VC_USB_STRM_AUD_ENABLE 0x08
#define CPIA2_VC_USB_STATUS 0xAA
#define CPIA2_VC_USB_STATUS_CMD_IN_PROGRESS 0x01
#define CPIA2_VC_USB_STATUS_CMD_STATUS_STALL 0x02
#define CPIA2_VC_USB_STATUS_CMD_HANDSHAKE 0x04
#define CPIA2_VC_USB_STATUS_CMD_OVERRIDE 0x08
#define CPIA2_VC_USB_STATUS_CMD_FIFO_BUSY 0x10
#define CPIA2_VC_USB_STATUS_BULK_REPEAT_TXN 0x20
#define CPIA2_VC_USB_STATUS_CONFIG_DONE 0x40
#define CPIA2_VC_USB_STATUS_USB_SUSPEND 0x80
#define CPIA2_VC_USB_CMDW 0xAB
#define CPIA2_VC_USB_DATARW 0xAC
#define CPIA2_VC_USB_INFO 0xAD
#define CPIA2_VC_USB_CONFIG 0xAE
#define CPIA2_VC_USB_SETTINGS 0xAF
#define CPIA2_VC_USB_SETTINGS_CONFIG_MASK 0x03
#define CPIA2_VC_USB_SETTINGS_INTERFACE_MASK 0x0C
#define CPIA2_VC_USB_SETTINGS_ALTERNATE_MASK 0x70
#define CPIA2_VC_USB_ISOLIM 0xB0
#define CPIA2_VC_USB_ISOFAILS 0xB1
#define CPIA2_VC_USB_ISOMAXPKTHI 0xB2
#define CPIA2_VC_USB_ISOMAXPKTLO 0xB3
#define CPIA2_VC_V2W_CTRL 0xB8
#define CPIA2_VC_V2W_SELECT 0x01
#define CPIA2_VC_V2W_SCL 0xB9
#define CPIA2_VC_V2W_SDA 0xBA
#define CPIA2_VC_VC_CTRL 0xC0
#define CPIA2_VC_VC_CTRL_RUN 0x01
#define CPIA2_VC_VC_CTRL_SINGLESHOT 0x02
#define CPIA2_VC_VC_CTRL_IDLING 0x04
#define CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES 0x10
#define CPIA2_VC_VC_CTRL_INHIBIT_Q_TABLES 0x20
#define CPIA2_VC_VC_CTRL_INHIBIT_PRIVATE 0x40
#define CPIA2_VC_VC_RESTART_IVAL_HI 0xC1
#define CPIA2_VC_VC_RESTART_IVAL_LO 0xC2
#define CPIA2_VC_VC_FORMAT 0xC3
#define CPIA2_VC_VC_FORMAT_UFIRST 0x01
#define CPIA2_VC_VC_FORMAT_MONO 0x02
#define CPIA2_VC_VC_FORMAT_DECIMATING 0x04
#define CPIA2_VC_VC_FORMAT_SHORTLINE 0x08
#define CPIA2_VC_VC_FORMAT_SELFTEST 0x10
#define CPIA2_VC_VC_CLOCKS 0xC4
#define CPIA2_VC_VC_CLOCKS_CLKDIV_MASK 0x03
#define CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 0x04
#define CPIA2_VC_VC_672_CLOCKS_SCALING 0x08
#define CPIA2_VC_VC_CLOCKS_LOGDIV0 0x00
#define CPIA2_VC_VC_CLOCKS_LOGDIV1 0x01
#define CPIA2_VC_VC_CLOCKS_LOGDIV2 0x02
#define CPIA2_VC_VC_CLOCKS_LOGDIV3 0x03
#define CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 0x08
#define CPIA2_VC_VC_676_CLOCKS_SCALING 0x10
#define CPIA2_VC_VC_IHSIZE_LO 0xC5
#define CPIA2_VC_VC_XLIM_HI 0xC6
#define CPIA2_VC_VC_XLIM_LO 0xC7
#define CPIA2_VC_VC_YLIM_HI 0xC8
#define CPIA2_VC_VC_YLIM_LO 0xC9
#define CPIA2_VC_VC_OHSIZE 0xCA
#define CPIA2_VC_VC_OVSIZE 0xCB
#define CPIA2_VC_VC_HCROP 0xCC
#define CPIA2_VC_VC_VCROP 0xCD
#define CPIA2_VC_VC_HPHASE 0xCE
#define CPIA2_VC_VC_VPHASE 0xCF
#define CPIA2_VC_VC_HISPAN 0xD0
#define CPIA2_VC_VC_VISPAN 0xD1
#define CPIA2_VC_VC_HICROP 0xD2
#define CPIA2_VC_VC_VICROP 0xD3
#define CPIA2_VC_VC_HFRACT 0xD4
#define CPIA2_VC_VC_HFRACT_DEN_MASK 0x0F
#define CPIA2_VC_VC_HFRACT_NUM_MASK 0xF0
#define CPIA2_VC_VC_VFRACT 0xD5
#define CPIA2_VC_VC_VFRACT_DEN_MASK 0x0F
#define CPIA2_VC_VC_VFRACT_NUM_MASK 0xF0
#define CPIA2_VC_VC_JPEG_OPT 0xD6
#define CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE 0x01
#define CPIA2_VC_VC_JPEG_OPT_NO_DC_AUTO_SQUEEZE 0x02
#define CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE 0x04
#define CPIA2_VC_VC_JPEG_OPT_DEFAULT (CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE|\
CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE)
#define CPIA2_VC_VC_CREEP_PERIOD 0xD7
#define CPIA2_VC_VC_USER_SQUEEZE 0xD8
#define CPIA2_VC_VC_TARGET_KB 0xD9
#define CPIA2_VC_VC_AUTO_SQUEEZE 0xE6
/***
* VP register set (Bank 2)
***/
#define CPIA2_VP_DEVICEH 0
#define CPIA2_VP_DEVICEL 1
#define CPIA2_VP_SYSTEMSTATE 0x02
#define CPIA2_VP_SYSTEMSTATE_HK_ALIVE 0x01
#define CPIA2_VP_SYSTEMCTRL 0x03
#define CPIA2_VP_SYSTEMCTRL_REQ_CLEAR_ERROR 0x80
#define CPIA2_VP_SYSTEMCTRL_POWER_DOWN_PLL 0x20
#define CPIA2_VP_SYSTEMCTRL_REQ_SUSPEND_STATE 0x10
#define CPIA2_VP_SYSTEMCTRL_REQ_SERIAL_WAKEUP 0x08
#define CPIA2_VP_SYSTEMCTRL_REQ_AUTOLOAD 0x04
#define CPIA2_VP_SYSTEMCTRL_HK_CONTROL 0x02
#define CPIA2_VP_SYSTEMCTRL_POWER_CONTROL 0x01
#define CPIA2_VP_SENSOR_FLAGS 0x05
#define CPIA2_VP_SENSOR_FLAGS_404 0x01
#define CPIA2_VP_SENSOR_FLAGS_407 0x02
#define CPIA2_VP_SENSOR_FLAGS_409 0x04
#define CPIA2_VP_SENSOR_FLAGS_410 0x08
#define CPIA2_VP_SENSOR_FLAGS_500 0x10
#define CPIA2_VP_SENSOR_REV 0x06
#define CPIA2_VP_DEVICE_CONFIG 0x07
#define CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE 0x01
#define CPIA2_VP_GPIO_DIRECTION 0x08
#define CPIA2_VP_GPIO_READ 0xFF
#define CPIA2_VP_GPIO_WRITE 0x00
#define CPIA2_VP_GPIO_DATA 0x09
#define CPIA2_VP_RAM_ADDR_H 0x0A
#define CPIA2_VP_RAM_ADDR_L 0x0B
#define CPIA2_VP_RAM_DATA 0x0C
#define CPIA2_VP_PATCH_REV 0x0F
#define CPIA2_VP4_USER_MODE 0x10
#define CPIA2_VP5_USER_MODE 0x13
#define CPIA2_VP_USER_MODE_CIF 0x01
#define CPIA2_VP_USER_MODE_QCIFDS 0x02
#define CPIA2_VP_USER_MODE_QCIFPTC 0x04
#define CPIA2_VP_USER_MODE_QVGADS 0x08
#define CPIA2_VP_USER_MODE_QVGAPTC 0x10
#define CPIA2_VP_USER_MODE_VGA 0x20
#define CPIA2_VP4_FRAMERATE_REQUEST 0x11
#define CPIA2_VP5_FRAMERATE_REQUEST 0x14
#define CPIA2_VP_FRAMERATE_60 0x80
#define CPIA2_VP_FRAMERATE_50 0x40
#define CPIA2_VP_FRAMERATE_30 0x20
#define CPIA2_VP_FRAMERATE_25 0x10
#define CPIA2_VP_FRAMERATE_15 0x08
#define CPIA2_VP_FRAMERATE_12_5 0x04
#define CPIA2_VP_FRAMERATE_7_5 0x02
#define CPIA2_VP_FRAMERATE_6_25 0x01
#define CPIA2_VP4_USER_EFFECTS 0x12
#define CPIA2_VP5_USER_EFFECTS 0x15
#define CPIA2_VP_USER_EFFECTS_COLBARS 0x01
#define CPIA2_VP_USER_EFFECTS_COLBARS_GRAD 0x02
#define CPIA2_VP_USER_EFFECTS_MIRROR 0x04
#define CPIA2_VP_USER_EFFECTS_FLIP 0x40 // VP5 only
/* NOTE: CPIA2_VP_EXPOSURE_MODES shares the same register as VP5 User
* Effects */
#define CPIA2_VP_EXPOSURE_MODES 0x15
#define CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER 0x20
#define CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP 0x10
#define CPIA2_VP4_EXPOSURE_TARGET 0x16 // VP4
#define CPIA2_VP5_EXPOSURE_TARGET 0x20 // VP5
#define CPIA2_VP_FLICKER_MODES 0x1B
#define CPIA2_VP_FLICKER_MODES_50HZ 0x80
#define CPIA2_VP_FLICKER_MODES_CUSTOM_FLT_FFREQ 0x40
#define CPIA2_VP_FLICKER_MODES_NEVER_FLICKER 0x20
#define CPIA2_VP_FLICKER_MODES_INHIBIT_RUB 0x10
#define CPIA2_VP_FLICKER_MODES_ADJUST_LINE_FREQ 0x08
#define CPIA2_VP_FLICKER_MODES_CUSTOM_INT_FFREQ 0x04
#define CPIA2_VP_UMISC 0x1D
#define CPIA2_VP_UMISC_FORCE_MONO 0x80
#define CPIA2_VP_UMISC_FORCE_ID_MASK 0x40
#define CPIA2_VP_UMISC_INHIBIT_AUTO_FGS 0x20
#define CPIA2_VP_UMISC_INHIBIT_AUTO_DIMS 0x08
#define CPIA2_VP_UMISC_OPT_FOR_SENSOR_DS 0x04
#define CPIA2_VP_UMISC_INHIBIT_AUTO_MODE_INT 0x02
#define CPIA2_VP5_ANTIFLKRSETUP 0x22 //34
#define CPIA2_VP_INTERPOLATION 0x24
#define CPIA2_VP_INTERPOLATION_EVEN_FIRST 0x40
#define CPIA2_VP_INTERPOLATION_HJOG 0x20
#define CPIA2_VP_INTERPOLATION_VJOG 0x10
#define CPIA2_VP_GAMMA 0x25
#define CPIA2_VP_DEFAULT_GAMMA 0x10
#define CPIA2_VP_YRANGE 0x26
#define CPIA2_VP_SATURATION 0x27
#define CPIA2_VP5_MYBLACK_LEVEL 0x3A //58
#define CPIA2_VP5_MCYRANGE 0x3B //59
#define CPIA2_VP5_MYCEILING 0x3C //60
#define CPIA2_VP5_MCUVSATURATION 0x3D //61
#define CPIA2_VP_REHASH_VALUES 0x60
/***
* Common sensor registers
***/
#define CPIA2_SENSOR_DEVICE_H 0x00
#define CPIA2_SENSOR_DEVICE_L 0x01
#define CPIA2_SENSOR_DATA_FORMAT 0x16
#define CPIA2_SENSOR_DATA_FORMAT_HMIRROR 0x08
#define CPIA2_SENSOR_DATA_FORMAT_VMIRROR 0x10
#define CPIA2_SENSOR_CR1 0x76
#define CPIA2_SENSOR_CR1_STAND_BY 0x01
#define CPIA2_SENSOR_CR1_DOWN_RAMP_GEN 0x02
#define CPIA2_SENSOR_CR1_DOWN_COLUMN_ADC 0x04
#define CPIA2_SENSOR_CR1_DOWN_CAB_REGULATOR 0x08
#define CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR 0x10
#define CPIA2_SENSOR_CR1_DOWN_VRT_AMP 0x20
#define CPIA2_SENSOR_CR1_DOWN_BAND_GAP 0x40
#endif

View file

@ -0,0 +1,955 @@
/****************************************************************************
*
* Filename: cpia2_usb.c
*
* Copyright 2001, STMicrolectronics, Inc.
* Contact: steve.miller@st.com
*
* Description:
* This is a USB driver for CPia2 based video cameras.
* The infrastructure of this driver is based on the cpia usb driver by
* Jochen Scharrlach and Johannes Erdfeldt.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Stripped of 2.4 stuff ready for main kernel submit by
* Alan Cox <alan@lxorguk.ukuu.org.uk>
****************************************************************************/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/module.h>
#include "cpia2.h"
static int frame_sizes[] = {
0, // USBIF_CMDONLY
0, // USBIF_BULK
128, // USBIF_ISO_1
384, // USBIF_ISO_2
640, // USBIF_ISO_3
768, // USBIF_ISO_4
896, // USBIF_ISO_5
1023, // USBIF_ISO_6
};
#define FRAMES_PER_DESC 10
#define FRAME_SIZE_PER_DESC frame_sizes[cam->cur_alt]
static void process_frame(struct camera_data *cam);
static void cpia2_usb_complete(struct urb *urb);
static int cpia2_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id);
static void cpia2_usb_disconnect(struct usb_interface *intf);
static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message);
static int cpia2_usb_resume(struct usb_interface *intf);
static void free_sbufs(struct camera_data *cam);
static void add_APPn(struct camera_data *cam);
static void add_COM(struct camera_data *cam);
static int submit_urbs(struct camera_data *cam);
static int set_alternate(struct camera_data *cam, unsigned int alt);
static int configure_transfer_mode(struct camera_data *cam, unsigned int alt);
static struct usb_device_id cpia2_id_table[] = {
{USB_DEVICE(0x0553, 0x0100)},
{USB_DEVICE(0x0553, 0x0140)},
{USB_DEVICE(0x0553, 0x0151)}, /* STV0676 */
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, cpia2_id_table);
static struct usb_driver cpia2_driver = {
.name = "cpia2",
.probe = cpia2_usb_probe,
.disconnect = cpia2_usb_disconnect,
.suspend = cpia2_usb_suspend,
.resume = cpia2_usb_resume,
.reset_resume = cpia2_usb_resume,
.id_table = cpia2_id_table
};
/******************************************************************************
*
* process_frame
*
*****************************************************************************/
static void process_frame(struct camera_data *cam)
{
static int frame_count;
unsigned char *inbuff = cam->workbuff->data;
DBG("Processing frame #%d, current:%d\n",
cam->workbuff->num, cam->curbuff->num);
if(cam->workbuff->length > cam->workbuff->max_length)
cam->workbuff->max_length = cam->workbuff->length;
if ((inbuff[0] == 0xFF) && (inbuff[1] == 0xD8)) {
frame_count++;
} else {
cam->workbuff->status = FRAME_ERROR;
DBG("Start of frame not found\n");
return;
}
/***
* Now the output buffer should have a JPEG image in it.
***/
if(!cam->first_image_seen) {
/* Always skip the first image after streaming
* starts. It is almost certainly corrupt. */
cam->first_image_seen = 1;
cam->workbuff->status = FRAME_EMPTY;
return;
}
if (cam->workbuff->length > 3) {
if(cam->mmapped &&
cam->workbuff->length < cam->workbuff->max_length) {
/* No junk in the buffers */
memset(cam->workbuff->data+cam->workbuff->length,
0, cam->workbuff->max_length-
cam->workbuff->length);
}
cam->workbuff->max_length = cam->workbuff->length;
cam->workbuff->status = FRAME_READY;
if(!cam->mmapped && cam->num_frames > 2) {
/* During normal reading, the most recent
* frame will be read. If the current frame
* hasn't started reading yet, it will never
* be read, so mark it empty. If the buffer is
* mmapped, or we have few buffers, we need to
* wait for the user to free the buffer.
*
* NOTE: This is not entirely foolproof with 3
* buffers, but it would take an EXTREMELY
* overloaded system to cause problems (possible
* image data corruption). Basically, it would
* need to take more time to execute cpia2_read
* than it would for the camera to send
* cam->num_frames-2 frames before problems
* could occur.
*/
cam->curbuff->status = FRAME_EMPTY;
}
cam->curbuff = cam->workbuff;
cam->workbuff = cam->workbuff->next;
DBG("Changed buffers, work:%d, current:%d\n",
cam->workbuff->num, cam->curbuff->num);
return;
} else {
DBG("Not enough data for an image.\n");
}
cam->workbuff->status = FRAME_ERROR;
return;
}
/******************************************************************************
*
* add_APPn
*
* Adds a user specified APPn record
*****************************************************************************/
static void add_APPn(struct camera_data *cam)
{
if(cam->APP_len > 0) {
cam->workbuff->data[cam->workbuff->length++] = 0xFF;
cam->workbuff->data[cam->workbuff->length++] = 0xE0+cam->APPn;
cam->workbuff->data[cam->workbuff->length++] = 0;
cam->workbuff->data[cam->workbuff->length++] = cam->APP_len+2;
memcpy(cam->workbuff->data+cam->workbuff->length,
cam->APP_data, cam->APP_len);
cam->workbuff->length += cam->APP_len;
}
}
/******************************************************************************
*
* add_COM
*
* Adds a user specified COM record
*****************************************************************************/
static void add_COM(struct camera_data *cam)
{
if(cam->COM_len > 0) {
cam->workbuff->data[cam->workbuff->length++] = 0xFF;
cam->workbuff->data[cam->workbuff->length++] = 0xFE;
cam->workbuff->data[cam->workbuff->length++] = 0;
cam->workbuff->data[cam->workbuff->length++] = cam->COM_len+2;
memcpy(cam->workbuff->data+cam->workbuff->length,
cam->COM_data, cam->COM_len);
cam->workbuff->length += cam->COM_len;
}
}
/******************************************************************************
*
* cpia2_usb_complete
*
* callback when incoming packet is received
*****************************************************************************/
static void cpia2_usb_complete(struct urb *urb)
{
int i;
unsigned char *cdata;
static bool frame_ready = false;
struct camera_data *cam = (struct camera_data *) urb->context;
if (urb->status!=0) {
if (!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN))
{
DBG("urb->status = %d!\n", urb->status);
}
DBG("Stopping streaming\n");
return;
}
if (!cam->streaming || !video_is_registered(&cam->vdev)) {
LOG("Will now stop the streaming: streaming = %d, present=%d\n",
cam->streaming, video_is_registered(&cam->vdev));
return;
}
/***
* Packet collater
***/
//DBG("Collating %d packets\n", urb->number_of_packets);
for (i = 0; i < urb->number_of_packets; i++) {
u16 checksum, iso_checksum;
int j;
int n = urb->iso_frame_desc[i].actual_length;
int st = urb->iso_frame_desc[i].status;
if(cam->workbuff->status == FRAME_READY) {
struct framebuf *ptr;
/* Try to find an available buffer */
DBG("workbuff full, searching\n");
for (ptr = cam->workbuff->next;
ptr != cam->workbuff;
ptr = ptr->next)
{
if (ptr->status == FRAME_EMPTY) {
ptr->status = FRAME_READING;
ptr->length = 0;
break;
}
}
if (ptr == cam->workbuff)
break; /* No READING or EMPTY buffers left */
cam->workbuff = ptr;
}
if (cam->workbuff->status == FRAME_EMPTY ||
cam->workbuff->status == FRAME_ERROR) {
cam->workbuff->status = FRAME_READING;
cam->workbuff->length = 0;
}
//DBG(" Packet %d length = %d, status = %d\n", i, n, st);
cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
if (st) {
LOG("cpia2 data error: [%d] len=%d, status = %d\n",
i, n, st);
if(!ALLOW_CORRUPT)
cam->workbuff->status = FRAME_ERROR;
continue;
}
if(n<=2)
continue;
checksum = 0;
for(j=0; j<n-2; ++j)
checksum += cdata[j];
iso_checksum = cdata[j] + cdata[j+1]*256;
if(checksum != iso_checksum) {
LOG("checksum mismatch: [%d] len=%d, calculated = %x, checksum = %x\n",
i, n, (int)checksum, (int)iso_checksum);
if(!ALLOW_CORRUPT) {
cam->workbuff->status = FRAME_ERROR;
continue;
}
}
n -= 2;
if(cam->workbuff->status != FRAME_READING) {
if((0xFF == cdata[0] && 0xD8 == cdata[1]) ||
(0xD8 == cdata[0] && 0xFF == cdata[1] &&
0 != cdata[2])) {
/* frame is skipped, but increment total
* frame count anyway */
cam->frame_count++;
}
DBG("workbuff not reading, status=%d\n",
cam->workbuff->status);
continue;
}
if (cam->frame_size < cam->workbuff->length + n) {
ERR("buffer overflow! length: %d, n: %d\n",
cam->workbuff->length, n);
cam->workbuff->status = FRAME_ERROR;
if(cam->workbuff->length > cam->workbuff->max_length)
cam->workbuff->max_length =
cam->workbuff->length;
continue;
}
if (cam->workbuff->length == 0) {
int data_offset;
if ((0xD8 == cdata[0]) && (0xFF == cdata[1])) {
data_offset = 1;
} else if((0xFF == cdata[0]) && (0xD8 == cdata[1])
&& (0xFF == cdata[2])) {
data_offset = 2;
} else {
DBG("Ignoring packet, not beginning!\n");
continue;
}
DBG("Start of frame pattern found\n");
v4l2_get_timestamp(&cam->workbuff->timestamp);
cam->workbuff->seq = cam->frame_count++;
cam->workbuff->data[0] = 0xFF;
cam->workbuff->data[1] = 0xD8;
cam->workbuff->length = 2;
add_APPn(cam);
add_COM(cam);
memcpy(cam->workbuff->data+cam->workbuff->length,
cdata+data_offset, n-data_offset);
cam->workbuff->length += n-data_offset;
} else if (cam->workbuff->length > 0) {
memcpy(cam->workbuff->data + cam->workbuff->length,
cdata, n);
cam->workbuff->length += n;
}
if ((cam->workbuff->length >= 3) &&
(cam->workbuff->data[cam->workbuff->length - 3] == 0xFF) &&
(cam->workbuff->data[cam->workbuff->length - 2] == 0xD9) &&
(cam->workbuff->data[cam->workbuff->length - 1] == 0xFF)) {
frame_ready = true;
cam->workbuff->data[cam->workbuff->length - 1] = 0;
cam->workbuff->length -= 1;
} else if ((cam->workbuff->length >= 2) &&
(cam->workbuff->data[cam->workbuff->length - 2] == 0xFF) &&
(cam->workbuff->data[cam->workbuff->length - 1] == 0xD9)) {
frame_ready = true;
}
if (frame_ready) {
DBG("Workbuff image size = %d\n",cam->workbuff->length);
process_frame(cam);
frame_ready = false;
if (waitqueue_active(&cam->wq_stream))
wake_up_interruptible(&cam->wq_stream);
}
}
if(cam->streaming) {
/* resubmit */
urb->dev = cam->dev;
if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0)
ERR("%s: usb_submit_urb ret %d!\n", __func__, i);
}
}
/******************************************************************************
*
* configure_transfer_mode
*
*****************************************************************************/
static int configure_transfer_mode(struct camera_data *cam, unsigned int alt)
{
static unsigned char iso_regs[8][4] = {
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0xB9, 0x00, 0x00, 0x7E},
{0xB9, 0x00, 0x01, 0x7E},
{0xB9, 0x00, 0x02, 0x7E},
{0xB9, 0x00, 0x02, 0xFE},
{0xB9, 0x00, 0x03, 0x7E},
{0xB9, 0x00, 0x03, 0xFD}
};
struct cpia2_command cmd;
unsigned char reg;
if (!video_is_registered(&cam->vdev))
return -ENODEV;
/***
* Write the isoc registers according to the alternate selected
***/
cmd.direction = TRANSFER_WRITE;
cmd.buffer.block_data[0] = iso_regs[alt][0];
cmd.buffer.block_data[1] = iso_regs[alt][1];
cmd.buffer.block_data[2] = iso_regs[alt][2];
cmd.buffer.block_data[3] = iso_regs[alt][3];
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
cmd.start = CPIA2_VC_USB_ISOLIM;
cmd.reg_count = 4;
cpia2_send_command(cam, &cmd);
/***
* Enable relevant streams before starting polling.
* First read USB Stream Config Register.
***/
cmd.direction = TRANSFER_READ;
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
cmd.start = CPIA2_VC_USB_STRM;
cmd.reg_count = 1;
cpia2_send_command(cam, &cmd);
reg = cmd.buffer.block_data[0];
/* Clear iso, bulk, and int */
reg &= ~(CPIA2_VC_USB_STRM_BLK_ENABLE |
CPIA2_VC_USB_STRM_ISO_ENABLE |
CPIA2_VC_USB_STRM_INT_ENABLE);
if (alt == USBIF_BULK) {
DBG("Enabling bulk xfer\n");
reg |= CPIA2_VC_USB_STRM_BLK_ENABLE; /* Enable Bulk */
cam->xfer_mode = XFER_BULK;
} else if (alt >= USBIF_ISO_1) {
DBG("Enabling ISOC xfer\n");
reg |= CPIA2_VC_USB_STRM_ISO_ENABLE;
cam->xfer_mode = XFER_ISOC;
}
cmd.buffer.block_data[0] = reg;
cmd.direction = TRANSFER_WRITE;
cmd.start = CPIA2_VC_USB_STRM;
cmd.reg_count = 1;
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
cpia2_send_command(cam, &cmd);
return 0;
}
/******************************************************************************
*
* cpia2_usb_change_streaming_alternate
*
*****************************************************************************/
int cpia2_usb_change_streaming_alternate(struct camera_data *cam,
unsigned int alt)
{
int ret = 0;
if(alt < USBIF_ISO_1 || alt > USBIF_ISO_6)
return -EINVAL;
if(alt == cam->params.camera_state.stream_mode)
return 0;
cpia2_usb_stream_pause(cam);
configure_transfer_mode(cam, alt);
cam->params.camera_state.stream_mode = alt;
/* Reset the camera to prevent image quality degradation */
cpia2_reset_camera(cam);
cpia2_usb_stream_resume(cam);
return ret;
}
/******************************************************************************
*
* set_alternate
*
*****************************************************************************/
static int set_alternate(struct camera_data *cam, unsigned int alt)
{
int ret = 0;
if(alt == cam->cur_alt)
return 0;
if (cam->cur_alt != USBIF_CMDONLY) {
DBG("Changing from alt %d to %d\n", cam->cur_alt, USBIF_CMDONLY);
ret = usb_set_interface(cam->dev, cam->iface, USBIF_CMDONLY);
if (ret != 0)
return ret;
}
if (alt != USBIF_CMDONLY) {
DBG("Changing from alt %d to %d\n", USBIF_CMDONLY, alt);
ret = usb_set_interface(cam->dev, cam->iface, alt);
if (ret != 0)
return ret;
}
cam->old_alt = cam->cur_alt;
cam->cur_alt = alt;
return ret;
}
/******************************************************************************
*
* free_sbufs
*
* Free all cam->sbuf[]. All non-NULL .data and .urb members that are non-NULL
* are assumed to be allocated. Non-NULL .urb members are also assumed to be
* submitted (and must therefore be killed before they are freed).
*****************************************************************************/
static void free_sbufs(struct camera_data *cam)
{
int i;
for (i = 0; i < NUM_SBUF; i++) {
if(cam->sbuf[i].urb) {
usb_kill_urb(cam->sbuf[i].urb);
usb_free_urb(cam->sbuf[i].urb);
cam->sbuf[i].urb = NULL;
}
if(cam->sbuf[i].data) {
kfree(cam->sbuf[i].data);
cam->sbuf[i].data = NULL;
}
}
}
/*******
* Convenience functions
*******/
/****************************************************************************
*
* write_packet
*
***************************************************************************/
static int write_packet(struct usb_device *udev,
u8 request, u8 * registers, u16 start, size_t size)
{
if (!registers || size <= 0)
return -EINVAL;
return usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
start, /* value */
0, /* index */
registers, /* buffer */
size,
HZ);
}
/****************************************************************************
*
* read_packet
*
***************************************************************************/
static int read_packet(struct usb_device *udev,
u8 request, u8 * registers, u16 start, size_t size)
{
if (!registers || size <= 0)
return -EINVAL;
return usb_control_msg(udev,
usb_rcvctrlpipe(udev, 0),
request,
USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE,
start, /* value */
0, /* index */
registers, /* buffer */
size,
HZ);
}
/******************************************************************************
*
* cpia2_usb_transfer_cmd
*
*****************************************************************************/
int cpia2_usb_transfer_cmd(struct camera_data *cam,
void *registers,
u8 request, u8 start, u8 count, u8 direction)
{
int err = 0;
struct usb_device *udev = cam->dev;
if (!udev) {
ERR("%s: Internal driver error: udev is NULL\n", __func__);
return -EINVAL;
}
if (!registers) {
ERR("%s: Internal driver error: register array is NULL\n", __func__);
return -EINVAL;
}
if (direction == TRANSFER_READ) {
err = read_packet(udev, request, (u8 *)registers, start, count);
if (err > 0)
err = 0;
} else if (direction == TRANSFER_WRITE) {
err =write_packet(udev, request, (u8 *)registers, start, count);
if (err < 0) {
LOG("Control message failed, err val = %d\n", err);
LOG("Message: request = 0x%0X, start = 0x%0X\n",
request, start);
LOG("Message: count = %d, register[0] = 0x%0X\n",
count, ((unsigned char *) registers)[0]);
} else
err=0;
} else {
LOG("Unexpected first byte of direction: %d\n",
direction);
return -EINVAL;
}
if(err != 0)
LOG("Unexpected error: %d\n", err);
return err;
}
/******************************************************************************
*
* submit_urbs
*
*****************************************************************************/
static int submit_urbs(struct camera_data *cam)
{
struct urb *urb;
int fx, err, i, j;
for(i=0; i<NUM_SBUF; ++i) {
if (cam->sbuf[i].data)
continue;
cam->sbuf[i].data =
kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
if (!cam->sbuf[i].data) {
while (--i >= 0) {
kfree(cam->sbuf[i].data);
cam->sbuf[i].data = NULL;
}
return -ENOMEM;
}
}
/* We double buffer the Isoc lists, and also know the polling
* interval is every frame (1 == (1 << (bInterval -1))).
*/
for(i=0; i<NUM_SBUF; ++i) {
if(cam->sbuf[i].urb) {
continue;
}
urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
if (!urb) {
ERR("%s: usb_alloc_urb error!\n", __func__);
for (j = 0; j < i; j++)
usb_free_urb(cam->sbuf[j].urb);
return -ENOMEM;
}
cam->sbuf[i].urb = urb;
urb->dev = cam->dev;
urb->context = cam;
urb->pipe = usb_rcvisocpipe(cam->dev, 1 /*ISOC endpoint*/);
urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = cam->sbuf[i].data;
urb->complete = cpia2_usb_complete;
urb->number_of_packets = FRAMES_PER_DESC;
urb->interval = 1;
urb->transfer_buffer_length =
FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
urb->iso_frame_desc[fx].offset =
FRAME_SIZE_PER_DESC * fx;
urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
}
}
/* Queue the ISO urbs, and resubmit in the completion handler */
for(i=0; i<NUM_SBUF; ++i) {
err = usb_submit_urb(cam->sbuf[i].urb, GFP_KERNEL);
if (err) {
ERR("usb_submit_urb[%d]() = %d\n", i, err);
return err;
}
}
return 0;
}
/******************************************************************************
*
* cpia2_usb_stream_start
*
*****************************************************************************/
int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate)
{
int ret;
int old_alt;
if(cam->streaming)
return 0;
if (cam->flush) {
int i;
DBG("Flushing buffers\n");
for(i=0; i<cam->num_frames; ++i) {
cam->buffers[i].status = FRAME_EMPTY;
cam->buffers[i].length = 0;
}
cam->curbuff = &cam->buffers[0];
cam->workbuff = cam->curbuff->next;
cam->flush = false;
}
old_alt = cam->params.camera_state.stream_mode;
cam->params.camera_state.stream_mode = 0;
ret = cpia2_usb_change_streaming_alternate(cam, alternate);
if (ret < 0) {
int ret2;
ERR("cpia2_usb_change_streaming_alternate() = %d!\n", ret);
cam->params.camera_state.stream_mode = old_alt;
ret2 = set_alternate(cam, USBIF_CMDONLY);
if (ret2 < 0) {
ERR("cpia2_usb_change_streaming_alternate(%d) =%d has already "
"failed. Then tried to call "
"set_alternate(USBIF_CMDONLY) = %d.\n",
alternate, ret, ret2);
}
} else {
cam->frame_count = 0;
cam->streaming = 1;
ret = cpia2_usb_stream_resume(cam);
}
return ret;
}
/******************************************************************************
*
* cpia2_usb_stream_pause
*
*****************************************************************************/
int cpia2_usb_stream_pause(struct camera_data *cam)
{
int ret = 0;
if(cam->streaming) {
free_sbufs(cam);
ret = set_alternate(cam, USBIF_CMDONLY);
}
return ret;
}
/******************************************************************************
*
* cpia2_usb_stream_resume
*
*****************************************************************************/
int cpia2_usb_stream_resume(struct camera_data *cam)
{
int ret = 0;
if(cam->streaming) {
cam->first_image_seen = 0;
ret = set_alternate(cam, cam->params.camera_state.stream_mode);
if(ret == 0) {
/* for some reason the user effects need to be set
again when starting streaming. */
cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
cam->params.vp_params.user_effects);
ret = submit_urbs(cam);
}
}
return ret;
}
/******************************************************************************
*
* cpia2_usb_stream_stop
*
*****************************************************************************/
int cpia2_usb_stream_stop(struct camera_data *cam)
{
int ret;
ret = cpia2_usb_stream_pause(cam);
cam->streaming = 0;
configure_transfer_mode(cam, 0);
return ret;
}
/******************************************************************************
*
* cpia2_usb_probe
*
* Probe and initialize.
*****************************************************************************/
static int cpia2_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_interface_descriptor *interface;
struct camera_data *cam;
int ret;
/* A multi-config CPiA2 camera? */
if (udev->descriptor.bNumConfigurations != 1)
return -ENODEV;
interface = &intf->cur_altsetting->desc;
/* If we get to this point, we found a CPiA2 camera */
LOG("CPiA2 USB camera found\n");
cam = cpia2_init_camera_struct(intf);
if (cam == NULL)
return -ENOMEM;
cam->dev = udev;
cam->iface = interface->bInterfaceNumber;
ret = set_alternate(cam, USBIF_CMDONLY);
if (ret < 0) {
ERR("%s: usb_set_interface error (ret = %d)\n", __func__, ret);
kfree(cam);
return ret;
}
if((ret = cpia2_init_camera(cam)) < 0) {
ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret);
kfree(cam);
return ret;
}
LOG(" CPiA Version: %d.%02d (%d.%d)\n",
cam->params.version.firmware_revision_hi,
cam->params.version.firmware_revision_lo,
cam->params.version.asic_id,
cam->params.version.asic_rev);
LOG(" CPiA PnP-ID: %04x:%04x:%04x\n",
cam->params.pnp_id.vendor,
cam->params.pnp_id.product,
cam->params.pnp_id.device_revision);
LOG(" SensorID: %d.(version %d)\n",
cam->params.version.sensor_flags,
cam->params.version.sensor_rev);
usb_set_intfdata(intf, cam);
ret = cpia2_register_camera(cam);
if (ret < 0) {
ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret);
kfree(cam);
return ret;
}
return 0;
}
/******************************************************************************
*
* cpia2_disconnect
*
*****************************************************************************/
static void cpia2_usb_disconnect(struct usb_interface *intf)
{
struct camera_data *cam = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
DBG("Stopping stream\n");
cpia2_usb_stream_stop(cam);
mutex_lock(&cam->v4l2_lock);
DBG("Unregistering camera\n");
cpia2_unregister_camera(cam);
v4l2_device_disconnect(&cam->v4l2_dev);
mutex_unlock(&cam->v4l2_lock);
v4l2_device_put(&cam->v4l2_dev);
if(cam->buffers) {
DBG("Wakeup waiting processes\n");
cam->curbuff->status = FRAME_READY;
cam->curbuff->length = 0;
if (waitqueue_active(&cam->wq_stream))
wake_up_interruptible(&cam->wq_stream);
}
DBG("Releasing interface\n");
usb_driver_release_interface(&cpia2_driver, intf);
LOG("CPiA2 camera disconnected.\n");
}
static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message)
{
struct camera_data *cam = usb_get_intfdata(intf);
mutex_lock(&cam->v4l2_lock);
if (cam->streaming) {
cpia2_usb_stream_stop(cam);
cam->streaming = 1;
}
mutex_unlock(&cam->v4l2_lock);
dev_info(&intf->dev, "going into suspend..\n");
return 0;
}
/* Resume device - start device. */
static int cpia2_usb_resume(struct usb_interface *intf)
{
struct camera_data *cam = usb_get_intfdata(intf);
mutex_lock(&cam->v4l2_lock);
v4l2_ctrl_handler_setup(&cam->hdl);
if (cam->streaming) {
cam->streaming = 0;
cpia2_usb_stream_start(cam,
cam->params.camera_state.stream_mode);
}
mutex_unlock(&cam->v4l2_lock);
dev_info(&intf->dev, "coming out of suspend..\n");
return 0;
}
/******************************************************************************
*
* usb_cpia2_init
*
*****************************************************************************/
int cpia2_usb_init(void)
{
return usb_register(&cpia2_driver);
}
/******************************************************************************
*
* usb_cpia_cleanup
*
*****************************************************************************/
void cpia2_usb_cleanup(void)
{
schedule_timeout(2 * HZ);
usb_deregister(&cpia2_driver);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
config VIDEO_CX231XX
tristate "Conexant cx231xx USB video capture support"
depends on VIDEO_DEV && I2C
select VIDEO_TUNER
select VIDEO_TVEEPROM
depends on RC_CORE
select VIDEOBUF_VMALLOC
select VIDEO_CX25840
select VIDEO_CX2341X
---help---
This is a video4linux driver for Conexant 231xx USB based TV cards.
To compile this driver as a module, choose M here: the
module will be called cx231xx
config VIDEO_CX231XX_RC
bool "Conexant cx231xx Remote Controller additional support"
depends on RC_CORE
depends on VIDEO_CX231XX
default y
---help---
cx231xx hardware has a builtin RX/TX support. However, a few
designs opted to not use it, but, instead, some other hardware.
This module enables the usage of those other hardware, like the
ones used with ISDB-T boards.
On most cases, all you need for IR is mceusb module.
config VIDEO_CX231XX_ALSA
tristate "Conexant Cx231xx ALSA audio module"
depends on VIDEO_CX231XX && SND
select SND_PCM
---help---
This is an ALSA driver for Cx231xx USB based TV cards.
To compile this driver as a module, choose M here: the
module will be called cx231xx-alsa
config VIDEO_CX231XX_DVB
tristate "DVB/ATSC Support for Cx231xx based TV cards"
depends on VIDEO_CX231XX && DVB_CORE
select VIDEOBUF_DVB
select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT
select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT
select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT
select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
---help---
This adds support for DVB cards based on the
Conexant cx231xx chips.

View file

@ -0,0 +1,15 @@
cx231xx-y += cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o
cx231xx-y += cx231xx-avcore.o cx231xx-417.o cx231xx-pcb-cfg.o cx231xx-vbi.o
cx231xx-$(CONFIG_VIDEO_CX231XX_RC) += cx231xx-input.o
cx231xx-alsa-objs := cx231xx-audio.o
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o
obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o
obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o
ccflags-y += -Idrivers/media/i2c
ccflags-y += -Idrivers/media/tuners
ccflags-y += -Idrivers/media/dvb-core
ccflags-y += -Idrivers/media/dvb-frontends
ccflags-y += -Idrivers/media/usb/dvb-usb

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,775 @@
/*
* Conexant Cx231xx audio extension
*
* Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
* Based on em28xx driver
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/init.h>
#include <linux/sound.h>
#include <linux/spinlock.h>
#include <linux/soundcard.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <media/v4l2-common.h>
#include "cx231xx.h"
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
#define dprintk(fmt, arg...) do { \
if (debug) \
printk(KERN_INFO "cx231xx-audio %s: " fmt, \
__func__, ##arg); \
} while (0)
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static int cx231xx_isoc_audio_deinit(struct cx231xx *dev)
{
int i;
dprintk("Stopping isoc\n");
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
if (dev->adev.urb[i]) {
if (!irqs_disabled())
usb_kill_urb(dev->adev.urb[i]);
else
usb_unlink_urb(dev->adev.urb[i]);
usb_free_urb(dev->adev.urb[i]);
dev->adev.urb[i] = NULL;
kfree(dev->adev.transfer_buffer[i]);
dev->adev.transfer_buffer[i] = NULL;
}
}
return 0;
}
static int cx231xx_bulk_audio_deinit(struct cx231xx *dev)
{
int i;
dprintk("Stopping bulk\n");
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
if (dev->adev.urb[i]) {
if (!irqs_disabled())
usb_kill_urb(dev->adev.urb[i]);
else
usb_unlink_urb(dev->adev.urb[i]);
usb_free_urb(dev->adev.urb[i]);
dev->adev.urb[i] = NULL;
kfree(dev->adev.transfer_buffer[i]);
dev->adev.transfer_buffer[i] = NULL;
}
}
return 0;
}
static void cx231xx_audio_isocirq(struct urb *urb)
{
struct cx231xx *dev = urb->context;
int i;
unsigned int oldptr;
int period_elapsed = 0;
int status;
unsigned char *cp;
unsigned int stride;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
if (dev->state & DEV_DISCONNECTED)
return;
switch (urb->status) {
case 0: /* success */
case -ETIMEDOUT: /* NAK */
break;
case -ECONNRESET: /* kill */
case -ENOENT:
case -ESHUTDOWN:
return;
default: /* error */
dprintk("urb completition error %d.\n", urb->status);
break;
}
if (atomic_read(&dev->stream_started) == 0)
return;
if (dev->adev.capture_pcm_substream) {
substream = dev->adev.capture_pcm_substream;
runtime = substream->runtime;
stride = runtime->frame_bits >> 3;
for (i = 0; i < urb->number_of_packets; i++) {
int length = urb->iso_frame_desc[i].actual_length /
stride;
cp = (unsigned char *)urb->transfer_buffer +
urb->iso_frame_desc[i].offset;
if (!length)
continue;
oldptr = dev->adev.hwptr_done_capture;
if (oldptr + length >= runtime->buffer_size) {
unsigned int cnt;
cnt = runtime->buffer_size - oldptr;
memcpy(runtime->dma_area + oldptr * stride, cp,
cnt * stride);
memcpy(runtime->dma_area, cp + cnt * stride,
length * stride - cnt * stride);
} else {
memcpy(runtime->dma_area + oldptr * stride, cp,
length * stride);
}
snd_pcm_stream_lock(substream);
dev->adev.hwptr_done_capture += length;
if (dev->adev.hwptr_done_capture >=
runtime->buffer_size)
dev->adev.hwptr_done_capture -=
runtime->buffer_size;
dev->adev.capture_transfer_done += length;
if (dev->adev.capture_transfer_done >=
runtime->period_size) {
dev->adev.capture_transfer_done -=
runtime->period_size;
period_elapsed = 1;
}
snd_pcm_stream_unlock(substream);
}
if (period_elapsed)
snd_pcm_period_elapsed(substream);
}
urb->status = 0;
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status < 0) {
cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",
status);
}
return;
}
static void cx231xx_audio_bulkirq(struct urb *urb)
{
struct cx231xx *dev = urb->context;
unsigned int oldptr;
int period_elapsed = 0;
int status;
unsigned char *cp;
unsigned int stride;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
if (dev->state & DEV_DISCONNECTED)
return;
switch (urb->status) {
case 0: /* success */
case -ETIMEDOUT: /* NAK */
break;
case -ECONNRESET: /* kill */
case -ENOENT:
case -ESHUTDOWN:
return;
default: /* error */
dprintk("urb completition error %d.\n", urb->status);
break;
}
if (atomic_read(&dev->stream_started) == 0)
return;
if (dev->adev.capture_pcm_substream) {
substream = dev->adev.capture_pcm_substream;
runtime = substream->runtime;
stride = runtime->frame_bits >> 3;
if (1) {
int length = urb->actual_length /
stride;
cp = (unsigned char *)urb->transfer_buffer;
oldptr = dev->adev.hwptr_done_capture;
if (oldptr + length >= runtime->buffer_size) {
unsigned int cnt;
cnt = runtime->buffer_size - oldptr;
memcpy(runtime->dma_area + oldptr * stride, cp,
cnt * stride);
memcpy(runtime->dma_area, cp + cnt * stride,
length * stride - cnt * stride);
} else {
memcpy(runtime->dma_area + oldptr * stride, cp,
length * stride);
}
snd_pcm_stream_lock(substream);
dev->adev.hwptr_done_capture += length;
if (dev->adev.hwptr_done_capture >=
runtime->buffer_size)
dev->adev.hwptr_done_capture -=
runtime->buffer_size;
dev->adev.capture_transfer_done += length;
if (dev->adev.capture_transfer_done >=
runtime->period_size) {
dev->adev.capture_transfer_done -=
runtime->period_size;
period_elapsed = 1;
}
snd_pcm_stream_unlock(substream);
}
if (period_elapsed)
snd_pcm_period_elapsed(substream);
}
urb->status = 0;
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status < 0) {
cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",
status);
}
return;
}
static int cx231xx_init_audio_isoc(struct cx231xx *dev)
{
int i, errCode;
int sb_size;
cx231xx_info("%s: Starting ISO AUDIO transfers\n", __func__);
if (dev->state & DEV_DISCONNECTED)
return -ENODEV;
sb_size = CX231XX_ISO_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
struct urb *urb;
int j, k;
dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
if (!dev->adev.transfer_buffer[i])
return -ENOMEM;
memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC);
if (!urb) {
cx231xx_errdev("usb_alloc_urb failed!\n");
for (j = 0; j < i; j++) {
usb_free_urb(dev->adev.urb[j]);
kfree(dev->adev.transfer_buffer[j]);
}
return -ENOMEM;
}
urb->dev = dev->udev;
urb->context = dev;
urb->pipe = usb_rcvisocpipe(dev->udev,
dev->adev.end_point_addr);
urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = dev->adev.transfer_buffer[i];
urb->interval = 1;
urb->complete = cx231xx_audio_isocirq;
urb->number_of_packets = CX231XX_ISO_NUM_AUDIO_PACKETS;
urb->transfer_buffer_length = sb_size;
for (j = k = 0; j < CX231XX_ISO_NUM_AUDIO_PACKETS;
j++, k += dev->adev.max_pkt_size) {
urb->iso_frame_desc[j].offset = k;
urb->iso_frame_desc[j].length = dev->adev.max_pkt_size;
}
dev->adev.urb[i] = urb;
}
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
if (errCode < 0) {
cx231xx_isoc_audio_deinit(dev);
return errCode;
}
}
return errCode;
}
static int cx231xx_init_audio_bulk(struct cx231xx *dev)
{
int i, errCode;
int sb_size;
cx231xx_info("%s: Starting BULK AUDIO transfers\n", __func__);
if (dev->state & DEV_DISCONNECTED)
return -ENODEV;
sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
struct urb *urb;
int j;
dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
if (!dev->adev.transfer_buffer[i])
return -ENOMEM;
memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
if (!urb) {
cx231xx_errdev("usb_alloc_urb failed!\n");
for (j = 0; j < i; j++) {
usb_free_urb(dev->adev.urb[j]);
kfree(dev->adev.transfer_buffer[j]);
}
return -ENOMEM;
}
urb->dev = dev->udev;
urb->context = dev;
urb->pipe = usb_rcvbulkpipe(dev->udev,
dev->adev.end_point_addr);
urb->transfer_flags = 0;
urb->transfer_buffer = dev->adev.transfer_buffer[i];
urb->complete = cx231xx_audio_bulkirq;
urb->transfer_buffer_length = sb_size;
dev->adev.urb[i] = urb;
}
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
if (errCode < 0) {
cx231xx_bulk_audio_deinit(dev);
return errCode;
}
}
return errCode;
}
static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
size_t size)
{
struct snd_pcm_runtime *runtime = subs->runtime;
dprintk("Allocating vbuffer\n");
if (runtime->dma_area) {
if (runtime->dma_bytes > size)
return 0;
vfree(runtime->dma_area);
}
runtime->dma_area = vmalloc(size);
if (!runtime->dma_area)
return -ENOMEM;
runtime->dma_bytes = size;
return 0;
}
static struct snd_pcm_hardware snd_cx231xx_hw_capture = {
.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
.period_bytes_min = 64, /* 12544/2, */
.period_bytes_max = 12544,
.periods_min = 2,
.periods_max = 98, /* 12544, */
};
static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream)
{
struct cx231xx *dev = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int ret = 0;
dprintk("opening device and trying to acquire exclusive lock\n");
if (!dev) {
cx231xx_errdev("BUG: cx231xx can't find device struct."
" Can't proceed with open\n");
return -ENODEV;
}
if (dev->state & DEV_DISCONNECTED) {
cx231xx_errdev("Can't open. the device was removed.\n");
return -ENODEV;
}
/* set alternate setting for audio interface */
/* 1 - 48000 samples per sec */
mutex_lock(&dev->lock);
if (dev->USE_ISO)
ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);
else
ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
mutex_unlock(&dev->lock);
if (ret < 0) {
cx231xx_errdev("failed to set alternate setting !\n");
return ret;
}
runtime->hw = snd_cx231xx_hw_capture;
mutex_lock(&dev->lock);
/* inform hardware to start streaming */
ret = cx231xx_capture_start(dev, 1, Audio);
dev->adev.users++;
mutex_unlock(&dev->lock);
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
dev->adev.capture_pcm_substream = substream;
runtime->private_data = dev;
return 0;
}
static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream)
{
int ret;
struct cx231xx *dev = snd_pcm_substream_chip(substream);
dprintk("closing device\n");
/* inform hardware to stop streaming */
mutex_lock(&dev->lock);
ret = cx231xx_capture_start(dev, 0, Audio);
/* set alternate setting for audio interface */
/* 1 - 48000 samples per sec */
ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
if (ret < 0) {
cx231xx_errdev("failed to set alternate setting !\n");
mutex_unlock(&dev->lock);
return ret;
}
dev->adev.users--;
mutex_unlock(&dev->lock);
if (dev->adev.users == 0 && dev->adev.shutdown == 1) {
dprintk("audio users: %d\n", dev->adev.users);
dprintk("disabling audio stream!\n");
dev->adev.shutdown = 0;
dprintk("released lock\n");
if (atomic_read(&dev->stream_started) > 0) {
atomic_set(&dev->stream_started, 0);
schedule_work(&dev->wq_trigger);
}
}
return 0;
}
static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
int ret;
dprintk("Setting capture parameters\n");
ret = snd_pcm_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
#if 0
/* TODO: set up cx231xx audio chip to deliver the correct audio format,
current default is 48000hz multiplexed => 96000hz mono
which shouldn't matter since analogue TV only supports mono */
unsigned int channels, rate, format;
format = params_format(hw_params);
rate = params_rate(hw_params);
channels = params_channels(hw_params);
#endif
return ret;
}
static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream)
{
struct cx231xx *dev = snd_pcm_substream_chip(substream);
dprintk("Stop capture, if needed\n");
if (atomic_read(&dev->stream_started) > 0) {
atomic_set(&dev->stream_started, 0);
schedule_work(&dev->wq_trigger);
}
return 0;
}
static int snd_cx231xx_prepare(struct snd_pcm_substream *substream)
{
struct cx231xx *dev = snd_pcm_substream_chip(substream);
dev->adev.hwptr_done_capture = 0;
dev->adev.capture_transfer_done = 0;
return 0;
}
static void audio_trigger(struct work_struct *work)
{
struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger);
if (atomic_read(&dev->stream_started)) {
dprintk("starting capture");
if (is_fw_load(dev) == 0)
cx25840_call(dev, core, load_fw);
if (dev->USE_ISO)
cx231xx_init_audio_isoc(dev);
else
cx231xx_init_audio_bulk(dev);
} else {
dprintk("stopping capture");
cx231xx_isoc_audio_deinit(dev);
}
}
static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct cx231xx *dev = snd_pcm_substream_chip(substream);
int retval = 0;
if (dev->state & DEV_DISCONNECTED)
return -ENODEV;
spin_lock(&dev->adev.slock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
atomic_set(&dev->stream_started, 1);
break;
case SNDRV_PCM_TRIGGER_STOP:
atomic_set(&dev->stream_started, 0);
break;
default:
retval = -EINVAL;
break;
}
spin_unlock(&dev->adev.slock);
schedule_work(&dev->wq_trigger);
return retval;
}
static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream
*substream)
{
struct cx231xx *dev;
unsigned long flags;
snd_pcm_uframes_t hwptr_done;
dev = snd_pcm_substream_chip(substream);
spin_lock_irqsave(&dev->adev.slock, flags);
hwptr_done = dev->adev.hwptr_done_capture;
spin_unlock_irqrestore(&dev->adev.slock, flags);
return hwptr_done;
}
static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
unsigned long offset)
{
void *pageptr = subs->runtime->dma_area + offset;
return vmalloc_to_page(pageptr);
}
static struct snd_pcm_ops snd_cx231xx_pcm_capture = {
.open = snd_cx231xx_capture_open,
.close = snd_cx231xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cx231xx_hw_capture_params,
.hw_free = snd_cx231xx_hw_capture_free,
.prepare = snd_cx231xx_prepare,
.trigger = snd_cx231xx_capture_trigger,
.pointer = snd_cx231xx_capture_pointer,
.page = snd_pcm_get_vmalloc_page,
};
static int cx231xx_audio_init(struct cx231xx *dev)
{
struct cx231xx_audio *adev = &dev->adev;
struct snd_pcm *pcm;
struct snd_card *card;
static int devnr;
int err;
struct usb_interface *uif;
int i, isoc_pipe = 0;
if (dev->has_alsa_audio != 1) {
/* This device does not support the extension (in this case
the device is expecting the snd-usb-audio module or
doesn't have analog audio support at all) */
return 0;
}
cx231xx_info("cx231xx-audio.c: probing for cx231xx "
"non standard usbaudio\n");
err = snd_card_new(&dev->udev->dev, index[devnr], "Cx231xx Audio",
THIS_MODULE, 0, &card);
if (err < 0)
return err;
spin_lock_init(&adev->slock);
err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
if (err < 0) {
snd_card_free(card);
return err;
}
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_cx231xx_pcm_capture);
pcm->info_flags = 0;
pcm->private_data = dev;
strcpy(pcm->name, "Conexant cx231xx Capture");
strcpy(card->driver, "Cx231xx-Audio");
strcpy(card->shortname, "Cx231xx Audio");
strcpy(card->longname, "Conexant cx231xx Audio");
INIT_WORK(&dev->wq_trigger, audio_trigger);
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
return err;
}
adev->sndcard = card;
adev->udev = dev->udev;
/* compute alternate max packet sizes for Audio */
uif =
dev->udev->actconfig->interface[dev->current_pcb_config.
hs_config_info[0].interface_info.
audio_index + 1];
adev->end_point_addr =
uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress;
adev->num_alt = uif->num_altsetting;
cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
adev->end_point_addr, adev->num_alt);
adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL);
if (adev->alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n");
return -ENOMEM;
}
for (i = 0; i < adev->num_alt; i++) {
u16 tmp =
le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
wMaxPacketSize);
adev->alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
cx231xx_info("Alternate setting %i, max size= %i\n", i,
adev->alt_max_pkt_size[i]);
}
return 0;
}
static int cx231xx_audio_fini(struct cx231xx *dev)
{
if (dev == NULL)
return 0;
if (dev->has_alsa_audio != 1) {
/* This device does not support the extension (in this case
the device is expecting the snd-usb-audio module or
doesn't have analog audio support at all) */
return 0;
}
if (dev->adev.sndcard) {
snd_card_free(dev->adev.sndcard);
kfree(dev->adev.alt_max_pkt_size);
dev->adev.sndcard = NULL;
}
return 0;
}
static struct cx231xx_ops audio_ops = {
.id = CX231XX_AUDIO,
.name = "Cx231xx Audio Extension",
.init = cx231xx_audio_init,
.fini = cx231xx_audio_fini,
};
static int __init cx231xx_alsa_register(void)
{
return cx231xx_register_extension(&audio_ops);
}
static void __exit cx231xx_alsa_unregister(void)
{
cx231xx_unregister_extension(&audio_ops);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
MODULE_DESCRIPTION("Cx231xx Audio driver");
module_init(cx231xx_alsa_register);
module_exit(cx231xx_alsa_unregister);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,495 @@
/*
cx231xx_conf-reg.h - driver for Conexant Cx23100/101/102 USB
video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _POLARIS_REG_H_
#define _POLARIS_REG_H_
#define BOARD_CFG_STAT 0x0
#define TS_MODE_REG 0x4
#define TS1_CFG_REG 0x8
#define TS1_LENGTH_REG 0xc
#define TS2_CFG_REG 0x10
#define TS2_LENGTH_REG 0x14
#define EP_MODE_SET 0x18
#define CIR_PWR_PTN1 0x1c
#define CIR_PWR_PTN2 0x20
#define CIR_PWR_PTN3 0x24
#define CIR_PWR_MASK0 0x28
#define CIR_PWR_MASK1 0x2c
#define CIR_PWR_MASK2 0x30
#define CIR_GAIN 0x34
#define CIR_CAR_REG 0x38
#define CIR_OT_CFG1 0x40
#define CIR_OT_CFG2 0x44
#define GBULK_BIT_EN 0x68
#define PWR_CTL_EN 0x74
/* Polaris Endpoints capture mask for register EP_MODE_SET */
#define ENABLE_EP1 0x01 /* Bit[0]=1 */
#define ENABLE_EP2 0x02 /* Bit[1]=1 */
#define ENABLE_EP3 0x04 /* Bit[2]=1 */
#define ENABLE_EP4 0x08 /* Bit[3]=1 */
#define ENABLE_EP5 0x10 /* Bit[4]=1 */
#define ENABLE_EP6 0x20 /* Bit[5]=1 */
/* Bit definition for register PWR_CTL_EN */
#define PWR_MODE_MASK 0x17f
#define PWR_AV_EN 0x08 /* bit3 */
#define PWR_ISO_EN 0x40 /* bit6 */
#define PWR_AV_MODE 0x30 /* bit4,5 */
#define PWR_TUNER_EN 0x04 /* bit2 */
#define PWR_DEMOD_EN 0x02 /* bit1 */
#define I2C_DEMOD_EN 0x01 /* bit0 */
#define PWR_RESETOUT_EN 0x100 /* bit8 */
enum AV_MODE{
POLARIS_AVMODE_DEFAULT = 0,
POLARIS_AVMODE_DIGITAL = 0x10,
POLARIS_AVMODE_ANALOGT_TV = 0x20,
POLARIS_AVMODE_ENXTERNAL_AV = 0x30,
};
/* Colibri Registers */
#define SINGLE_ENDED 0x0
#define LOW_IF 0x4
#define EU_IF 0x9
#define US_IF 0xa
#define SUP_BLK_TUNE1 0x00
#define SUP_BLK_TUNE2 0x01
#define SUP_BLK_TUNE3 0x02
#define SUP_BLK_XTAL 0x03
#define SUP_BLK_PLL1 0x04
#define SUP_BLK_PLL2 0x05
#define SUP_BLK_PLL3 0x06
#define SUP_BLK_REF 0x07
#define SUP_BLK_PWRDN 0x08
#define SUP_BLK_TESTPAD 0x09
#define ADC_COM_INT5_STAB_REF 0x0a
#define ADC_COM_QUANT 0x0b
#define ADC_COM_BIAS1 0x0c
#define ADC_COM_BIAS2 0x0d
#define ADC_COM_BIAS3 0x0e
#define TESTBUS_CTRL 0x12
#define FLD_PWRDN_TUNING_BIAS 0x10
#define FLD_PWRDN_ENABLE_PLL 0x08
#define FLD_PWRDN_PD_BANDGAP 0x04
#define FLD_PWRDN_PD_BIAS 0x02
#define FLD_PWRDN_PD_TUNECK 0x01
#define ADC_STATUS_CH1 0x20
#define ADC_STATUS_CH2 0x40
#define ADC_STATUS_CH3 0x60
#define ADC_STATUS2_CH1 0x21
#define ADC_STATUS2_CH2 0x41
#define ADC_STATUS2_CH3 0x61
#define ADC_CAL_ATEST_CH1 0x22
#define ADC_CAL_ATEST_CH2 0x42
#define ADC_CAL_ATEST_CH3 0x62
#define ADC_PWRDN_CLAMP_CH1 0x23
#define ADC_PWRDN_CLAMP_CH2 0x43
#define ADC_PWRDN_CLAMP_CH3 0x63
#define ADC_CTRL_DAC23_CH1 0x24
#define ADC_CTRL_DAC23_CH2 0x44
#define ADC_CTRL_DAC23_CH3 0x64
#define ADC_CTRL_DAC1_CH1 0x25
#define ADC_CTRL_DAC1_CH2 0x45
#define ADC_CTRL_DAC1_CH3 0x65
#define ADC_DCSERVO_DEM_CH1 0x26
#define ADC_DCSERVO_DEM_CH2 0x46
#define ADC_DCSERVO_DEM_CH3 0x66
#define ADC_FB_FRCRST_CH1 0x27
#define ADC_FB_FRCRST_CH2 0x47
#define ADC_FB_FRCRST_CH3 0x67
#define ADC_INPUT_CH1 0x28
#define ADC_INPUT_CH2 0x48
#define ADC_INPUT_CH3 0x68
#define INPUT_SEL_MASK 0x30 /* [5:4] in_sel */
#define ADC_NTF_PRECLMP_EN_CH1 0x29
#define ADC_NTF_PRECLMP_EN_CH2 0x49
#define ADC_NTF_PRECLMP_EN_CH3 0x69
#define ADC_QGAIN_RES_TRM_CH1 0x2a
#define ADC_QGAIN_RES_TRM_CH2 0x4a
#define ADC_QGAIN_RES_TRM_CH3 0x6a
#define ADC_SOC_PRECLMP_TERM_CH1 0x2b
#define ADC_SOC_PRECLMP_TERM_CH2 0x4b
#define ADC_SOC_PRECLMP_TERM_CH3 0x6b
#define TESTBUS_CTRL_CH1 0x32
#define TESTBUS_CTRL_CH2 0x52
#define TESTBUS_CTRL_CH3 0x72
/******************************************************************************
* DIF registers *
******************************************************************************/
#define DIRECT_IF_REVB_BASE 0x00300
/*****************************************************************************/
#define DIF_PLL_FREQ_WORD (DIRECT_IF_REVB_BASE + 0x00000000)
/*****************************************************************************/
#define FLD_DIF_PLL_LOCK 0x80000000
/* Reserved [30:29] */
#define FLD_DIF_PLL_FREE_RUN 0x10000000
#define FLD_DIF_PLL_FREQ 0x0fffffff
/*****************************************************************************/
#define DIF_PLL_CTRL (DIRECT_IF_REVB_BASE + 0x00000004)
/*****************************************************************************/
#define FLD_DIF_KD_PD 0xff000000
/* Reserved [23:20] */
#define FLD_DIF_KDS_PD 0x000f0000
#define FLD_DIF_KI_PD 0x0000ff00
/* Reserved [7:4] */
#define FLD_DIF_KIS_PD 0x0000000f
/*****************************************************************************/
#define DIF_PLL_CTRL1 (DIRECT_IF_REVB_BASE + 0x00000008)
/*****************************************************************************/
#define FLD_DIF_KD_FD 0xff000000
/* Reserved [23:20] */
#define FLD_DIF_KDS_FD 0x000f0000
#define FLD_DIF_KI_FD 0x0000ff00
#define FLD_DIF_SIG_PROP_SZ 0x000000f0
#define FLD_DIF_KIS_FD 0x0000000f
/*****************************************************************************/
#define DIF_PLL_CTRL2 (DIRECT_IF_REVB_BASE + 0x0000000c)
/*****************************************************************************/
#define FLD_DIF_PLL_AGC_REF 0xfff00000
#define FLD_DIF_PLL_AGC_KI 0x000f0000
/* Reserved [15] */
#define FLD_DIF_FREQ_LIMIT 0x00007000
#define FLD_DIF_K_FD 0x00000f00
#define FLD_DIF_DOWNSMPL_FD 0x000000ff
/*****************************************************************************/
#define DIF_PLL_CTRL3 (DIRECT_IF_REVB_BASE + 0x00000010)
/*****************************************************************************/
/* Reserved [31:16] */
#define FLD_DIF_PLL_AGC_EN 0x00008000
/* Reserved [14:12] */
#define FLD_DIF_PLL_MAN_GAIN 0x00000fff
/*****************************************************************************/
#define DIF_AGC_IF_REF (DIRECT_IF_REVB_BASE + 0x00000014)
/*****************************************************************************/
#define FLD_DIF_K_AGC_RF 0xf0000000
#define FLD_DIF_K_AGC_IF 0x0f000000
#define FLD_DIF_K_AGC_INT 0x00f00000
/* Reserved [19:12] */
#define FLD_DIF_IF_REF 0x00000fff
/*****************************************************************************/
#define DIF_AGC_CTRL_IF (DIRECT_IF_REVB_BASE + 0x00000018)
/*****************************************************************************/
#define FLD_DIF_IF_MAX 0xff000000
#define FLD_DIF_IF_MIN 0x00ff0000
#define FLD_DIF_IF_AGC 0x0000ffff
/*****************************************************************************/
#define DIF_AGC_CTRL_INT (DIRECT_IF_REVB_BASE + 0x0000001c)
/*****************************************************************************/
#define FLD_DIF_INT_MAX 0xff000000
#define FLD_DIF_INT_MIN 0x00ff0000
#define FLD_DIF_INT_AGC 0x0000ffff
/*****************************************************************************/
#define DIF_AGC_CTRL_RF (DIRECT_IF_REVB_BASE + 0x00000020)
/*****************************************************************************/
#define FLD_DIF_RF_MAX 0xff000000
#define FLD_DIF_RF_MIN 0x00ff0000
#define FLD_DIF_RF_AGC 0x0000ffff
/*****************************************************************************/
#define DIF_AGC_IF_INT_CURRENT (DIRECT_IF_REVB_BASE + 0x00000024)
/*****************************************************************************/
#define FLD_DIF_IF_AGC_IN 0xffff0000
#define FLD_DIF_INT_AGC_IN 0x0000ffff
/*****************************************************************************/
#define DIF_AGC_RF_CURRENT (DIRECT_IF_REVB_BASE + 0x00000028)
/*****************************************************************************/
/* Reserved [31:16] */
#define FLD_DIF_RF_AGC_IN 0x0000ffff
/*****************************************************************************/
#define DIF_VIDEO_AGC_CTRL (DIRECT_IF_REVB_BASE + 0x0000002c)
/*****************************************************************************/
#define FLD_DIF_AFD 0xc0000000
#define FLD_DIF_K_VID_AGC 0x30000000
#define FLD_DIF_LINE_LENGTH 0x0fff0000
#define FLD_DIF_AGC_GAIN 0x0000ffff
/*****************************************************************************/
#define DIF_VID_AUD_OVERRIDE (DIRECT_IF_REVB_BASE + 0x00000030)
/*****************************************************************************/
#define FLD_DIF_AUDIO_AGC_OVERRIDE 0x80000000
/* Reserved [30:30] */
#define FLD_DIF_AUDIO_MAN_GAIN 0x3f000000
/* Reserved [23:17] */
#define FLD_DIF_VID_AGC_OVERRIDE 0x00010000
#define FLD_DIF_VID_MAN_GAIN 0x0000ffff
/*****************************************************************************/
#define DIF_AV_SEP_CTRL (DIRECT_IF_REVB_BASE + 0x00000034)
/*****************************************************************************/
#define FLD_DIF_LPF_FREQ 0xc0000000
#define FLD_DIF_AV_PHASE_INC 0x3f000000
#define FLD_DIF_AUDIO_FREQ 0x00ffffff
/*****************************************************************************/
#define DIF_COMP_FLT_CTRL (DIRECT_IF_REVB_BASE + 0x00000038)
/*****************************************************************************/
/* Reserved [31:24] */
#define FLD_DIF_IIR23_R2 0x00ff0000
#define FLD_DIF_IIR23_R1 0x0000ff00
#define FLD_DIF_IIR1_R1 0x000000ff
/*****************************************************************************/
#define DIF_MISC_CTRL (DIRECT_IF_REVB_BASE + 0x0000003c)
/*****************************************************************************/
#define FLD_DIF_DIF_BYPASS 0x80000000
#define FLD_DIF_FM_NYQ_GAIN 0x40000000
#define FLD_DIF_RF_AGC_ENA 0x20000000
#define FLD_DIF_INT_AGC_ENA 0x10000000
#define FLD_DIF_IF_AGC_ENA 0x08000000
#define FLD_DIF_FORCE_RF_IF_LOCK 0x04000000
#define FLD_DIF_VIDEO_AGC_ENA 0x02000000
#define FLD_DIF_RF_AGC_INV 0x01000000
#define FLD_DIF_INT_AGC_INV 0x00800000
#define FLD_DIF_IF_AGC_INV 0x00400000
#define FLD_DIF_SPEC_INV 0x00200000
#define FLD_DIF_AUD_FULL_BW 0x00100000
#define FLD_DIF_AUD_SRC_SEL 0x00080000
/* Reserved [18] */
#define FLD_DIF_IF_FREQ 0x00030000
/* Reserved [15:14] */
#define FLD_DIF_TIP_OFFSET 0x00003f00
/* Reserved [7:5] */
#define FLD_DIF_DITHER_ENA 0x00000010
/* Reserved [3:1] */
#define FLD_DIF_RF_IF_LOCK 0x00000001
/*****************************************************************************/
#define DIF_SRC_PHASE_INC (DIRECT_IF_REVB_BASE + 0x00000040)
/*****************************************************************************/
/* Reserved [31:29] */
#define FLD_DIF_PHASE_INC 0x1fffffff
/*****************************************************************************/
#define DIF_SRC_GAIN_CONTROL (DIRECT_IF_REVB_BASE + 0x00000044)
/*****************************************************************************/
/* Reserved [31:16] */
#define FLD_DIF_SRC_KI 0x0000ff00
#define FLD_DIF_SRC_KD 0x000000ff
/*****************************************************************************/
#define DIF_BPF_COEFF01 (DIRECT_IF_REVB_BASE + 0x00000048)
/*****************************************************************************/
/* Reserved [31:19] */
#define FLD_DIF_BPF_COEFF_0 0x00070000
/* Reserved [15:4] */
#define FLD_DIF_BPF_COEFF_1 0x0000000f
/*****************************************************************************/
#define DIF_BPF_COEFF23 (DIRECT_IF_REVB_BASE + 0x0000004c)
/*****************************************************************************/
/* Reserved [31:22] */
#define FLD_DIF_BPF_COEFF_2 0x003f0000
/* Reserved [15:7] */
#define FLD_DIF_BPF_COEFF_3 0x0000007f
/*****************************************************************************/
#define DIF_BPF_COEFF45 (DIRECT_IF_REVB_BASE + 0x00000050)
/*****************************************************************************/
/* Reserved [31:24] */
#define FLD_DIF_BPF_COEFF_4 0x00ff0000
/* Reserved [15:8] */
#define FLD_DIF_BPF_COEFF_5 0x000000ff
/*****************************************************************************/
#define DIF_BPF_COEFF67 (DIRECT_IF_REVB_BASE + 0x00000054)
/*****************************************************************************/
/* Reserved [31:25] */
#define FLD_DIF_BPF_COEFF_6 0x01ff0000
/* Reserved [15:9] */
#define FLD_DIF_BPF_COEFF_7 0x000001ff
/*****************************************************************************/
#define DIF_BPF_COEFF89 (DIRECT_IF_REVB_BASE + 0x00000058)
/*****************************************************************************/
/* Reserved [31:26] */
#define FLD_DIF_BPF_COEFF_8 0x03ff0000
/* Reserved [15:10] */
#define FLD_DIF_BPF_COEFF_9 0x000003ff
/*****************************************************************************/
#define DIF_BPF_COEFF1011 (DIRECT_IF_REVB_BASE + 0x0000005c)
/*****************************************************************************/
/* Reserved [31:27] */
#define FLD_DIF_BPF_COEFF_10 0x07ff0000
/* Reserved [15:11] */
#define FLD_DIF_BPF_COEFF_11 0x000007ff
/*****************************************************************************/
#define DIF_BPF_COEFF1213 (DIRECT_IF_REVB_BASE + 0x00000060)
/*****************************************************************************/
/* Reserved [31:27] */
#define FLD_DIF_BPF_COEFF_12 0x07ff0000
/* Reserved [15:12] */
#define FLD_DIF_BPF_COEFF_13 0x00000fff
/*****************************************************************************/
#define DIF_BPF_COEFF1415 (DIRECT_IF_REVB_BASE + 0x00000064)
/*****************************************************************************/
/* Reserved [31:28] */
#define FLD_DIF_BPF_COEFF_14 0x0fff0000
/* Reserved [15:12] */
#define FLD_DIF_BPF_COEFF_15 0x00000fff
/*****************************************************************************/
#define DIF_BPF_COEFF1617 (DIRECT_IF_REVB_BASE + 0x00000068)
/*****************************************************************************/
/* Reserved [31:29] */
#define FLD_DIF_BPF_COEFF_16 0x1fff0000
/* Reserved [15:13] */
#define FLD_DIF_BPF_COEFF_17 0x00001fff
/*****************************************************************************/
#define DIF_BPF_COEFF1819 (DIRECT_IF_REVB_BASE + 0x0000006c)
/*****************************************************************************/
/* Reserved [31:29] */
#define FLD_DIF_BPF_COEFF_18 0x1fff0000
/* Reserved [15:13] */
#define FLD_DIF_BPF_COEFF_19 0x00001fff
/*****************************************************************************/
#define DIF_BPF_COEFF2021 (DIRECT_IF_REVB_BASE + 0x00000070)
/*****************************************************************************/
/* Reserved [31:29] */
#define FLD_DIF_BPF_COEFF_20 0x1fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_21 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF2223 (DIRECT_IF_REVB_BASE + 0x00000074)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_22 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_23 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF2425 (DIRECT_IF_REVB_BASE + 0x00000078)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_24 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_25 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF2627 (DIRECT_IF_REVB_BASE + 0x0000007c)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_26 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_27 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF2829 (DIRECT_IF_REVB_BASE + 0x00000080)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_28 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_29 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF3031 (DIRECT_IF_REVB_BASE + 0x00000084)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_30 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_31 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF3233 (DIRECT_IF_REVB_BASE + 0x00000088)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_32 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_33 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF3435 (DIRECT_IF_REVB_BASE + 0x0000008c)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_34 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_35 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF36 (DIRECT_IF_REVB_BASE + 0x00000090)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_36 0x3fff0000
/* Reserved [15:0] */
/*****************************************************************************/
#define DIF_RPT_VARIANCE (DIRECT_IF_REVB_BASE + 0x00000094)
/*****************************************************************************/
/* Reserved [31:20] */
#define FLD_DIF_RPT_VARIANCE 0x000fffff
/*****************************************************************************/
#define DIF_SOFT_RST_CTRL_REVB (DIRECT_IF_REVB_BASE + 0x00000098)
/*****************************************************************************/
/* Reserved [31:8] */
#define FLD_DIF_DIF_SOFT_RST 0x00000080
#define FLD_DIF_DIF_REG_RST_MSK 0x00000040
#define FLD_DIF_AGC_RST_MSK 0x00000020
#define FLD_DIF_CMP_RST_MSK 0x00000010
#define FLD_DIF_AVS_RST_MSK 0x00000008
#define FLD_DIF_NYQ_RST_MSK 0x00000004
#define FLD_DIF_DIF_SRC_RST_MSK 0x00000002
#define FLD_DIF_PLL_RST_MSK 0x00000001
/*****************************************************************************/
#define DIF_PLL_FREQ_ERR (DIRECT_IF_REVB_BASE + 0x0000009c)
/*****************************************************************************/
/* Reserved [31:25] */
#define FLD_DIF_CTL_IP 0x01ffffff
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,900 @@
/*
DVB device driver for cx231xx
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include "cx231xx.h"
#include <media/v4l2-common.h>
#include <media/videobuf-vmalloc.h>
#include "xc5000.h"
#include "s5h1432.h"
#include "tda18271.h"
#include "s5h1411.h"
#include "lgdt3305.h"
#include "si2165.h"
#include "mb86a20s.h"
#include "si2157.h"
MODULE_DESCRIPTION("driver for cx231xx based DVB cards");
MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
MODULE_LICENSE("GPL");
static unsigned int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages [dvb]");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define dprintk(level, fmt, arg...) do { \
if (debug >= level) \
printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
} while (0)
#define CX231XX_DVB_NUM_BUFS 5
#define CX231XX_DVB_MAX_PACKETSIZE 564
#define CX231XX_DVB_MAX_PACKETS 64
struct cx231xx_dvb {
struct dvb_frontend *frontend;
/* feed count management */
struct mutex lock;
int nfeeds;
/* general boilerplate stuff */
struct dvb_adapter adapter;
struct dvb_demux demux;
struct dmxdev dmxdev;
struct dmx_frontend fe_hw;
struct dmx_frontend fe_mem;
struct dvb_net net;
struct i2c_client *i2c_client_tuner;
};
static struct s5h1432_config dvico_s5h1432_config = {
.output_mode = S5H1432_SERIAL_OUTPUT,
.gpio = S5H1432_GPIO_ON,
.qam_if = S5H1432_IF_4000,
.vsb_if = S5H1432_IF_4000,
.inversion = S5H1432_INVERSION_OFF,
.status_mode = S5H1432_DEMODLOCKING,
.mpeg_timing = S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
};
static struct tda18271_std_map cnxt_rde253s_tda18271_std_map = {
.dvbt_6 = { .if_freq = 4000, .agc_mode = 3, .std = 4,
.if_lvl = 1, .rfagc_top = 0x37, },
.dvbt_7 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
.if_lvl = 1, .rfagc_top = 0x37, },
.dvbt_8 = { .if_freq = 4000, .agc_mode = 3, .std = 6,
.if_lvl = 1, .rfagc_top = 0x37, },
};
static struct tda18271_std_map mb86a20s_tda18271_config = {
.dvbt_6 = { .if_freq = 4000, .agc_mode = 3, .std = 4,
.if_lvl = 0, .rfagc_top = 0x37, },
};
static struct tda18271_config cnxt_rde253s_tunerconfig = {
.std_map = &cnxt_rde253s_tda18271_std_map,
.gate = TDA18271_GATE_ANALOG,
};
static struct s5h1411_config tda18271_s5h1411_config = {
.output_mode = S5H1411_SERIAL_OUTPUT,
.gpio = S5H1411_GPIO_OFF,
.vsb_if = S5H1411_IF_3250,
.qam_if = S5H1411_IF_4000,
.inversion = S5H1411_INVERSION_ON,
.status_mode = S5H1411_DEMODLOCKING,
.mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
};
static struct s5h1411_config xc5000_s5h1411_config = {
.output_mode = S5H1411_SERIAL_OUTPUT,
.gpio = S5H1411_GPIO_OFF,
.vsb_if = S5H1411_IF_3250,
.qam_if = S5H1411_IF_3250,
.inversion = S5H1411_INVERSION_OFF,
.status_mode = S5H1411_DEMODLOCKING,
.mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
};
static struct lgdt3305_config hcw_lgdt3305_config = {
.i2c_addr = 0x0e,
.mpeg_mode = LGDT3305_MPEG_SERIAL,
.tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
.tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
.deny_i2c_rptr = 1,
.spectral_inversion = 1,
.qam_if_khz = 4000,
.vsb_if_khz = 3250,
};
static struct tda18271_std_map hauppauge_tda18271_std_map = {
.atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4,
.if_lvl = 1, .rfagc_top = 0x58, },
.qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
.if_lvl = 1, .rfagc_top = 0x58, },
};
static struct tda18271_config hcw_tda18271_config = {
.std_map = &hauppauge_tda18271_std_map,
.gate = TDA18271_GATE_DIGITAL,
};
static const struct mb86a20s_config pv_mb86a20s_config = {
.demod_address = 0x10,
.is_serial = true,
};
static struct tda18271_config pv_tda18271_config = {
.std_map = &mb86a20s_tda18271_config,
.gate = TDA18271_GATE_DIGITAL,
.small_i2c = TDA18271_03_BYTE_CHUNK_INIT,
};
static const struct si2165_config hauppauge_930C_HD_1113xx_si2165_config = {
.i2c_addr = 0x64,
.chip_mode = SI2165_MODE_PLL_XTAL,
.ref_freq_Hz = 16000000,
};
static const struct si2165_config pctv_quatro_stick_1114xx_si2165_config = {
.i2c_addr = 0x64,
.chip_mode = SI2165_MODE_PLL_EXT,
.ref_freq_Hz = 24000000,
};
static inline void print_err_status(struct cx231xx *dev, int packet, int status)
{
char *errmsg = "Unknown";
switch (status) {
case -ENOENT:
errmsg = "unlinked synchronuously";
break;
case -ECONNRESET:
errmsg = "unlinked asynchronuously";
break;
case -ENOSR:
errmsg = "Buffer error (overrun)";
break;
case -EPIPE:
errmsg = "Stalled (device not responding)";
break;
case -EOVERFLOW:
errmsg = "Babble (bad cable?)";
break;
case -EPROTO:
errmsg = "Bit-stuff error (bad cable?)";
break;
case -EILSEQ:
errmsg = "CRC/Timeout (could be anything)";
break;
case -ETIME:
errmsg = "Device does not respond";
break;
}
if (packet < 0) {
dprintk(1, "URB status %d [%s].\n", status, errmsg);
} else {
dprintk(1, "URB packet %d, status %d [%s].\n",
packet, status, errmsg);
}
}
static inline int dvb_isoc_copy(struct cx231xx *dev, struct urb *urb)
{
int i;
if (!dev)
return 0;
if (dev->state & DEV_DISCONNECTED)
return 0;
if (urb->status < 0) {
print_err_status(dev, -1, urb->status);
if (urb->status == -ENOENT)
return 0;
}
for (i = 0; i < urb->number_of_packets; i++) {
int status = urb->iso_frame_desc[i].status;
if (status < 0) {
print_err_status(dev, i, status);
if (urb->iso_frame_desc[i].status != -EPROTO)
continue;
}
dvb_dmx_swfilter(&dev->dvb->demux,
urb->transfer_buffer +
urb->iso_frame_desc[i].offset,
urb->iso_frame_desc[i].actual_length);
}
return 0;
}
static inline int dvb_bulk_copy(struct cx231xx *dev, struct urb *urb)
{
if (!dev)
return 0;
if (dev->state & DEV_DISCONNECTED)
return 0;
if (urb->status < 0) {
print_err_status(dev, -1, urb->status);
if (urb->status == -ENOENT)
return 0;
}
/* Feed the transport payload into the kernel demux */
dvb_dmx_swfilter(&dev->dvb->demux,
urb->transfer_buffer, urb->actual_length);
return 0;
}
static int start_streaming(struct cx231xx_dvb *dvb)
{
int rc;
struct cx231xx *dev = dvb->adapter.priv;
if (dev->USE_ISO) {
cx231xx_info("DVB transfer mode is ISO.\n");
mutex_lock(&dev->i2c_lock);
cx231xx_enable_i2c_port_3(dev, false);
cx231xx_set_alt_setting(dev, INDEX_TS1, 4);
cx231xx_enable_i2c_port_3(dev, true);
mutex_unlock(&dev->i2c_lock);
rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
if (rc < 0)
return rc;
dev->mode_tv = 1;
return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS,
CX231XX_DVB_NUM_BUFS,
dev->ts1_mode.max_pkt_size,
dvb_isoc_copy);
} else {
cx231xx_info("DVB transfer mode is BULK.\n");
cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
if (rc < 0)
return rc;
dev->mode_tv = 1;
return cx231xx_init_bulk(dev, CX231XX_DVB_MAX_PACKETS,
CX231XX_DVB_NUM_BUFS,
dev->ts1_mode.max_pkt_size,
dvb_bulk_copy);
}
}
static int stop_streaming(struct cx231xx_dvb *dvb)
{
struct cx231xx *dev = dvb->adapter.priv;
if (dev->USE_ISO)
cx231xx_uninit_isoc(dev);
else
cx231xx_uninit_bulk(dev);
cx231xx_set_mode(dev, CX231XX_SUSPEND);
return 0;
}
static int start_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct cx231xx_dvb *dvb = demux->priv;
int rc, ret;
if (!demux->dmx.frontend)
return -EINVAL;
mutex_lock(&dvb->lock);
dvb->nfeeds++;
rc = dvb->nfeeds;
if (dvb->nfeeds == 1) {
ret = start_streaming(dvb);
if (ret < 0)
rc = ret;
}
mutex_unlock(&dvb->lock);
return rc;
}
static int stop_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct cx231xx_dvb *dvb = demux->priv;
int err = 0;
mutex_lock(&dvb->lock);
dvb->nfeeds--;
if (0 == dvb->nfeeds)
err = stop_streaming(dvb);
mutex_unlock(&dvb->lock);
return err;
}
/* ------------------------------------------------------------------ */
static int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
{
struct cx231xx *dev = fe->dvb->priv;
if (acquire)
return cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
else
return cx231xx_set_mode(dev, CX231XX_SUSPEND);
}
/* ------------------------------------------------------------------ */
static struct xc5000_config cnxt_rde250_tunerconfig = {
.i2c_address = 0x61,
.if_khz = 4000,
};
static struct xc5000_config cnxt_rdu250_tunerconfig = {
.i2c_address = 0x61,
.if_khz = 3250,
};
/* ------------------------------------------------------------------ */
#if 0
static int attach_xc5000(u8 addr, struct cx231xx *dev)
{
struct dvb_frontend *fe;
struct xc5000_config cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.i2c_adap = &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap;
cfg.i2c_addr = addr;
if (!dev->dvb->frontend) {
printk(KERN_ERR "%s/2: dvb frontend not attached. "
"Can't attach xc5000\n", dev->name);
return -EINVAL;
}
fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg);
if (!fe) {
printk(KERN_ERR "%s/2: xc5000 attach failed\n", dev->name);
dvb_frontend_detach(dev->dvb->frontend);
dev->dvb->frontend = NULL;
return -EINVAL;
}
printk(KERN_INFO "%s/2: xc5000 attached\n", dev->name);
return 0;
}
#endif
int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq)
{
if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
if (dops->set_analog_params != NULL) {
struct analog_parameters params;
params.frequency = freq;
params.std = dev->norm;
params.mode = 0; /* 0- Air; 1 - cable */
/*params.audmode = ; */
/* Set the analog parameters to set the frequency */
dops->set_analog_params(dev->dvb->frontend, &params);
}
}
return 0;
}
int cx231xx_reset_analog_tuner(struct cx231xx *dev)
{
int status = 0;
if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
if (dops->init != NULL && !dev->xc_fw_load_done) {
cx231xx_info("Reloading firmware for XC5000\n");
status = dops->init(dev->dvb->frontend);
if (status == 0) {
dev->xc_fw_load_done = 1;
cx231xx_info
("XC5000 firmware download completed\n");
} else {
dev->xc_fw_load_done = 0;
cx231xx_info
("XC5000 firmware download failed !!!\n");
}
}
}
return status;
}
/* ------------------------------------------------------------------ */
static int register_dvb(struct cx231xx_dvb *dvb,
struct module *module,
struct cx231xx *dev, struct device *device)
{
int result;
mutex_init(&dvb->lock);
/* register adapter */
result = dvb_register_adapter(&dvb->adapter, dev->name, module, device,
adapter_nr);
if (result < 0) {
printk(KERN_WARNING
"%s: dvb_register_adapter failed (errno = %d)\n",
dev->name, result);
goto fail_adapter;
}
/* Ensure all frontends negotiate bus access */
dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
dvb->adapter.priv = dev;
/* register frontend */
result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
if (result < 0) {
printk(KERN_WARNING
"%s: dvb_register_frontend failed (errno = %d)\n",
dev->name, result);
goto fail_frontend;
}
/* register demux stuff */
dvb->demux.dmx.capabilities =
DMX_TS_FILTERING | DMX_SECTION_FILTERING |
DMX_MEMORY_BASED_FILTERING;
dvb->demux.priv = dvb;
dvb->demux.filternum = 256;
dvb->demux.feednum = 256;
dvb->demux.start_feed = start_feed;
dvb->demux.stop_feed = stop_feed;
result = dvb_dmx_init(&dvb->demux);
if (result < 0) {
printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
dev->name, result);
goto fail_dmx;
}
dvb->dmxdev.filternum = 256;
dvb->dmxdev.demux = &dvb->demux.dmx;
dvb->dmxdev.capabilities = 0;
result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
if (result < 0) {
printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
dev->name, result);
goto fail_dmxdev;
}
dvb->fe_hw.source = DMX_FRONTEND_0;
result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
printk(KERN_WARNING
"%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
dev->name, result);
goto fail_fe_hw;
}
dvb->fe_mem.source = DMX_MEMORY_FE;
result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
if (result < 0) {
printk(KERN_WARNING
"%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
dev->name, result);
goto fail_fe_mem;
}
result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
printk(KERN_WARNING
"%s: connect_frontend failed (errno = %d)\n", dev->name,
result);
goto fail_fe_conn;
}
/* register network adapter */
dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
return 0;
fail_fe_conn:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
fail_fe_mem:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
fail_fe_hw:
dvb_dmxdev_release(&dvb->dmxdev);
fail_dmxdev:
dvb_dmx_release(&dvb->demux);
fail_dmx:
dvb_unregister_frontend(dvb->frontend);
fail_frontend:
dvb_frontend_detach(dvb->frontend);
dvb_unregister_adapter(&dvb->adapter);
fail_adapter:
return result;
}
static void unregister_dvb(struct cx231xx_dvb *dvb)
{
struct i2c_client *client;
dvb_net_release(&dvb->net);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
dvb_dmxdev_release(&dvb->dmxdev);
dvb_dmx_release(&dvb->demux);
client = dvb->i2c_client_tuner;
/* remove I2C tuner */
if (client) {
module_put(client->dev.driver->owner);
i2c_unregister_device(client);
}
dvb_unregister_frontend(dvb->frontend);
dvb_frontend_detach(dvb->frontend);
dvb_unregister_adapter(&dvb->adapter);
}
static int dvb_init(struct cx231xx *dev)
{
int result = 0;
struct cx231xx_dvb *dvb;
if (!dev->board.has_dvb) {
/* This device does not support the extension */
return 0;
}
dvb = kzalloc(sizeof(struct cx231xx_dvb), GFP_KERNEL);
if (dvb == NULL) {
printk(KERN_INFO "cx231xx_dvb: memory allocation failed\n");
return -ENOMEM;
}
dev->dvb = dvb;
dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq;
dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner;
mutex_lock(&dev->lock);
cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
cx231xx_demod_reset(dev);
/* init frontend */
switch (dev->model) {
case CX231XX_BOARD_CNXT_CARRAERA:
case CX231XX_BOARD_CNXT_RDE_250:
dev->dvb->frontend = dvb_attach(s5h1432_attach,
&dvico_s5h1432_config,
&dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
if (dev->dvb->frontend == NULL) {
printk(DRIVER_NAME
": Failed to attach s5h1432 front end\n");
result = -EINVAL;
goto out_free;
}
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
&cnxt_rde250_tunerconfig)) {
result = -EINVAL;
goto out_free;
}
break;
case CX231XX_BOARD_CNXT_SHELBY:
case CX231XX_BOARD_CNXT_RDU_250:
dev->dvb->frontend = dvb_attach(s5h1411_attach,
&xc5000_s5h1411_config,
&dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
if (dev->dvb->frontend == NULL) {
printk(DRIVER_NAME
": Failed to attach s5h1411 front end\n");
result = -EINVAL;
goto out_free;
}
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
&cnxt_rdu250_tunerconfig)) {
result = -EINVAL;
goto out_free;
}
break;
case CX231XX_BOARD_CNXT_RDE_253S:
dev->dvb->frontend = dvb_attach(s5h1432_attach,
&dvico_s5h1432_config,
&dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
if (dev->dvb->frontend == NULL) {
printk(DRIVER_NAME
": Failed to attach s5h1432 front end\n");
result = -EINVAL;
goto out_free;
}
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
&cnxt_rde253s_tunerconfig)) {
result = -EINVAL;
goto out_free;
}
break;
case CX231XX_BOARD_CNXT_RDU_253S:
case CX231XX_BOARD_KWORLD_UB445_USB_HYBRID:
dev->dvb->frontend = dvb_attach(s5h1411_attach,
&tda18271_s5h1411_config,
&dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
if (dev->dvb->frontend == NULL) {
printk(DRIVER_NAME
": Failed to attach s5h1411 front end\n");
result = -EINVAL;
goto out_free;
}
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
&cnxt_rde253s_tunerconfig)) {
result = -EINVAL;
goto out_free;
}
break;
case CX231XX_BOARD_HAUPPAUGE_EXETER:
printk(KERN_INFO "%s: looking for tuner / demod on i2c bus: %d\n",
__func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap));
dev->dvb->frontend = dvb_attach(lgdt3305_attach,
&hcw_lgdt3305_config,
&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap);
if (dev->dvb->frontend == NULL) {
printk(DRIVER_NAME
": Failed to attach LG3305 front end\n");
result = -EINVAL;
goto out_free;
}
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
dvb_attach(tda18271_attach, dev->dvb->frontend,
0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
&hcw_tda18271_config);
break;
case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx:
dev->dvb->frontend = dvb_attach(si2165_attach,
&hauppauge_930C_HD_1113xx_si2165_config,
&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap
);
if (dev->dvb->frontend == NULL) {
printk(DRIVER_NAME
": Failed to attach SI2165 front end\n");
result = -EINVAL;
goto out_free;
}
dev->dvb->frontend->ops.i2c_gate_ctrl = NULL;
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
dvb_attach(tda18271_attach, dev->dvb->frontend,
0x60,
&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
&hcw_tda18271_config);
dev->cx231xx_reset_analog_tuner = NULL;
break;
case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
{
struct i2c_client *client;
struct i2c_board_info info;
struct si2157_config si2157_config;
memset(&info, 0, sizeof(struct i2c_board_info));
dev->dvb->frontend = dvb_attach(si2165_attach,
&pctv_quatro_stick_1114xx_si2165_config,
&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap
);
if (dev->dvb->frontend == NULL) {
printk(DRIVER_NAME
": Failed to attach SI2165 front end\n");
result = -EINVAL;
goto out_free;
}
dev->dvb->frontend->ops.i2c_gate_ctrl = NULL;
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
/* attach tuner */
memset(&si2157_config, 0, sizeof(si2157_config));
si2157_config.fe = dev->dvb->frontend;
si2157_config.inversion = true;
strlcpy(info.type, "si2157", I2C_NAME_SIZE);
info.addr = 0x60;
info.platform_data = &si2157_config;
request_module("si2157");
client = i2c_new_device(
&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
&info);
if (client == NULL || client->dev.driver == NULL) {
dvb_frontend_detach(dev->dvb->frontend);
result = -ENODEV;
goto out_free;
}
if (!try_module_get(client->dev.driver->owner)) {
i2c_unregister_device(client);
dvb_frontend_detach(dev->dvb->frontend);
result = -ENODEV;
goto out_free;
}
dev->cx231xx_reset_analog_tuner = NULL;
dev->dvb->i2c_client_tuner = client;
break;
}
case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID:
printk(KERN_INFO "%s: looking for demod on i2c bus: %d\n",
__func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap));
dev->dvb->frontend = dvb_attach(mb86a20s_attach,
&pv_mb86a20s_config,
&dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
if (dev->dvb->frontend == NULL) {
printk(DRIVER_NAME
": Failed to attach mb86a20s demod\n");
result = -EINVAL;
goto out_free;
}
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
dvb_attach(tda18271_attach, dev->dvb->frontend,
0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
&pv_tda18271_config);
break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n", dev->name);
break;
}
if (NULL == dvb->frontend) {
printk(KERN_ERR
"%s/2: frontend initialization failed\n", dev->name);
result = -EINVAL;
goto out_free;
}
/* register everything */
result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
if (result < 0)
goto out_free;
printk(KERN_INFO "Successfully loaded cx231xx-dvb\n");
ret:
cx231xx_set_mode(dev, CX231XX_SUSPEND);
mutex_unlock(&dev->lock);
return result;
out_free:
kfree(dvb);
dev->dvb = NULL;
goto ret;
}
static int dvb_fini(struct cx231xx *dev)
{
if (!dev->board.has_dvb) {
/* This device does not support the extension */
return 0;
}
if (dev->dvb) {
unregister_dvb(dev->dvb);
dev->dvb = NULL;
}
return 0;
}
static struct cx231xx_ops dvb_ops = {
.id = CX231XX_DVB,
.name = "Cx231xx dvb Extension",
.init = dvb_init,
.fini = dvb_fini,
};
static int __init cx231xx_dvb_register(void)
{
return cx231xx_register_extension(&dvb_ops);
}
static void __exit cx231xx_dvb_unregister(void)
{
cx231xx_unregister_extension(&dvb_ops);
}
module_init(cx231xx_dvb_register);
module_exit(cx231xx_dvb_unregister);

View file

@ -0,0 +1,541 @@
/*
cx231xx-i2c.c - driver for Conexant Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
Based on Cx23885 driver
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
#include "cx231xx.h"
/* ----------------------------------------------------------- */
static unsigned int i2c_scan;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
static unsigned int i2c_debug;
module_param(i2c_debug, int, 0644);
MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
#define dprintk1(lvl, fmt, args...) \
do { \
if (i2c_debug >= lvl) { \
printk(fmt, ##args); \
} \
} while (0)
#define dprintk2(lvl, fmt, args...) \
do { \
if (i2c_debug >= lvl) { \
printk(KERN_DEBUG "%s at %s: " fmt, \
dev->name, __func__ , ##args); \
} \
} while (0)
static inline bool is_tuner(struct cx231xx *dev, struct cx231xx_i2c *bus,
const struct i2c_msg *msg, int tuner_type)
{
if (bus->nr != dev->board.tuner_i2c_master)
return false;
if (msg->addr != dev->board.tuner_addr)
return false;
if (dev->tuner_type != tuner_type)
return false;
return true;
}
/*
* cx231xx_i2c_send_bytes()
*/
static int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
u16 size = 0;
u8 loop = 0;
u8 saddr_len = 1;
u8 *buf_ptr = NULL;
u16 saddr = 0;
u8 need_gpio = 0;
if (is_tuner(dev, bus, msg, TUNER_XC5000)) {
size = msg->len;
if (size == 2) { /* register write sub addr */
/* Just writing sub address will cause problem
* to XC5000. So ignore the request */
return 0;
} else if (size == 4) { /* register write with sub addr */
if (msg->len >= 2)
saddr = msg->buf[0] << 8 | msg->buf[1];
else if (msg->len == 1)
saddr = msg->buf[0];
switch (saddr) {
case 0x0000: /* start tuner calibration mode */
need_gpio = 1;
/* FW Loading is done */
dev->xc_fw_load_done = 1;
break;
case 0x000D: /* Set signal source */
case 0x0001: /* Set TV standard - Video */
case 0x0002: /* Set TV standard - Audio */
case 0x0003: /* Set RF Frequency */
need_gpio = 1;
break;
default:
if (dev->xc_fw_load_done)
need_gpio = 1;
break;
}
if (need_gpio) {
dprintk1(1,
"GPIO WRITE: addr 0x%x, len %d, saddr 0x%x\n",
msg->addr, msg->len, saddr);
return dev->cx231xx_gpio_i2c_write(dev,
msg->addr,
msg->buf,
msg->len);
}
}
/* special case for Xc5000 tuner case */
saddr_len = 1;
/* adjust the length to correct length */
size -= saddr_len;
buf_ptr = (u8 *) (msg->buf + 1);
do {
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = saddr_len;
req_data.saddr_dat = msg->buf[0];
req_data.buf_size = size > 16 ? 16 : size;
req_data.p_buffer = (u8 *) (buf_ptr + loop * 16);
bus->i2c_nostop = (size > 16) ? 1 : 0;
bus->i2c_reserve = (loop == 0) ? 0 : 1;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
loop++;
if (size >= 16)
size -= 16;
else
size = 0;
} while (size > 0);
bus->i2c_nostop = 0;
bus->i2c_reserve = 0;
} else { /* regular case */
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = 0;
req_data.saddr_dat = 0;
req_data.buf_size = msg->len;
req_data.p_buffer = msg->buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
}
return status < 0 ? status : 0;
}
/*
* cx231xx_i2c_recv_bytes()
* read a byte from the i2c device
*/
static int cx231xx_i2c_recv_bytes(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
u16 saddr = 0;
u8 need_gpio = 0;
if (is_tuner(dev, bus, msg, TUNER_XC5000)) {
if (msg->len == 2)
saddr = msg->buf[0] << 8 | msg->buf[1];
else if (msg->len == 1)
saddr = msg->buf[0];
if (dev->xc_fw_load_done) {
switch (saddr) {
case 0x0009: /* BUSY check */
dprintk1(1,
"GPIO R E A D: Special case BUSY check \n");
/*Try read BUSY register, just set it to zero*/
msg->buf[0] = 0;
if (msg->len == 2)
msg->buf[1] = 0;
return 0;
case 0x0004: /* read Lock status */
need_gpio = 1;
break;
}
if (need_gpio) {
/* this is a special case to handle Xceive tuner
clock stretch issue with gpio based I2C */
dprintk1(1,
"GPIO R E A D: addr 0x%x, len %d, saddr 0x%x\n",
msg->addr, msg->len,
msg->buf[0] << 8 | msg->buf[1]);
status =
dev->cx231xx_gpio_i2c_write(dev, msg->addr,
msg->buf,
msg->len);
status =
dev->cx231xx_gpio_i2c_read(dev, msg->addr,
msg->buf,
msg->len);
return status;
}
}
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = msg->len;
req_data.saddr_dat = msg->buf[0] << 8 | msg->buf[1];
req_data.buf_size = msg->len;
req_data.p_buffer = msg->buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
} else {
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = 0;
req_data.saddr_dat = 0;
req_data.buf_size = msg->len;
req_data.p_buffer = msg->buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
}
return status < 0 ? status : 0;
}
/*
* cx231xx_i2c_recv_bytes_with_saddr()
* read a byte from the i2c device
*/
static int cx231xx_i2c_recv_bytes_with_saddr(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg1,
const struct i2c_msg *msg2)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
u16 saddr = 0;
u8 need_gpio = 0;
if (msg1->len == 2)
saddr = msg1->buf[0] << 8 | msg1->buf[1];
else if (msg1->len == 1)
saddr = msg1->buf[0];
if (is_tuner(dev, bus, msg2, TUNER_XC5000)) {
if ((msg2->len < 16)) {
dprintk1(1,
"i2c_read: addr 0x%x, len %d, saddr 0x%x, len %d\n",
msg2->addr, msg2->len, saddr, msg1->len);
switch (saddr) {
case 0x0008: /* read FW load status */
need_gpio = 1;
break;
case 0x0004: /* read Lock status */
need_gpio = 1;
break;
}
if (need_gpio) {
status =
dev->cx231xx_gpio_i2c_write(dev, msg1->addr,
msg1->buf,
msg1->len);
status =
dev->cx231xx_gpio_i2c_read(dev, msg2->addr,
msg2->buf,
msg2->len);
return status;
}
}
}
/* prepare xfer_data struct */
req_data.dev_addr = msg2->addr;
req_data.direction = msg2->flags;
req_data.saddr_len = msg1->len;
req_data.saddr_dat = saddr;
req_data.buf_size = msg2->len;
req_data.p_buffer = msg2->buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
return status < 0 ? status : 0;
}
/*
* cx231xx_i2c_check_for_device()
* check if there is a i2c_device at the supplied address
*/
static int cx231xx_i2c_check_for_device(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = 0;
req_data.saddr_dat = 0;
req_data.buf_size = 0;
req_data.p_buffer = NULL;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
return status < 0 ? status : 0;
}
/*
* cx231xx_i2c_xfer()
* the main i2c transfer function
*/
static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msgs[], int num)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
int addr, rc, i, byte;
if (num <= 0)
return 0;
mutex_lock(&dev->i2c_lock);
for (i = 0; i < num; i++) {
addr = msgs[i].addr;
dprintk2(2, "%s %s addr=0x%x len=%d:",
(msgs[i].flags & I2C_M_RD) ? "read" : "write",
i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
if (!msgs[i].len) {
/* no len: check only for device presence */
rc = cx231xx_i2c_check_for_device(i2c_adap, &msgs[i]);
if (rc < 0) {
dprintk2(2, " no device\n");
mutex_unlock(&dev->i2c_lock);
return rc;
}
} else if (msgs[i].flags & I2C_M_RD) {
/* read bytes */
rc = cx231xx_i2c_recv_bytes(i2c_adap, &msgs[i]);
if (i2c_debug >= 2) {
for (byte = 0; byte < msgs[i].len; byte++)
printk(KERN_CONT " %02x", msgs[i].buf[byte]);
}
} else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
msgs[i].addr == msgs[i + 1].addr
&& (msgs[i].len <= 2) && (bus->nr < 3)) {
/* write bytes */
if (i2c_debug >= 2) {
for (byte = 0; byte < msgs[i].len; byte++)
printk(KERN_CONT " %02x", msgs[i].buf[byte]);
printk(KERN_CONT "\n");
}
/* read bytes */
dprintk2(2, "plus %s %s addr=0x%x len=%d:",
(msgs[i+1].flags & I2C_M_RD) ? "read" : "write",
i+1 == num - 1 ? "stop" : "nonstop", addr, msgs[i+1].len);
rc = cx231xx_i2c_recv_bytes_with_saddr(i2c_adap,
&msgs[i],
&msgs[i + 1]);
if (i2c_debug >= 2) {
for (byte = 0; byte < msgs[i+1].len; byte++)
printk(KERN_CONT " %02x", msgs[i+1].buf[byte]);
}
i++;
} else {
/* write bytes */
if (i2c_debug >= 2) {
for (byte = 0; byte < msgs[i].len; byte++)
printk(KERN_CONT " %02x", msgs[i].buf[byte]);
}
rc = cx231xx_i2c_send_bytes(i2c_adap, &msgs[i]);
}
if (rc < 0)
goto err;
if (i2c_debug >= 2)
printk(KERN_CONT "\n");
}
mutex_unlock(&dev->i2c_lock);
return num;
err:
dprintk2(2, " ERROR: %i\n", rc);
mutex_unlock(&dev->i2c_lock);
return rc;
}
/* ----------------------------------------------------------- */
/*
* functionality()
*/
static u32 functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
}
static struct i2c_algorithm cx231xx_algo = {
.master_xfer = cx231xx_i2c_xfer,
.functionality = functionality,
};
static struct i2c_adapter cx231xx_adap_template = {
.owner = THIS_MODULE,
.name = "cx231xx",
.algo = &cx231xx_algo,
};
static struct i2c_client cx231xx_client_template = {
.name = "cx231xx internal",
};
/* ----------------------------------------------------------- */
/*
* i2c_devs
* incomplete list of known devices
*/
static char *i2c_devs[128] = {
[0x60 >> 1] = "colibri",
[0x88 >> 1] = "hammerhead",
[0x8e >> 1] = "CIR",
[0x32 >> 1] = "GeminiIII",
[0x02 >> 1] = "Aquarius",
[0xa0 >> 1] = "eeprom",
[0xc0 >> 1] = "tuner",
[0xc2 >> 1] = "tuner",
};
/*
* cx231xx_do_i2c_scan()
* check i2c address range for devices
*/
void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c)
{
unsigned char buf;
int i, rc;
cx231xx_info(": Checking for I2C devices ..\n");
for (i = 0; i < 128; i++) {
c->addr = i;
rc = i2c_master_recv(c, &buf, 0);
if (rc < 0)
continue;
cx231xx_info("%s: i2c scan: found device @ 0x%x [%s]\n",
dev->name, i << 1,
i2c_devs[i] ? i2c_devs[i] : "???");
}
cx231xx_info(": Completed Checking for I2C devices.\n");
}
/*
* cx231xx_i2c_register()
* register i2c bus
*/
int cx231xx_i2c_register(struct cx231xx_i2c *bus)
{
struct cx231xx *dev = bus->dev;
BUG_ON(!dev->cx231xx_send_usb_command);
bus->i2c_adap = cx231xx_adap_template;
bus->i2c_client = cx231xx_client_template;
bus->i2c_adap.dev.parent = &dev->udev->dev;
strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
bus->i2c_adap.algo_data = bus;
i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
i2c_add_adapter(&bus->i2c_adap);
bus->i2c_client.adapter = &bus->i2c_adap;
if (0 == bus->i2c_rc) {
if (i2c_scan)
cx231xx_do_i2c_scan(dev, &bus->i2c_client);
} else
cx231xx_warn("%s: i2c bus %d register FAILED\n",
dev->name, bus->nr);
return bus->i2c_rc;
}
/*
* cx231xx_i2c_unregister()
* unregister i2c_bus
*/
int cx231xx_i2c_unregister(struct cx231xx_i2c *bus)
{
i2c_del_adapter(&bus->i2c_adap);
return 0;
}

View file

@ -0,0 +1,113 @@
/*
* cx231xx IR glue driver
*
* Copyright (C) 2010 Mauro Carvalho Chehab
*
* Polaris (cx231xx) has its support for IR's with a design close to MCE.
* however, a few designs are using an external I2C chip for IR, instead
* of using the one provided by the chip.
* This driver provides support for those extra devices
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* 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 "cx231xx.h"
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/bitrev.h>
#define MODULE_NAME "cx231xx-input"
static int get_key_isdbt(struct IR_i2c *ir, enum rc_type *protocol,
u32 *pscancode, u8 *toggle)
{
int rc;
u8 cmd, scancode;
dev_dbg(&ir->rc->input_dev->dev, "%s\n", __func__);
/* poll IR chip */
rc = i2c_master_recv(ir->c, &cmd, 1);
if (rc < 0)
return rc;
if (rc != 1)
return -EIO;
/* it seems that 0xFE indicates that a button is still hold
down, while 0xff indicates that no button is hold
down. 0xfe sequences are sometimes interrupted by 0xFF */
if (cmd == 0xff)
return 0;
scancode = bitrev8(cmd);
dev_dbg(&ir->rc->input_dev->dev, "cmd %02x, scan = %02x\n",
cmd, scancode);
*protocol = RC_TYPE_OTHER;
*pscancode = scancode;
*toggle = 0;
return 1;
}
int cx231xx_ir_init(struct cx231xx *dev)
{
struct i2c_board_info info;
u8 ir_i2c_bus;
dev_dbg(&dev->udev->dev, "%s\n", __func__);
/* Only initialize if a rc keycode map is defined */
if (!cx231xx_boards[dev->model].rc_map_name)
return -ENODEV;
request_module("ir-kbd-i2c");
memset(&info, 0, sizeof(struct i2c_board_info));
memset(&dev->init_data, 0, sizeof(dev->init_data));
dev->init_data.rc_dev = rc_allocate_device();
if (!dev->init_data.rc_dev)
return -ENOMEM;
dev->init_data.name = cx231xx_boards[dev->model].name;
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
info.platform_data = &dev->init_data;
/*
* Board-dependent values
*
* For now, there's just one type of hardware design using
* an i2c device.
*/
dev->init_data.get_key = get_key_isdbt;
dev->init_data.ir_codes = cx231xx_boards[dev->model].rc_map_name;
/* The i2c micro-controller only outputs the cmd part of NEC protocol */
dev->init_data.rc_dev->scancode_mask = 0xff;
dev->init_data.rc_dev->driver_name = "cx231xx";
dev->init_data.type = RC_BIT_NEC;
info.addr = 0x30;
/* Load and bind ir-kbd-i2c */
ir_i2c_bus = cx231xx_boards[dev->model].ir_i2c_master;
dev_dbg(&dev->udev->dev, "Trying to bind ir at bus %d, addr 0x%02x\n",
ir_i2c_bus, info.addr);
dev->ir_i2c_client = i2c_new_device(&dev->i2c_bus[ir_i2c_bus].i2c_adap, &info);
return 0;
}
void cx231xx_ir_exit(struct cx231xx *dev)
{
if (dev->ir_i2c_client)
i2c_unregister_device(dev->ir_i2c_client);
dev->ir_i2c_client = NULL;
}

View file

@ -0,0 +1,799 @@
/*
cx231xx-pcb-config.c - driver for Conexant
Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx231xx.h"
#include "cx231xx-conf-reg.h"
static unsigned int pcb_debug;
module_param(pcb_debug, int, 0644);
MODULE_PARM_DESC(pcb_debug, "enable pcb config debug messages [video]");
/******************************************************************************/
static struct pcb_config cx231xx_Scenario[] = {
{
INDEX_SELFPOWER_DIGITAL_ONLY, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_DIGITAL, /* mode */
SOURCE_TS_BDA, /* ts1_source, digital tv only */
NOT_SUPPORTED, /* ts2_source */
NOT_SUPPORTED, /* analog source */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
,
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed config */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_DUAL_DIGITAL, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_DIGITAL, /* mode */
SOURCE_TS_BDA, /* ts1_source, digital tv only */
0, /* ts2_source,need update from register */
NOT_SUPPORTED, /* analog source */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
2, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
2, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_ANALOG_ONLY, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_ANALOG | MOD_DIF | MOD_EXTERNAL, /* mode ,analog tv only */
NOT_SUPPORTED, /* ts1_source, NOT SUPPORT */
NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */
0, /* analog source, need update */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
1, /* AUDIO */
2, /* VIDEO */
3, /* VANC */
4, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
1, /* AUDIO */
2, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_DUAL, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
/* mode ,analog tv and digital path */
MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
0, /* ts1_source,will update in register */
NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */
0, /* analog source need update */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
4, /* VANC */
5, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_TRIPLE, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
/* mode ,analog tv and digital path */
MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
0, /* ts1_source, update in register */
0, /* ts2_source,update in register */
0, /* analog source, need update */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
2, /* TS2 index */
3, /* AUDIO */
4, /* VIDEO */
5, /* VANC */
6, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
2, /* TS2 index */
3, /* AUDIO */
4, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_COMPRESSOR, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
/* mode ,analog tv AND DIGITAL path */
MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
NOT_SUPPORTED, /* ts1_source, disable */
SOURCE_TS_BDA, /* ts2_source */
0, /* analog source,need update */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
1, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
4, /* VANC */
5, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
1, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_BUSPOWER_DIGITAL_ONLY, /* index */
USB_BUS_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_DIGITAL, /* mode ,analog tv AND DIGITAL path */
SOURCE_TS_BDA, /* ts1_source, disable */
NOT_SUPPORTED, /* ts2_source */
NOT_SUPPORTED, /* analog source */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index = 2 */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index = 2 */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_BUSPOWER_ANALOG_ONLY, /* index */
USB_BUS_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_ANALOG, /* mode ,analog tv AND DIGITAL path */
NOT_SUPPORTED, /* ts1_source, disable */
NOT_SUPPORTED, /* ts2_source */
SOURCE_ANALOG, /* analog source--analog */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
1, /* AUDIO */
2, /* VIDEO */
3, /* VANC */
4, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
{ /* full-speed */
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
1, /* AUDIO */
2, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_BUSPOWER_DIF_ONLY, /* index */
USB_BUS_POWER, /* power_type */
0, /* speed , not decide yet */
/* mode ,analog tv AND DIGITAL path */
MOD_DIF | MOD_ANALOG | MOD_DIGITAL | MOD_EXTERNAL,
SOURCE_TS_BDA, /* ts1_source, disable */
NOT_SUPPORTED, /* ts2_source */
SOURCE_DIF | SOURCE_ANALOG | SOURCE_EXTERNAL, /* analog source, dif */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
4, /* VANC */
5, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
{ /* full speed */
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
};
/*****************************************************************/
int initialize_cx231xx(struct cx231xx *dev)
{
int retval;
u32 config_info = 0;
struct pcb_config *p_pcb_info;
u8 usb_speed = 1; /* from register,1--HS, 0--FS */
u8 data[4] = { 0, 0, 0, 0 };
u32 ts1_source = 0;
u32 ts2_source = 0;
u32 analog_source = 0;
u8 _current_scenario_idx = 0xff;
ts1_source = SOURCE_TS_BDA;
ts2_source = SOURCE_TS_BDA;
/* read board config register to find out which
pcb config it is related to */
retval = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT,
data, 4);
if (retval < 0)
return retval;
config_info = le32_to_cpu(*((__le32 *)data));
usb_speed = (u8) (config_info & 0x1);
/* Verify this device belongs to Bus power or Self power device */
if (config_info & BUS_POWER) { /* bus-power */
switch (config_info & BUSPOWER_MASK) {
case TS1_PORT | BUS_POWER:
cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY].speed =
usb_speed;
p_pcb_info =
&cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY];
_current_scenario_idx = INDEX_BUSPOWER_DIGITAL_ONLY;
break;
case AVDEC_ENABLE | BUS_POWER:
cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY].speed =
usb_speed;
p_pcb_info =
&cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY];
_current_scenario_idx = INDEX_BUSPOWER_ANALOG_ONLY;
break;
case AVDEC_ENABLE | BUS_POWER | TS1_PORT:
cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY].speed =
usb_speed;
p_pcb_info = &cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY];
_current_scenario_idx = INDEX_BUSPOWER_DIF_ONLY;
break;
default:
cx231xx_info("bad config in buspower!!!!\n");
cx231xx_info("config_info=%x\n",
(config_info & BUSPOWER_MASK));
return 1;
}
} else { /* self-power */
switch (config_info & SELFPOWER_MASK) {
case TS1_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY].speed =
usb_speed;
p_pcb_info =
&cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY];
_current_scenario_idx = INDEX_SELFPOWER_DIGITAL_ONLY;
break;
case TS1_TS2_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].
ts2_source = ts2_source;
p_pcb_info =
&cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL];
_current_scenario_idx = INDEX_SELFPOWER_DUAL_DIGITAL;
break;
case AVDEC_ENABLE | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].
analog_source = analog_source;
p_pcb_info =
&cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY];
_current_scenario_idx = INDEX_SELFPOWER_ANALOG_ONLY;
break;
case AVDEC_ENABLE | TS1_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_DUAL].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_DUAL].ts1_source =
ts1_source;
cx231xx_Scenario[INDEX_SELFPOWER_DUAL].analog_source =
analog_source;
p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_DUAL];
_current_scenario_idx = INDEX_SELFPOWER_DUAL;
break;
case AVDEC_ENABLE | TS1_TS2_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts1_source =
ts1_source;
cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts2_source =
ts2_source;
cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].analog_source =
analog_source;
p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE];
_current_scenario_idx = INDEX_SELFPOWER_TRIPLE;
break;
case AVDEC_ENABLE | TS1VIP_TS2_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].
analog_source = analog_source;
p_pcb_info =
&cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR];
_current_scenario_idx = INDEX_SELFPOWER_COMPRESSOR;
break;
default:
cx231xx_info("bad senario!!!!!\n");
cx231xx_info("config_info=%x\n",
(config_info & SELFPOWER_MASK));
return -ENODEV;
}
}
dev->current_scenario_idx = _current_scenario_idx;
memcpy(&dev->current_pcb_config, p_pcb_info,
sizeof(struct pcb_config));
if (pcb_debug) {
cx231xx_info("SC(0x00) register = 0x%x\n", config_info);
cx231xx_info("scenario %d\n",
(dev->current_pcb_config.index) + 1);
cx231xx_info("type=%x\n", dev->current_pcb_config.type);
cx231xx_info("mode=%x\n", dev->current_pcb_config.mode);
cx231xx_info("speed=%x\n", dev->current_pcb_config.speed);
cx231xx_info("ts1_source=%x\n",
dev->current_pcb_config.ts1_source);
cx231xx_info("ts2_source=%x\n",
dev->current_pcb_config.ts2_source);
cx231xx_info("analog_source=%x\n",
dev->current_pcb_config.analog_source);
}
return 0;
}

View file

@ -0,0 +1,226 @@
/*
cx231xx-pcb-cfg.h - driver for Conexant
Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _PCB_CONFIG_H_
#define _PCB_CONFIG_H_
#include <linux/init.h>
#include <linux/module.h>
/***************************************************************************
* Class Information *
***************************************************************************/
#define CLASS_DEFAULT 0xFF
enum VENDOR_REQUEST_TYPE {
/* Set/Get I2C */
VRT_SET_I2C0 = 0x0,
VRT_SET_I2C1 = 0x1,
VRT_SET_I2C2 = 0x2,
VRT_GET_I2C0 = 0x4,
VRT_GET_I2C1 = 0x5,
VRT_GET_I2C2 = 0x6,
/* Set/Get GPIO */
VRT_SET_GPIO = 0x8,
VRT_GET_GPIO = 0x9,
/* Set/Get GPIE */
VRT_SET_GPIE = 0xA,
VRT_GET_GPIE = 0xB,
/* Set/Get Register Control/Status */
VRT_SET_REGISTER = 0xC,
VRT_GET_REGISTER = 0xD,
/* Get Extended Compat ID Descriptor */
VRT_GET_EXTCID_DESC = 0xFF,
};
enum BYTE_ENABLE_MASK {
ENABLE_ONE_BYTE = 0x1,
ENABLE_TWE_BYTE = 0x3,
ENABLE_THREE_BYTE = 0x7,
ENABLE_FOUR_BYTE = 0xF,
};
#define SPEED_MASK 0x1
enum USB_SPEED{
FULL_SPEED = 0x0, /* 0: full speed */
HIGH_SPEED = 0x1 /* 1: high speed */
};
#define TS_MASK 0x6
enum TS_PORT{
NO_TS_PORT = 0x0, /* 2'b00: Neither port used. PCB not a Hybrid,
only offers Analog TV or Video */
TS1_PORT = 0x4, /* 2'b10: TS1 Input (Hybrid mode :
Digital or External Analog/Compressed source) */
TS1_TS2_PORT = 0x6, /* 2'b11: TS1 & TS2 Inputs
(Dual inputs from Digital and/or
External Analog/Compressed sources) */
TS1_EXT_CLOCK = 0x6, /* 2'b11: TS1 & TS2 as selector
to external clock */
TS1VIP_TS2_PORT = 0x2 /* 2'b01: TS1 used as 656/VIP Output,
TS2 Input (from Compressor) */
};
#define EAVP_MASK 0x8
enum EAV_PRESENT{
NO_EXTERNAL_AV = 0x0, /* 0: No External A/V inputs
(no need for i2s blcok),
Analog Tuner must be present */
EXTERNAL_AV = 0x8 /* 1: External A/V inputs
present (requires i2s blk) */
};
#define ATM_MASK 0x30
enum AT_MODE{
DIF_TUNER = 0x30, /* 2'b11: IF Tuner (requires use of DIF) */
BASEBAND_SOUND = 0x20, /* 2'b10: Baseband Composite &
Sound-IF Signals present */
NO_TUNER = 0x10 /* 2'b0x: No Analog Tuner present */
};
#define PWR_SEL_MASK 0x40
enum POWE_TYPE{
SELF_POWER = 0x0, /* 0: self power */
BUS_POWER = 0x40 /* 1: bus power */
};
enum USB_POWE_TYPE{
USB_SELF_POWER = 0,
USB_BUS_POWER
};
#define BO_0_MASK 0x80
enum AVDEC_STATUS{
AVDEC_DISABLE = 0x0, /* 0: A/V Decoder Disabled */
AVDEC_ENABLE = 0x80 /* 1: A/V Decoder Enabled */
};
#define BO_1_MASK 0x100
#define BUSPOWER_MASK 0xC4 /* for Polaris spec 0.8 */
#define SELFPOWER_MASK 0x86
/***************************************************************************/
#define NOT_DECIDE_YET 0xFE
#define NOT_SUPPORTED 0xFF
/***************************************************************************
* for mod field use *
***************************************************************************/
#define MOD_DIGITAL 0x1
#define MOD_ANALOG 0x2
#define MOD_DIF 0x4
#define MOD_EXTERNAL 0x8
#define CAP_ALL_MOD 0x0f
/***************************************************************************
* source define *
***************************************************************************/
#define SOURCE_DIGITAL 0x1
#define SOURCE_ANALOG 0x2
#define SOURCE_DIF 0x4
#define SOURCE_EXTERNAL 0x8
#define SOURCE_TS_BDA 0x10
#define SOURCE_TS_ENCODE 0x20
#define SOURCE_TS_EXTERNAL 0x40
/***************************************************************************
* interface information define *
***************************************************************************/
struct INTERFACE_INFO {
u8 interrupt_index;
u8 ts1_index;
u8 ts2_index;
u8 audio_index;
u8 video_index;
u8 vanc_index; /* VBI */
u8 hanc_index; /* Sliced CC */
u8 ir_index;
};
enum INDEX_INTERFACE_INFO{
INDEX_INTERRUPT = 0x0,
INDEX_TS1,
INDEX_TS2,
INDEX_AUDIO,
INDEX_VIDEO,
INDEX_VANC,
INDEX_HANC,
INDEX_IR,
};
/***************************************************************************
* configuration information define *
***************************************************************************/
struct CONFIG_INFO {
u8 config_index;
struct INTERFACE_INFO interface_info;
};
struct pcb_config {
u8 index;
u8 type; /* bus power or self power,
self power--0, bus_power--1 */
u8 speed; /* usb speed, 2.0--1, 1.1--0 */
u8 mode; /* digital , anlog, dif or external A/V */
u32 ts1_source; /* three source -- BDA,External,encode */
u32 ts2_source;
u32 analog_source;
u8 digital_index; /* bus-power used */
u8 analog_index; /* bus-power used */
u8 dif_index; /* bus-power used */
u8 external_index; /* bus-power used */
u8 config_num; /* current config num, 0,1,2,
for self-power, always 0 */
struct CONFIG_INFO hs_config_info[3];
struct CONFIG_INFO fs_config_info[3];
};
enum INDEX_PCB_CONFIG{
INDEX_SELFPOWER_DIGITAL_ONLY = 0x0,
INDEX_SELFPOWER_DUAL_DIGITAL,
INDEX_SELFPOWER_ANALOG_ONLY,
INDEX_SELFPOWER_DUAL,
INDEX_SELFPOWER_TRIPLE,
INDEX_SELFPOWER_COMPRESSOR,
INDEX_BUSPOWER_DIGITAL_ONLY,
INDEX_BUSPOWER_ANALOG_ONLY,
INDEX_BUSPOWER_DIF_ONLY,
INDEX_BUSPOWER_EXTERNAL_ONLY,
INDEX_BUSPOWER_EXTERNAL_ANALOG,
INDEX_BUSPOWER_EXTERNAL_DIF,
INDEX_BUSPOWER_EXTERNAL_DIGITAL,
INDEX_BUSPOWER_DIGITAL_ANALOG,
INDEX_BUSPOWER_DIGITAL_DIF,
INDEX_BUSPOWER_DIGITAL_ANALOG_EXTERNAL,
INDEX_BUSPOWER_DIGITAL_DIF_EXTERNAL,
};
/***************************************************************************/
struct cx231xx;
int initialize_cx231xx(struct cx231xx *p_dev);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,706 @@
/*
cx231xx_vbi.c - driver for Conexant Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on cx88 driver
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/bitmap.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/msp3400.h>
#include <media/tuner.h>
#include "cx231xx.h"
#include "cx231xx-vbi.h"
static inline void print_err_status(struct cx231xx *dev, int packet, int status)
{
char *errmsg = "Unknown";
switch (status) {
case -ENOENT:
errmsg = "unlinked synchronuously";
break;
case -ECONNRESET:
errmsg = "unlinked asynchronuously";
break;
case -ENOSR:
errmsg = "Buffer error (overrun)";
break;
case -EPIPE:
errmsg = "Stalled (device not responding)";
break;
case -EOVERFLOW:
errmsg = "Babble (bad cable?)";
break;
case -EPROTO:
errmsg = "Bit-stuff error (bad cable?)";
break;
case -EILSEQ:
errmsg = "CRC/Timeout (could be anything)";
break;
case -ETIME:
errmsg = "Device does not respond";
break;
}
if (packet < 0) {
cx231xx_err("URB status %d [%s].\n", status,
errmsg);
} else {
cx231xx_err("URB packet %d, status %d [%s].\n",
packet, status, errmsg);
}
}
/*
* Controls the isoc copy of each urb packet
*/
static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb)
{
struct cx231xx_dmaqueue *dma_q = urb->context;
int rc = 1;
unsigned char *p_buffer;
u32 bytes_parsed = 0, buffer_size = 0;
u8 sav_eav = 0;
if (!dev)
return 0;
if (dev->state & DEV_DISCONNECTED)
return 0;
if (urb->status < 0) {
print_err_status(dev, -1, urb->status);
if (urb->status == -ENOENT)
return 0;
}
/* get buffer pointer and length */
p_buffer = urb->transfer_buffer;
buffer_size = urb->actual_length;
if (buffer_size > 0) {
bytes_parsed = 0;
if (dma_q->is_partial_line) {
/* Handle the case where we were working on a partial
line */
sav_eav = dma_q->last_sav;
} else {
/* Check for a SAV/EAV overlapping the
buffer boundary */
sav_eav = cx231xx_find_boundary_SAV_EAV(p_buffer,
dma_q->partial_buf,
&bytes_parsed);
}
sav_eav &= 0xF0;
/* Get the first line if we have some portion of an SAV/EAV from
the last buffer or a partial line */
if (sav_eav) {
bytes_parsed += cx231xx_get_vbi_line(dev, dma_q,
sav_eav, /* SAV/EAV */
p_buffer + bytes_parsed, /* p_buffer */
buffer_size - bytes_parsed); /* buffer size */
}
/* Now parse data that is completely in this buffer */
dma_q->is_partial_line = 0;
while (bytes_parsed < buffer_size) {
u32 bytes_used = 0;
sav_eav = cx231xx_find_next_SAV_EAV(
p_buffer + bytes_parsed, /* p_buffer */
buffer_size - bytes_parsed, /* buffer size */
&bytes_used); /* bytes used to get SAV/EAV */
bytes_parsed += bytes_used;
sav_eav &= 0xF0;
if (sav_eav && (bytes_parsed < buffer_size)) {
bytes_parsed += cx231xx_get_vbi_line(dev,
dma_q, sav_eav, /* SAV/EAV */
p_buffer+bytes_parsed, /* p_buffer */
buffer_size-bytes_parsed);/*buf size*/
}
}
/* Save the last four bytes of the buffer so we can
check the buffer boundary condition next time */
memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
bytes_parsed = 0;
}
return rc;
}
/* ------------------------------------------------------------------
Vbi buf operations
------------------------------------------------------------------*/
static int
vbi_buffer_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
struct cx231xx_fh *fh = vq->priv_data;
struct cx231xx *dev = fh->dev;
u32 height = 0;
height = ((dev->norm & V4L2_STD_625_50) ?
PAL_VBI_LINES : NTSC_VBI_LINES);
*size = (dev->width * height * 2 * 2);
if (0 == *count)
*count = CX231XX_DEF_VBI_BUF;
if (*count < CX231XX_MIN_BUF)
*count = CX231XX_MIN_BUF;
return 0;
}
/* This is called *without* dev->slock held; please keep it that way */
static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
{
struct cx231xx_fh *fh = vq->priv_data;
struct cx231xx *dev = fh->dev;
unsigned long flags = 0;
if (in_interrupt())
BUG();
/* We used to wait for the buffer to finish here, but this didn't work
because, as we were keeping the state as VIDEOBUF_QUEUED,
videobuf_queue_cancel marked it as finished for us.
(Also, it could wedge forever if the hardware was misconfigured.)
This should be safe; by the time we get here, the buffer isn't
queued anymore. If we ever start marking the buffers as
VIDEOBUF_ACTIVE, it won't be, though.
*/
spin_lock_irqsave(&dev->vbi_mode.slock, flags);
if (dev->vbi_mode.bulk_ctl.buf == buf)
dev->vbi_mode.bulk_ctl.buf = NULL;
spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
videobuf_vmalloc_free(&buf->vb);
buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
static int
vbi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
enum v4l2_field field)
{
struct cx231xx_fh *fh = vq->priv_data;
struct cx231xx_buffer *buf =
container_of(vb, struct cx231xx_buffer, vb);
struct cx231xx *dev = fh->dev;
int rc = 0, urb_init = 0;
u32 height = 0;
height = ((dev->norm & V4L2_STD_625_50) ?
PAL_VBI_LINES : NTSC_VBI_LINES);
buf->vb.size = ((dev->width << 1) * height * 2);
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
buf->vb.width = dev->width;
buf->vb.height = height;
buf->vb.field = field;
buf->vb.field = V4L2_FIELD_SEQ_TB;
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
rc = videobuf_iolock(vq, &buf->vb, NULL);
if (rc < 0)
goto fail;
}
if (!dev->vbi_mode.bulk_ctl.num_bufs)
urb_init = 1;
if (urb_init) {
rc = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS,
CX231XX_NUM_VBI_BUFS,
dev->vbi_mode.alt_max_pkt_size[0],
cx231xx_isoc_vbi_copy);
if (rc < 0)
goto fail;
}
buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
free_buffer(vq, buf);
return rc;
}
static void
vbi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx231xx_buffer *buf =
container_of(vb, struct cx231xx_buffer, vb);
struct cx231xx_fh *fh = vq->priv_data;
struct cx231xx *dev = fh->dev;
struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq;
buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue, &vidq->active);
}
static void vbi_buffer_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct cx231xx_buffer *buf =
container_of(vb, struct cx231xx_buffer, vb);
free_buffer(vq, buf);
}
struct videobuf_queue_ops cx231xx_vbi_qops = {
.buf_setup = vbi_buffer_setup,
.buf_prepare = vbi_buffer_prepare,
.buf_queue = vbi_buffer_queue,
.buf_release = vbi_buffer_release,
};
/* ------------------------------------------------------------------
URB control
------------------------------------------------------------------*/
/*
* IRQ callback, called by URB callback
*/
static void cx231xx_irq_vbi_callback(struct urb *urb)
{
struct cx231xx_dmaqueue *dma_q = urb->context;
struct cx231xx_video_mode *vmode =
container_of(dma_q, struct cx231xx_video_mode, vidq);
struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
switch (urb->status) {
case 0: /* success */
case -ETIMEDOUT: /* NAK */
break;
case -ECONNRESET: /* kill */
case -ENOENT:
case -ESHUTDOWN:
return;
default: /* error */
cx231xx_err("urb completition error %d.\n",
urb->status);
break;
}
/* Copy data from URB */
spin_lock(&dev->vbi_mode.slock);
dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb);
spin_unlock(&dev->vbi_mode.slock);
/* Reset status */
urb->status = 0;
urb->status = usb_submit_urb(urb, GFP_ATOMIC);
if (urb->status) {
cx231xx_err("urb resubmit failed (error=%i)\n",
urb->status);
}
}
/*
* Stop and Deallocate URBs
*/
void cx231xx_uninit_vbi_isoc(struct cx231xx *dev)
{
struct urb *urb;
int i;
cx231xx_info("called cx231xx_uninit_vbi_isoc\n");
dev->vbi_mode.bulk_ctl.nfields = -1;
for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
urb = dev->vbi_mode.bulk_ctl.urb[i];
if (urb) {
if (!irqs_disabled())
usb_kill_urb(urb);
else
usb_unlink_urb(urb);
if (dev->vbi_mode.bulk_ctl.transfer_buffer[i]) {
kfree(dev->vbi_mode.bulk_ctl.
transfer_buffer[i]);
dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
NULL;
}
usb_free_urb(urb);
dev->vbi_mode.bulk_ctl.urb[i] = NULL;
}
dev->vbi_mode.bulk_ctl.transfer_buffer[i] = NULL;
}
kfree(dev->vbi_mode.bulk_ctl.urb);
kfree(dev->vbi_mode.bulk_ctl.transfer_buffer);
dev->vbi_mode.bulk_ctl.urb = NULL;
dev->vbi_mode.bulk_ctl.transfer_buffer = NULL;
dev->vbi_mode.bulk_ctl.num_bufs = 0;
cx231xx_capture_start(dev, 0, Vbi);
}
EXPORT_SYMBOL_GPL(cx231xx_uninit_vbi_isoc);
/*
* Allocate URBs and start IRQ
*/
int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
int num_bufs, int max_pkt_size,
int (*bulk_copy) (struct cx231xx *dev,
struct urb *urb))
{
struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq;
int i;
int sb_size, pipe;
struct urb *urb;
int rc;
cx231xx_info("called cx231xx_vbi_isoc\n");
/* De-allocates all pending stuff */
cx231xx_uninit_vbi_isoc(dev);
/* clear if any halt */
usb_clear_halt(dev->udev,
usb_rcvbulkpipe(dev->udev,
dev->vbi_mode.end_point_addr));
dev->vbi_mode.bulk_ctl.bulk_copy = bulk_copy;
dev->vbi_mode.bulk_ctl.num_bufs = num_bufs;
dma_q->pos = 0;
dma_q->is_partial_line = 0;
dma_q->last_sav = 0;
dma_q->current_field = -1;
dma_q->bytes_left_in_line = dev->width << 1;
dma_q->lines_per_field = ((dev->norm & V4L2_STD_625_50) ?
PAL_VBI_LINES : NTSC_VBI_LINES);
dma_q->lines_completed = 0;
for (i = 0; i < 8; i++)
dma_q->partial_buf[i] = 0;
dev->vbi_mode.bulk_ctl.urb = kzalloc(sizeof(void *) * num_bufs,
GFP_KERNEL);
if (!dev->vbi_mode.bulk_ctl.urb) {
cx231xx_errdev("cannot alloc memory for usb buffers\n");
return -ENOMEM;
}
dev->vbi_mode.bulk_ctl.transfer_buffer =
kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
if (!dev->vbi_mode.bulk_ctl.transfer_buffer) {
cx231xx_errdev("cannot allocate memory for usbtransfer\n");
kfree(dev->vbi_mode.bulk_ctl.urb);
return -ENOMEM;
}
dev->vbi_mode.bulk_ctl.max_pkt_size = max_pkt_size;
dev->vbi_mode.bulk_ctl.buf = NULL;
sb_size = max_packets * dev->vbi_mode.bulk_ctl.max_pkt_size;
/* allocate urbs and transfer buffers */
for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
cx231xx_err("cannot alloc bulk_ctl.urb %i\n", i);
cx231xx_uninit_vbi_isoc(dev);
return -ENOMEM;
}
dev->vbi_mode.bulk_ctl.urb[i] = urb;
urb->transfer_flags = 0;
dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
kzalloc(sb_size, GFP_KERNEL);
if (!dev->vbi_mode.bulk_ctl.transfer_buffer[i]) {
cx231xx_err("unable to allocate %i bytes for transfer"
" buffer %i%s\n", sb_size, i,
in_interrupt() ? " while in int" : "");
cx231xx_uninit_vbi_isoc(dev);
return -ENOMEM;
}
pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr);
usb_fill_bulk_urb(urb, dev->udev, pipe,
dev->vbi_mode.bulk_ctl.transfer_buffer[i],
sb_size, cx231xx_irq_vbi_callback, dma_q);
}
init_waitqueue_head(&dma_q->wq);
/* submit urbs and enables IRQ */
for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
rc = usb_submit_urb(dev->vbi_mode.bulk_ctl.urb[i], GFP_ATOMIC);
if (rc) {
cx231xx_err("submit of urb %i failed (error=%i)\n", i,
rc);
cx231xx_uninit_vbi_isoc(dev);
return rc;
}
}
cx231xx_capture_start(dev, 1, Vbi);
return 0;
}
EXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc);
u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 sav_eav, u8 *p_buffer, u32 buffer_size)
{
u32 bytes_copied = 0;
int current_field = -1;
switch (sav_eav) {
case SAV_VBI_FIELD1:
current_field = 1;
break;
case SAV_VBI_FIELD2:
current_field = 2;
break;
default:
break;
}
if (current_field < 0)
return bytes_copied;
dma_q->last_sav = sav_eav;
bytes_copied =
cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size,
current_field);
return bytes_copied;
}
/*
* Announces that a buffer were filled and request the next
*/
static inline void vbi_buffer_filled(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q,
struct cx231xx_buffer *buf)
{
/* Advice that buffer was filled */
/* cx231xx_info("[%p/%d] wakeup\n", buf, buf->vb.i); */
buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
v4l2_get_timestamp(&buf->vb.ts);
dev->vbi_mode.bulk_ctl.buf = NULL;
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
}
u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_line, u32 length, int field_number)
{
u32 bytes_to_copy;
struct cx231xx_buffer *buf;
u32 _line_size = dev->width * 2;
if (dma_q->current_field == -1) {
/* Just starting up */
cx231xx_reset_vbi_buffer(dev, dma_q);
}
if (dma_q->current_field != field_number)
dma_q->lines_completed = 0;
/* get the buffer pointer */
buf = dev->vbi_mode.bulk_ctl.buf;
/* Remember the field number for next time */
dma_q->current_field = field_number;
bytes_to_copy = dma_q->bytes_left_in_line;
if (bytes_to_copy > length)
bytes_to_copy = length;
if (dma_q->lines_completed >= dma_q->lines_per_field) {
dma_q->bytes_left_in_line -= bytes_to_copy;
dma_q->is_partial_line =
(dma_q->bytes_left_in_line == 0) ? 0 : 1;
return 0;
}
dma_q->is_partial_line = 1;
/* If we don't have a buffer, just return the number of bytes we would
have copied if we had a buffer. */
if (!buf) {
dma_q->bytes_left_in_line -= bytes_to_copy;
dma_q->is_partial_line =
(dma_q->bytes_left_in_line == 0) ? 0 : 1;
return bytes_to_copy;
}
/* copy the data to video buffer */
cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy);
dma_q->pos += bytes_to_copy;
dma_q->bytes_left_in_line -= bytes_to_copy;
if (dma_q->bytes_left_in_line == 0) {
dma_q->bytes_left_in_line = _line_size;
dma_q->lines_completed++;
dma_q->is_partial_line = 0;
if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) {
vbi_buffer_filled(dev, dma_q, buf);
dma_q->pos = 0;
dma_q->lines_completed = 0;
cx231xx_reset_vbi_buffer(dev, dma_q);
}
}
return bytes_to_copy;
}
/*
* video-buf generic routine to get the next available buffer
*/
static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q,
struct cx231xx_buffer **buf)
{
struct cx231xx_video_mode *vmode =
container_of(dma_q, struct cx231xx_video_mode, vidq);
struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
char *outp;
if (list_empty(&dma_q->active)) {
cx231xx_err("No active queue to serve\n");
dev->vbi_mode.bulk_ctl.buf = NULL;
*buf = NULL;
return;
}
/* Get the next buffer */
*buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue);
/* Cleans up buffer - Useful for testing for frame/URB loss */
outp = videobuf_to_vmalloc(&(*buf)->vb);
memset(outp, 0, (*buf)->vb.size);
dev->vbi_mode.bulk_ctl.buf = *buf;
return;
}
void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q)
{
struct cx231xx_buffer *buf;
buf = dev->vbi_mode.bulk_ctl.buf;
if (buf == NULL) {
/* first try to get the buffer */
get_next_vbi_buf(dma_q, &buf);
dma_q->pos = 0;
dma_q->current_field = -1;
}
dma_q->bytes_left_in_line = dev->width << 1;
dma_q->lines_completed = 0;
}
int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_buffer, u32 bytes_to_copy)
{
u8 *p_out_buffer = NULL;
u32 current_line_bytes_copied = 0;
struct cx231xx_buffer *buf;
u32 _line_size = dev->width << 1;
void *startwrite;
int offset, lencopy;
buf = dev->vbi_mode.bulk_ctl.buf;
if (buf == NULL)
return -EINVAL;
p_out_buffer = videobuf_to_vmalloc(&buf->vb);
if (dma_q->bytes_left_in_line != _line_size) {
current_line_bytes_copied =
_line_size - dma_q->bytes_left_in_line;
}
offset = (dma_q->lines_completed * _line_size) +
current_line_bytes_copied;
if (dma_q->current_field == 2) {
/* Populate the second half of the frame */
offset += (dev->width * 2 * dma_q->lines_per_field);
}
/* prepare destination address */
startwrite = p_out_buffer + offset;
lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
bytes_to_copy : dma_q->bytes_left_in_line;
memcpy(startwrite, p_buffer, lencopy);
return 0;
}
u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q)
{
u32 height = 0;
height = ((dev->norm & V4L2_STD_625_50) ?
PAL_VBI_LINES : NTSC_VBI_LINES);
if (dma_q->lines_completed == height && dma_q->current_field == 2)
return 1;
else
return 0;
}

View file

@ -0,0 +1,65 @@
/*
cx231xx_vbi.h - driver for Conexant Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on cx88 driver
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _CX231XX_VBI_H
#define _CX231XX_VBI_H
extern struct videobuf_queue_ops cx231xx_vbi_qops;
#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */
#define NTSC_VBI_END_LINE 21
#define NTSC_VBI_LINES (NTSC_VBI_END_LINE-NTSC_VBI_START_LINE+1)
#define PAL_VBI_START_LINE 6
#define PAL_VBI_END_LINE 23
#define PAL_VBI_LINES (PAL_VBI_END_LINE-PAL_VBI_START_LINE+1)
#define VBI_STRIDE 1440
#define VBI_SAMPLES_PER_LINE 1440
#define CX231XX_NUM_VBI_PACKETS 4
#define CX231XX_NUM_VBI_BUFS 5
/* stream functions */
int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
int num_bufs, int max_pkt_size,
int (*bulk_copy) (struct cx231xx *dev,
struct urb *urb));
void cx231xx_uninit_vbi_isoc(struct cx231xx *dev);
/* vbi data copy functions */
u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 sav_eav, u8 *p_buffer, u32 buffer_size);
u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_line, u32 length, int field_number);
void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q);
int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_buffer, u32 bytes_to_copy);
u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q);
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,150 @@
config DVB_USB_V2
tristate "Support for various USB DVB devices v2"
depends on DVB_CORE && USB && I2C && (RC_CORE || RC_CORE=n)
help
By enabling this you will be able to choose the various supported
USB1.1 and USB2.0 DVB devices.
Almost every USB device needs a firmware, please look into
<file:Documentation/dvb/README.dvb-usb>.
For a complete list of supported USB devices see the LinuxTV DVB Wiki:
<http://www.linuxtv.org/wiki/index.php/DVB_USB>
Say Y if you own a USB DVB device.
config DVB_USB_AF9015
tristate "Afatech AF9015 DVB-T USB2.0 support"
depends on DVB_USB_V2
select DVB_AF9013
select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MC44S803 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TDA18218 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver
config DVB_USB_AF9035
tristate "Afatech AF9035 DVB-T USB2.0 support"
depends on DVB_USB_V2
select DVB_AF9033
select MEDIA_TUNER_TUA9001 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_FC0011 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TDA18218 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_IT913X if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the Afatech AF9035 based DVB USB receiver.
config DVB_USB_ANYSEE
tristate "Anysee DVB-T/C USB2.0 support"
depends on DVB_USB_V2
select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT
select DVB_CXD2820R if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the Anysee E30, Anysee E30 Plus or
Anysee E30 C Plus DVB USB2.0 receiver.
config DVB_USB_AU6610
tristate "Alcor Micro AU6610 USB2.0 support"
depends on DVB_USB_V2
select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver.
config DVB_USB_AZ6007
tristate "AzureWave 6007 and clones DVB-T/C USB2.0 support"
depends on DVB_USB_V2
select CYPRESS_FIRMWARE
select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the AZ6007 receivers like Terratec H7.
config DVB_USB_CE6230
tristate "Intel CE6230 DVB-T USB2.0 support"
depends on DVB_USB_V2
select DVB_ZL10353
select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver
config DVB_USB_EC168
tristate "E3C EC168 DVB-T USB2.0 support"
depends on DVB_USB_V2
select DVB_EC100
select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the E3C EC168 DVB-T USB2.0 receiver.
config DVB_USB_GL861
tristate "Genesys Logic GL861 USB2.0 support"
depends on DVB_USB_V2
select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0
receiver with USB ID 0db0:5581.
config DVB_USB_LME2510
tristate "LME DM04/QQBOX DVB-S USB2.0 support"
depends on DVB_USB_V2
depends on RC_CORE
select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT
select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
select DVB_IX2505V if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
select DVB_M88RS2000 if MEDIA_SUBDRV_AUTOSELECT
select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the LME DM04/QQBOX DVB-S USB2.0
config DVB_USB_MXL111SF
tristate "MxL111SF DTV USB2.0 support"
depends on DVB_USB_V2
select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT
select DVB_LG2160 if MEDIA_SUBDRV_AUTOSELECT
select VIDEO_TVEEPROM
help
Say Y here to support the MxL111SF USB2.0 DTV receiver.
config DVB_USB_RTL28XXU
tristate "Realtek RTL28xxU DVB USB support"
depends on DVB_USB_V2 && I2C_MUX
select DVB_RTL2830
select DVB_RTL2832
select DVB_RTL2832_SDR if (MEDIA_SUBDRV_AUTOSELECT && MEDIA_SDR_SUPPORT)
select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_FC0012 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_FC0013 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_E4000 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_R820T if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the Realtek RTL28xxU DVB USB receiver.
config DVB_USB_DVBSKY
tristate "DVBSky USB support"
depends on DVB_USB_V2
select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the USB receivers from DVBSky.

View file

@ -0,0 +1,46 @@
dvb_usb_v2-objs := dvb_usb_core.o dvb_usb_urb.o usb_urb.o
obj-$(CONFIG_DVB_USB_V2) += dvb_usb_v2.o
dvb-usb-af9015-objs := af9015.o
obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o
dvb-usb-af9035-objs := af9035.o
obj-$(CONFIG_DVB_USB_AF9035) += dvb-usb-af9035.o
dvb-usb-anysee-objs := anysee.o
obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o
dvb-usb-au6610-objs := au6610.o
obj-$(CONFIG_DVB_USB_AU6610) += dvb-usb-au6610.o
dvb-usb-az6007-objs := az6007.o
obj-$(CONFIG_DVB_USB_AZ6007) += dvb-usb-az6007.o
dvb-usb-ce6230-objs := ce6230.o
obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o
dvb-usb-ec168-objs := ec168.o
obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o
dvb-usb-lmedm04-objs := lmedm04.o
obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o
dvb-usb-gl861-objs := gl861.o
obj-$(CONFIG_DVB_USB_GL861) += dvb-usb-gl861.o
dvb-usb-mxl111sf-objs += mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o
dvb-usb-mxl111sf-objs += mxl111sf-gpio.o
obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
dvb-usb-rtl28xxu-objs := rtl28xxu.o
obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o
dvb-usb-dvbsky-objs := dvbsky.o
obj-$(CONFIG_DVB_USB_DVBSKY) += dvb-usb-dvbsky.o
ccflags-y += -I$(srctree)/drivers/media/dvb-core
ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
ccflags-y += -I$(srctree)/drivers/media/tuners
ccflags-y += -I$(srctree)/drivers/media/common

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,153 @@
/*
* DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
*
* Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
*
* Thanks to Afatech who kindly provided information.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef AF9015_H
#define AF9015_H
#include <linux/hash.h>
#include "dvb_usb.h"
#include "af9013.h"
#include "dvb-pll.h"
#include "mt2060.h"
#include "qt1010.h"
#include "tda18271.h"
#include "mxl5005s.h"
#include "mc44s803.h"
#include "tda18218.h"
#include "mxl5007t.h"
#define AF9015_FIRMWARE "dvb-usb-af9015.fw"
/* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0.
We use smaller - about 1/4 from the original, 5 and 87. */
#define TS_PACKET_SIZE 188
#define TS_USB20_PACKET_COUNT 87
#define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT)
#define TS_USB11_PACKET_COUNT 5
#define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT)
#define TS_USB20_MAX_PACKET_SIZE 512
#define TS_USB11_MAX_PACKET_SIZE 64
#define AF9015_I2C_EEPROM 0xa0
#define AF9015_I2C_DEMOD 0x38
#define AF9015_USB_TIMEOUT 2000
/* EEPROM locations */
#define AF9015_EEPROM_IR_MODE 0x18
#define AF9015_EEPROM_IR_REMOTE_TYPE 0x34
#define AF9015_EEPROM_TS_MODE 0x31
#define AF9015_EEPROM_DEMOD2_I2C 0x32
#define AF9015_EEPROM_SAW_BW1 0x35
#define AF9015_EEPROM_XTAL_TYPE1 0x36
#define AF9015_EEPROM_SPEC_INV1 0x37
#define AF9015_EEPROM_IF1L 0x38
#define AF9015_EEPROM_IF1H 0x39
#define AF9015_EEPROM_MT2060_IF1L 0x3a
#define AF9015_EEPROM_MT2060_IF1H 0x3b
#define AF9015_EEPROM_TUNER_ID1 0x3c
#define AF9015_EEPROM_SAW_BW2 0x45
#define AF9015_EEPROM_XTAL_TYPE2 0x46
#define AF9015_EEPROM_SPEC_INV2 0x47
#define AF9015_EEPROM_IF2L 0x48
#define AF9015_EEPROM_IF2H 0x49
#define AF9015_EEPROM_MT2060_IF2L 0x4a
#define AF9015_EEPROM_MT2060_IF2H 0x4b
#define AF9015_EEPROM_TUNER_ID2 0x4c
#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1)
struct req_t {
u8 cmd; /* [0] */
/* seq */ /* [1] */
u8 i2c_addr; /* [2] */
u16 addr; /* [3|4] */
u8 mbox; /* [5] */
u8 addr_len; /* [6] */
u8 data_len; /* [7] */
u8 *data;
};
enum af9015_cmd {
GET_CONFIG = 0x10,
DOWNLOAD_FIRMWARE = 0x11,
BOOT = 0x13,
READ_MEMORY = 0x20,
WRITE_MEMORY = 0x21,
READ_WRITE_I2C = 0x22,
COPY_FIRMWARE = 0x23,
RECONNECT_USB = 0x5a,
WRITE_VIRTUAL_MEMORY = 0x26,
GET_IR_CODE = 0x27,
READ_I2C,
WRITE_I2C,
};
enum af9015_ir_mode {
AF9015_IR_MODE_DISABLED = 0,
AF9015_IR_MODE_HID,
AF9015_IR_MODE_RLC,
AF9015_IR_MODE_RC6,
AF9015_IR_MODE_POLLING, /* just guess */
};
#define BUF_LEN 63
struct af9015_state {
u8 buf[BUF_LEN]; /* bulk USB control message */
u8 ir_mode;
u8 rc_repeat;
u32 rc_keycode;
u8 rc_last[4];
bool rc_failed;
u8 dual_mode;
u8 seq; /* packet sequence number */
u16 mt2060_if1[2];
u16 firmware_size;
u16 firmware_checksum;
u32 eeprom_sum;
struct af9013_config af9013_config[2];
/* for demod callback override */
int (*set_frontend[2]) (struct dvb_frontend *fe);
int (*read_status[2]) (struct dvb_frontend *fe, fe_status_t *status);
int (*init[2]) (struct dvb_frontend *fe);
int (*sleep[2]) (struct dvb_frontend *fe);
int (*tuner_init[2]) (struct dvb_frontend *fe);
int (*tuner_sleep[2]) (struct dvb_frontend *fe);
struct mutex fe_mutex;
};
enum af9015_remote {
AF9015_REMOTE_NONE = 0,
/* 1 */ AF9015_REMOTE_A_LINK_DTU_M,
AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
AF9015_REMOTE_MYGICTV_U718,
AF9015_REMOTE_DIGITTRADE_DVB_T,
/* 5 */ AF9015_REMOTE_AVERMEDIA_KS,
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,150 @@
/*
* Afatech AF9035 DVB USB driver
*
* Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
* Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef AF9035_H
#define AF9035_H
#include "dvb_usb.h"
#include "af9033.h"
#include "tua9001.h"
#include "fc0011.h"
#include "fc0012.h"
#include "mxl5007t.h"
#include "tda18218.h"
#include "fc2580.h"
#include "it913x.h"
#include "si2168.h"
#include "si2157.h"
struct reg_val {
u32 reg;
u8 val;
};
struct reg_val_mask {
u32 reg;
u8 val;
u8 mask;
};
struct usb_req {
u8 cmd;
u8 mbox;
u8 wlen;
u8 *wbuf;
u8 rlen;
u8 *rbuf;
};
struct state {
#define BUF_LEN 64
u8 buf[BUF_LEN];
u8 seq; /* packet sequence number */
u8 prechip_version;
u8 chip_version;
u16 chip_type;
u8 dual_mode:1;
u16 eeprom_addr;
u8 af9033_i2c_addr[2];
struct af9033_config af9033_config[2];
struct af9033_ops ops;
#define AF9035_I2C_CLIENT_MAX 4
struct i2c_client *i2c_client[AF9035_I2C_CLIENT_MAX];
struct i2c_adapter *i2c_adapter_demod;
};
static const u32 clock_lut_af9035[] = {
20480000, /* FPGA */
16384000, /* 16.38 MHz */
20480000, /* 20.48 MHz */
36000000, /* 36.00 MHz */
30000000, /* 30.00 MHz */
26000000, /* 26.00 MHz */
28000000, /* 28.00 MHz */
32000000, /* 32.00 MHz */
34000000, /* 34.00 MHz */
24000000, /* 24.00 MHz */
22000000, /* 22.00 MHz */
12000000, /* 12.00 MHz */
};
static const u32 clock_lut_it9135[] = {
12000000, /* 12.00 MHz */
20480000, /* 20.48 MHz */
36000000, /* 36.00 MHz */
30000000, /* 30.00 MHz */
26000000, /* 26.00 MHz */
28000000, /* 28.00 MHz */
32000000, /* 32.00 MHz */
34000000, /* 34.00 MHz */
24000000, /* 24.00 MHz */
22000000, /* 22.00 MHz */
};
#define AF9035_FIRMWARE_AF9035 "dvb-usb-af9035-02.fw"
#define AF9035_FIRMWARE_IT9135_V1 "dvb-usb-it9135-01.fw"
#define AF9035_FIRMWARE_IT9135_V2 "dvb-usb-it9135-02.fw"
#define AF9035_FIRMWARE_IT9303 "dvb-usb-it9303-01.fw"
/*
* eeprom is memory mapped as read only. Writing that memory mapped address
* will not corrupt eeprom.
*
* TS mode:
* 0 TS
* 1 DCA + PIP
* 3 PIP
* n DCA
*
* Values 0 and 3 are seen to this day. 0 for single TS and 3 for dual TS.
*/
#define EEPROM_BASE_AF9035 0x42fd
#define EEPROM_BASE_IT9135 0x499c
#define EEPROM_SHIFT 0x10
#define EEPROM_IR_MODE 0x10
#define EEPROM_TS_MODE 0x29
#define EEPROM_2ND_DEMOD_ADDR 0x2a
#define EEPROM_IR_TYPE 0x2c
#define EEPROM_1_IF_L 0x30
#define EEPROM_1_IF_H 0x31
#define EEPROM_1_TUNER_ID 0x34
#define EEPROM_2_IF_L 0x40
#define EEPROM_2_IF_H 0x41
#define EEPROM_2_TUNER_ID 0x44
/* USB commands */
#define CMD_MEM_RD 0x00
#define CMD_MEM_WR 0x01
#define CMD_I2C_RD 0x02
#define CMD_I2C_WR 0x03
#define CMD_IR_GET 0x18
#define CMD_FW_DL 0x21
#define CMD_FW_QUERYINFO 0x22
#define CMD_FW_BOOT 0x23
#define CMD_FW_DL_BEGIN 0x24
#define CMD_FW_DL_END 0x25
#define CMD_FW_SCATTER_WR 0x29
#define CMD_GENERIC_I2C_RD 0x2a
#define CMD_GENERIC_I2C_WR 0x2b
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,330 @@
/*
* DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver
*
* Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* TODO:
* - add smart card reader support for Conditional Access (CA)
*
* Card reader in Anysee is nothing more than ISO 7816 card reader.
* There is no hardware CAM in any Anysee device sold.
* In my understanding it should be implemented by making own module
* for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This
* module registers serial interface that can be used to communicate
* with any ISO 7816 smart card.
*
* Any help according to implement serial smart card reader support
* is highly welcome!
*/
#ifndef _DVB_USB_ANYSEE_H_
#define _DVB_USB_ANYSEE_H_
#define DVB_USB_LOG_PREFIX "anysee"
#include "dvb_usb.h"
#include "dvb_ca_en50221.h"
enum cmd {
CMD_I2C_READ = 0x33,
CMD_I2C_WRITE = 0x31,
CMD_REG_READ = 0xb0,
CMD_REG_WRITE = 0xb1,
CMD_STREAMING_CTRL = 0x12,
CMD_LED_AND_IR_CTRL = 0x16,
CMD_GET_IR_CODE = 0x41,
CMD_GET_HW_INFO = 0x19,
CMD_SMARTCARD = 0x34,
CMD_CI = 0x37,
};
struct anysee_state {
u8 buf[64];
u8 seq;
u8 hw; /* PCB ID */
#define ANYSEE_I2C_CLIENT_MAX 1
struct i2c_client *i2c_client[ANYSEE_I2C_CLIENT_MAX];
u8 fe_id:1; /* frondend ID */
u8 has_ci:1;
u8 has_tda18212:1;
u8 ci_attached:1;
struct dvb_ca_en50221 ci;
unsigned long ci_cam_ready; /* jiffies */
};
#define ANYSEE_HW_507T 2 /* E30 */
#define ANYSEE_HW_507CD 6 /* E30 Plus */
#define ANYSEE_HW_507DC 10 /* E30 C Plus */
#define ANYSEE_HW_507SI 11 /* E30 S2 Plus */
#define ANYSEE_HW_507FA 15 /* E30 Combo Plus / E30 C Plus */
#define ANYSEE_HW_508TC 18 /* E7 TC */
#define ANYSEE_HW_508S2 19 /* E7 S2 */
#define ANYSEE_HW_508T2C 20 /* E7 T2C */
#define ANYSEE_HW_508PTC 21 /* E7 PTC Plus */
#define ANYSEE_HW_508PS2 22 /* E7 PS2 Plus */
#define REG_IOA 0x80 /* Port A (bit addressable) */
#define REG_IOB 0x90 /* Port B (bit addressable) */
#define REG_IOC 0xa0 /* Port C (bit addressable) */
#define REG_IOD 0xb0 /* Port D (bit addressable) */
#define REG_IOE 0xb1 /* Port E (NOT bit addressable) */
#define REG_OEA 0xb2 /* Port A Output Enable */
#define REG_OEB 0xb3 /* Port B Output Enable */
#define REG_OEC 0xb4 /* Port C Output Enable */
#define REG_OED 0xb5 /* Port D Output Enable */
#define REG_OEE 0xb6 /* Port E Output Enable */
#endif
/***************************************************************************
* USB API description (reverse engineered)
***************************************************************************
Transaction flow:
=================
BULK[00001] >>> REQUEST PACKET 64 bytes
BULK[00081] <<< REPLY PACKET #1 64 bytes (PREVIOUS TRANSACTION REPLY)
BULK[00081] <<< REPLY PACKET #2 64 bytes (CURRENT TRANSACTION REPLY)
General reply packet(s) are always used if not own reply defined.
============================================================================
| 00-63 | GENERAL REPLY PACKET #1 (PREVIOUS REPLY)
============================================================================
| 00 | reply data (if any) from previous transaction
| | Just same reply packet as returned during previous transaction.
| | Needed only if reply is missed in previous transaction.
| | Just skip normally.
----------------------------------------------------------------------------
| 01-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | GENERAL REPLY PACKET #2 (CURRENT REPLY)
============================================================================
| 00 | reply data (if any)
----------------------------------------------------------------------------
| 01-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | I2C WRITE REQUEST PACKET
============================================================================
| 00 | 0x31 I2C write command
----------------------------------------------------------------------------
| 01 | i2c address
----------------------------------------------------------------------------
| 02 | data length
| | 0x02 (for typical I2C reg / val pair)
----------------------------------------------------------------------------
| 03 | 0x01
----------------------------------------------------------------------------
| 04- | data
----------------------------------------------------------------------------
| -59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | I2C READ REQUEST PACKET
============================================================================
| 00 | 0x33 I2C read command
----------------------------------------------------------------------------
| 01 | i2c address + 1
----------------------------------------------------------------------------
| 02 | register
----------------------------------------------------------------------------
| 03 | 0x00
----------------------------------------------------------------------------
| 04 | 0x00
----------------------------------------------------------------------------
| 05 | data length
----------------------------------------------------------------------------
| 06-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | USB CONTROLLER REGISTER WRITE REQUEST PACKET
============================================================================
| 00 | 0xb1 register write command
----------------------------------------------------------------------------
| 01-02 | register
----------------------------------------------------------------------------
| 03 | 0x01
----------------------------------------------------------------------------
| 04 | value
----------------------------------------------------------------------------
| 05-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | USB CONTROLLER REGISTER READ REQUEST PACKET
============================================================================
| 00 | 0xb0 register read command
----------------------------------------------------------------------------
| 01-02 | register
----------------------------------------------------------------------------
| 03 | 0x01
----------------------------------------------------------------------------
| 04-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | LED CONTROL REQUEST PACKET
============================================================================
| 00 | 0x16 LED and IR control command
----------------------------------------------------------------------------
| 01 | 0x01 (LED)
----------------------------------------------------------------------------
| 03 | 0x00 blink
| | 0x01 lights continuously
----------------------------------------------------------------------------
| 04 | blink interval
| | 0x00 fastest (looks like LED lights continuously)
| | 0xff slowest
----------------------------------------------------------------------------
| 05-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | IR CONTROL REQUEST PACKET
============================================================================
| 00 | 0x16 LED and IR control command
----------------------------------------------------------------------------
| 01 | 0x02 (IR)
----------------------------------------------------------------------------
| 03 | 0x00 IR disabled
| | 0x01 IR enabled
----------------------------------------------------------------------------
| 04-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | STREAMING CONTROL REQUEST PACKET
============================================================================
| 00 | 0x12 streaming control command
----------------------------------------------------------------------------
| 01 | 0x00 streaming disabled
| | 0x01 streaming enabled
----------------------------------------------------------------------------
| 02 | 0x00
----------------------------------------------------------------------------
| 03-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | REMOTE CONTROL REQUEST PACKET
============================================================================
| 00 | 0x41 remote control command
----------------------------------------------------------------------------
| 01-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | REMOTE CONTROL REPLY PACKET
============================================================================
| 00 | 0x00 code not received
| | 0x01 code received
----------------------------------------------------------------------------
| 01 | remote control code
----------------------------------------------------------------------------
| 02-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | GET HARDWARE INFO REQUEST PACKET
============================================================================
| 00 | 0x19 get hardware info command
----------------------------------------------------------------------------
| 01-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | GET HARDWARE INFO REPLY PACKET
============================================================================
| 00 | hardware id
----------------------------------------------------------------------------
| 01-02 | firmware version
----------------------------------------------------------------------------
| 03-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
============================================================================
| 00-63 | SMART CARD READER PACKET
============================================================================
| 00 | 0x34 smart card reader command
----------------------------------------------------------------------------
| xx |
----------------------------------------------------------------------------
| xx-59 | don't care
----------------------------------------------------------------------------
| 60 | packet sequence number
----------------------------------------------------------------------------
| 61-63 | don't care
----------------------------------------------------------------------------
*/

View file

@ -0,0 +1,213 @@
/*
* DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0.
*
* Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "au6610.h"
#include "zl10353.h"
#include "qt1010.h"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
int ret;
u16 index;
u8 *usb_buf;
/*
* allocate enough for all known requests,
* read returns 5 and write 6 bytes
*/
usb_buf = kmalloc(6, GFP_KERNEL);
if (!usb_buf)
return -ENOMEM;
switch (wlen) {
case 1:
index = wbuf[0] << 8;
break;
case 2:
index = wbuf[0] << 8;
index += wbuf[1];
break;
default:
dev_err(&d->udev->dev, "%s: wlen=%d, aborting\n",
KBUILD_MODNAME, wlen);
ret = -EINVAL;
goto error;
}
ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation,
USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index,
usb_buf, 6, AU6610_USB_TIMEOUT);
dvb_usb_dbg_usb_control_msg(d->udev, operation,
(USB_TYPE_VENDOR|USB_DIR_IN), addr << 1, index,
usb_buf, 6);
if (ret < 0)
goto error;
switch (operation) {
case AU6610_REQ_I2C_READ:
case AU6610_REQ_USB_READ:
/* requested value is always 5th byte in buffer */
rbuf[0] = usb_buf[4];
}
error:
kfree(usb_buf);
return ret;
}
static int au6610_i2c_msg(struct dvb_usb_device *d, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
u8 request;
u8 wo = (rbuf == NULL || rlen == 0); /* write-only */
if (wo) {
request = AU6610_REQ_I2C_WRITE;
} else { /* rw */
request = AU6610_REQ_I2C_READ;
}
return au6610_usb_msg(d, request, addr, wbuf, wlen, rbuf, rlen);
}
/* I2C */
static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
if (num > 2)
return -EINVAL;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
for (i = 0; i < num; i++) {
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf,
msg[i].len, msg[i+1].buf,
msg[i+1].len) < 0)
break;
i++;
} else if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf,
msg[i].len, NULL, 0) < 0)
break;
}
mutex_unlock(&d->i2c_mutex);
return i;
}
static u32 au6610_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm au6610_i2c_algo = {
.master_xfer = au6610_i2c_xfer,
.functionality = au6610_i2c_func,
};
/* Callbacks for DVB USB */
static struct zl10353_config au6610_zl10353_config = {
.demod_address = 0x0f,
.no_tuner = 1,
.parallel_ts = 1,
};
static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
{
adap->fe[0] = dvb_attach(zl10353_attach, &au6610_zl10353_config,
&adap_to_d(adap)->i2c_adap);
if (adap->fe[0] == NULL)
return -ENODEV;
return 0;
}
static struct qt1010_config au6610_qt1010_config = {
.i2c_address = 0x62
};
static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
{
return dvb_attach(qt1010_attach, adap->fe[0],
&adap_to_d(adap)->i2c_adap,
&au6610_qt1010_config) == NULL ? -ENODEV : 0;
}
static int au6610_init(struct dvb_usb_device *d)
{
/* TODO: this functionality belongs likely to the streaming control */
/* bInterfaceNumber 0, bAlternateSetting 5 */
return usb_set_interface(d->udev, 0, 5);
}
static struct dvb_usb_device_properties au6610_props = {
.driver_name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.adapter_nr = adapter_nr,
.i2c_algo = &au6610_i2c_algo,
.frontend_attach = au6610_zl10353_frontend_attach,
.tuner_attach = au6610_qt1010_tuner_attach,
.init = au6610_init,
.num_adapters = 1,
.adapter = {
{
.stream = DVB_USB_STREAM_ISOC(0x82, 5, 40, 942, 1),
},
},
};
static const struct usb_device_id au6610_id_table[] = {
{ DVB_USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110,
&au6610_props, "Sigmatek DVB-110", NULL) },
{ }
};
MODULE_DEVICE_TABLE(usb, au6610_id_table);
static struct usb_driver au6610_driver = {
.name = KBUILD_MODNAME,
.id_table = au6610_id_table,
.probe = dvb_usbv2_probe,
.disconnect = dvb_usbv2_disconnect,
.suspend = dvb_usbv2_suspend,
.resume = dvb_usbv2_resume,
.reset_resume = dvb_usbv2_reset_resume,
.no_dynamic_id = 1,
.soft_unbind = 1,
};
module_usb_driver(au6610_driver);
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
MODULE_DESCRIPTION("Driver for Alcor Micro AU6610 DVB-T USB2.0");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,32 @@
/*
* DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0.
*
* Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef AU6610_H
#define AU6610_H
#include "dvb_usb.h"
#define AU6610_REQ_I2C_WRITE 0x14
#define AU6610_REQ_I2C_READ 0x13
#define AU6610_REQ_USB_WRITE 0x16
#define AU6610_REQ_USB_READ 0x15
#define AU6610_USB_TIMEOUT 1000
#endif

View file

@ -0,0 +1,985 @@
/*
* Driver for AzureWave 6007 DVB-C/T USB2.0 and clones
*
* Copyright (c) Henry Wang <Henry.wang@AzureWave.com>
*
* This driver was made publicly available by Terratec, at:
* http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
* The original driver's license is GPL, as declared with MODULE_LICENSE()
*
* Copyright (c) 2010-2012 Mauro Carvalho Chehab
* Driver modified by in order to work with upstream drxk driver, and
* tons of bugs got fixed, and converted to use dvb-usb-v2.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation under version 2 of the License.
*
* 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 "drxk.h"
#include "mt2063.h"
#include "dvb_ca_en50221.h"
#include "dvb_usb.h"
#include "cypress_firmware.h"
#define AZ6007_FIRMWARE "dvb-usb-terratec-h7-az6007.fw"
static int az6007_xfer_debug;
module_param_named(xfer_debug, az6007_xfer_debug, int, 0644);
MODULE_PARM_DESC(xfer_debug, "Enable xfer debug");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
/* Known requests (Cypress FX2 firmware + az6007 "private" ones*/
#define FX2_OED 0xb5
#define AZ6007_READ_DATA 0xb7
#define AZ6007_I2C_RD 0xb9
#define AZ6007_POWER 0xbc
#define AZ6007_I2C_WR 0xbd
#define FX2_SCON1 0xc0
#define AZ6007_TS_THROUGH 0xc7
#define AZ6007_READ_IR 0xb4
struct az6007_device_state {
struct mutex mutex;
struct mutex ca_mutex;
struct dvb_ca_en50221 ca;
unsigned warm:1;
int (*gate_ctrl) (struct dvb_frontend *, int);
unsigned char data[4096];
};
static struct drxk_config terratec_h7_drxk = {
.adr = 0x29,
.parallel_ts = true,
.dynamic_clk = true,
.single_master = true,
.enable_merr_cfg = true,
.no_i2c_bridge = false,
.chunk_size = 64,
.mpeg_out_clk_strength = 0x02,
.qam_demod_parameter_count = 2,
.microcode_name = "dvb-usb-terratec-h7-drxk.fw",
};
static struct drxk_config cablestar_hdci_drxk = {
.adr = 0x29,
.parallel_ts = true,
.dynamic_clk = true,
.single_master = true,
.enable_merr_cfg = true,
.no_i2c_bridge = false,
.chunk_size = 64,
.mpeg_out_clk_strength = 0x02,
.qam_demod_parameter_count = 2,
.microcode_name = "dvb-usb-technisat-cablestar-hdci-drxk.fw",
};
static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct az6007_device_state *st = fe_to_priv(fe);
struct dvb_usb_adapter *adap = fe->sec_priv;
int status = 0;
pr_debug("%s: %s\n", __func__, enable ? "enable" : "disable");
if (!adap || !st)
return -EINVAL;
if (enable)
status = st->gate_ctrl(fe, 1);
else
status = st->gate_ctrl(fe, 0);
return status;
}
static struct mt2063_config az6007_mt2063_config = {
.tuner_address = 0x60,
.refclock = 36125000,
};
static int __az6007_read(struct usb_device *udev, u8 req, u16 value,
u16 index, u8 *b, int blen)
{
int ret;
ret = usb_control_msg(udev,
usb_rcvctrlpipe(udev, 0),
req,
USB_TYPE_VENDOR | USB_DIR_IN,
value, index, b, blen, 5000);
if (ret < 0) {
pr_warn("usb read operation failed. (%d)\n", ret);
return -EIO;
}
if (az6007_xfer_debug) {
printk(KERN_DEBUG "az6007: IN req: %02x, value: %04x, index: %04x\n",
req, value, index);
print_hex_dump_bytes("az6007: payload: ",
DUMP_PREFIX_NONE, b, blen);
}
return ret;
}
static int az6007_read(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen)
{
struct az6007_device_state *st = d->priv;
int ret;
if (mutex_lock_interruptible(&st->mutex) < 0)
return -EAGAIN;
ret = __az6007_read(d->udev, req, value, index, b, blen);
mutex_unlock(&st->mutex);
return ret;
}
static int __az6007_write(struct usb_device *udev, u8 req, u16 value,
u16 index, u8 *b, int blen)
{
int ret;
if (az6007_xfer_debug) {
printk(KERN_DEBUG "az6007: OUT req: %02x, value: %04x, index: %04x\n",
req, value, index);
print_hex_dump_bytes("az6007: payload: ",
DUMP_PREFIX_NONE, b, blen);
}
if (blen > 64) {
pr_err("az6007: tried to write %d bytes, but I2C max size is 64 bytes\n",
blen);
return -EOPNOTSUPP;
}
ret = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
req,
USB_TYPE_VENDOR | USB_DIR_OUT,
value, index, b, blen, 5000);
if (ret != blen) {
pr_err("usb write operation failed. (%d)\n", ret);
return -EIO;
}
return 0;
}
static int az6007_write(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen)
{
struct az6007_device_state *st = d->priv;
int ret;
if (mutex_lock_interruptible(&st->mutex) < 0)
return -EAGAIN;
ret = __az6007_write(d->udev, req, value, index, b, blen);
mutex_unlock(&st->mutex);
return ret;
}
static int az6007_streaming_ctrl(struct dvb_frontend *fe, int onoff)
{
struct dvb_usb_device *d = fe_to_d(fe);
pr_debug("%s: %s\n", __func__, onoff ? "enable" : "disable");
return az6007_write(d, 0xbc, onoff, 0, NULL, 0);
}
#if IS_ENABLED(CONFIG_RC_CORE)
/* remote control stuff (does not work with my box) */
static int az6007_rc_query(struct dvb_usb_device *d)
{
struct az6007_device_state *st = d_to_priv(d);
unsigned code;
az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10);
if (st->data[1] == 0x44)
return 0;
if ((st->data[3] ^ st->data[4]) == 0xff) {
if ((st->data[1] ^ st->data[2]) == 0xff)
code = RC_SCANCODE_NEC(st->data[1], st->data[3]);
else
code = RC_SCANCODE_NECX(st->data[1] << 8 | st->data[2],
st->data[3]);
} else {
code = RC_SCANCODE_NEC32(st->data[1] << 24 |
st->data[2] << 16 |
st->data[3] << 8 |
st->data[4]);
}
rc_keydown(d->rc_dev, RC_TYPE_NEC, code, st->data[5]);
return 0;
}
static int az6007_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
{
pr_debug("Getting az6007 Remote Control properties\n");
rc->allowed_protos = RC_BIT_NEC;
rc->query = az6007_rc_query;
rc->interval = 400;
return 0;
}
#else
#define az6007_get_rc_config NULL
#endif
static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca,
int slot,
int address)
{
struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
struct az6007_device_state *state = d_to_priv(d);
int ret;
u8 req;
u16 value;
u16 index;
int blen;
u8 *b;
if (slot != 0)
return -EINVAL;
b = kmalloc(12, GFP_KERNEL);
if (!b)
return -ENOMEM;
mutex_lock(&state->ca_mutex);
req = 0xC1;
value = address;
index = 0;
blen = 1;
ret = az6007_read(d, req, value, index, b, blen);
if (ret < 0) {
pr_warn("usb in operation failed. (%d)\n", ret);
ret = -EINVAL;
} else {
ret = b[0];
}
mutex_unlock(&state->ca_mutex);
kfree(b);
return ret;
}
static int az6007_ci_write_attribute_mem(struct dvb_ca_en50221 *ca,
int slot,
int address,
u8 value)
{
struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
struct az6007_device_state *state = d_to_priv(d);
int ret;
u8 req;
u16 value1;
u16 index;
int blen;
pr_debug("%s(), slot %d\n", __func__, slot);
if (slot != 0)
return -EINVAL;
mutex_lock(&state->ca_mutex);
req = 0xC2;
value1 = address;
index = value;
blen = 0;
ret = az6007_write(d, req, value1, index, NULL, blen);
if (ret != 0)
pr_warn("usb out operation failed. (%d)\n", ret);
mutex_unlock(&state->ca_mutex);
return ret;
}
static int az6007_ci_read_cam_control(struct dvb_ca_en50221 *ca,
int slot,
u8 address)
{
struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
struct az6007_device_state *state = d_to_priv(d);
int ret;
u8 req;
u16 value;
u16 index;
int blen;
u8 *b;
if (slot != 0)
return -EINVAL;
b = kmalloc(12, GFP_KERNEL);
if (!b)
return -ENOMEM;
mutex_lock(&state->ca_mutex);
req = 0xC3;
value = address;
index = 0;
blen = 2;
ret = az6007_read(d, req, value, index, b, blen);
if (ret < 0) {
pr_warn("usb in operation failed. (%d)\n", ret);
ret = -EINVAL;
} else {
if (b[0] == 0)
pr_warn("Read CI IO error\n");
ret = b[1];
pr_debug("read cam data = %x from 0x%x\n", b[1], value);
}
mutex_unlock(&state->ca_mutex);
kfree(b);
return ret;
}
static int az6007_ci_write_cam_control(struct dvb_ca_en50221 *ca,
int slot,
u8 address,
u8 value)
{
struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
struct az6007_device_state *state = d_to_priv(d);
int ret;
u8 req;
u16 value1;
u16 index;
int blen;
if (slot != 0)
return -EINVAL;
mutex_lock(&state->ca_mutex);
req = 0xC4;
value1 = address;
index = value;
blen = 0;
ret = az6007_write(d, req, value1, index, NULL, blen);
if (ret != 0) {
pr_warn("usb out operation failed. (%d)\n", ret);
goto failed;
}
failed:
mutex_unlock(&state->ca_mutex);
return ret;
}
static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot)
{
struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
int ret;
u8 req;
u16 value;
u16 index;
int blen;
u8 *b;
b = kmalloc(12, GFP_KERNEL);
if (!b)
return -ENOMEM;
req = 0xC8;
value = 0;
index = 0;
blen = 1;
ret = az6007_read(d, req, value, index, b, blen);
if (ret < 0) {
pr_warn("usb in operation failed. (%d)\n", ret);
ret = -EIO;
} else{
ret = b[0];
}
kfree(b);
return ret;
}
static int az6007_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
{
struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
struct az6007_device_state *state = d_to_priv(d);
int ret, i;
u8 req;
u16 value;
u16 index;
int blen;
mutex_lock(&state->ca_mutex);
req = 0xC6;
value = 1;
index = 0;
blen = 0;
ret = az6007_write(d, req, value, index, NULL, blen);
if (ret != 0) {
pr_warn("usb out operation failed. (%d)\n", ret);
goto failed;
}
msleep(500);
req = 0xC6;
value = 0;
index = 0;
blen = 0;
ret = az6007_write(d, req, value, index, NULL, blen);
if (ret != 0) {
pr_warn("usb out operation failed. (%d)\n", ret);
goto failed;
}
for (i = 0; i < 15; i++) {
msleep(100);
if (CI_CamReady(ca, slot)) {
pr_debug("CAM Ready\n");
break;
}
}
msleep(5000);
failed:
mutex_unlock(&state->ca_mutex);
return ret;
}
static int az6007_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
{
return 0;
}
static int az6007_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
{
struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
struct az6007_device_state *state = d_to_priv(d);
int ret;
u8 req;
u16 value;
u16 index;
int blen;
pr_debug("%s()\n", __func__);
mutex_lock(&state->ca_mutex);
req = 0xC7;
value = 1;
index = 0;
blen = 0;
ret = az6007_write(d, req, value, index, NULL, blen);
if (ret != 0) {
pr_warn("usb out operation failed. (%d)\n", ret);
goto failed;
}
failed:
mutex_unlock(&state->ca_mutex);
return ret;
}
static int az6007_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
{
struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
struct az6007_device_state *state = d_to_priv(d);
int ret;
u8 req;
u16 value;
u16 index;
int blen;
u8 *b;
b = kmalloc(12, GFP_KERNEL);
if (!b)
return -ENOMEM;
mutex_lock(&state->ca_mutex);
req = 0xC5;
value = 0;
index = 0;
blen = 1;
ret = az6007_read(d, req, value, index, b, blen);
if (ret < 0) {
pr_warn("usb in operation failed. (%d)\n", ret);
ret = -EIO;
} else
ret = 0;
if (!ret && b[0] == 1) {
ret = DVB_CA_EN50221_POLL_CAM_PRESENT |
DVB_CA_EN50221_POLL_CAM_READY;
}
mutex_unlock(&state->ca_mutex);
kfree(b);
return ret;
}
static void az6007_ci_uninit(struct dvb_usb_device *d)
{
struct az6007_device_state *state;
pr_debug("%s()\n", __func__);
if (NULL == d)
return;
state = d_to_priv(d);
if (NULL == state)
return;
if (NULL == state->ca.data)
return;
dvb_ca_en50221_release(&state->ca);
memset(&state->ca, 0, sizeof(state->ca));
}
static int az6007_ci_init(struct dvb_usb_adapter *adap)
{
struct dvb_usb_device *d = adap_to_d(adap);
struct az6007_device_state *state = adap_to_priv(adap);
int ret;
pr_debug("%s()\n", __func__);
mutex_init(&state->ca_mutex);
state->ca.owner = THIS_MODULE;
state->ca.read_attribute_mem = az6007_ci_read_attribute_mem;
state->ca.write_attribute_mem = az6007_ci_write_attribute_mem;
state->ca.read_cam_control = az6007_ci_read_cam_control;
state->ca.write_cam_control = az6007_ci_write_cam_control;
state->ca.slot_reset = az6007_ci_slot_reset;
state->ca.slot_shutdown = az6007_ci_slot_shutdown;
state->ca.slot_ts_enable = az6007_ci_slot_ts_enable;
state->ca.poll_slot_status = az6007_ci_poll_slot_status;
state->ca.data = d;
ret = dvb_ca_en50221_init(&adap->dvb_adap,
&state->ca,
0, /* flags */
1);/* n_slots */
if (ret != 0) {
pr_err("Cannot initialize CI: Error %d.\n", ret);
memset(&state->ca, 0, sizeof(state->ca));
return ret;
}
pr_debug("CI initialized.\n");
return 0;
}
static int az6007_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6])
{
struct dvb_usb_device *d = adap_to_d(adap);
struct az6007_device_state *st = adap_to_priv(adap);
int ret;
ret = az6007_read(d, AZ6007_READ_DATA, 6, 0, st->data, 6);
memcpy(mac, st->data, 6);
if (ret > 0)
pr_debug("%s: mac is %pM\n", __func__, mac);
return ret;
}
static int az6007_frontend_attach(struct dvb_usb_adapter *adap)
{
struct az6007_device_state *st = adap_to_priv(adap);
struct dvb_usb_device *d = adap_to_d(adap);
pr_debug("attaching demod drxk\n");
adap->fe[0] = dvb_attach(drxk_attach, &terratec_h7_drxk,
&d->i2c_adap);
if (!adap->fe[0])
return -EINVAL;
adap->fe[0]->sec_priv = adap;
st->gate_ctrl = adap->fe[0]->ops.i2c_gate_ctrl;
adap->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;
az6007_ci_init(adap);
return 0;
}
static int az6007_cablestar_hdci_frontend_attach(struct dvb_usb_adapter *adap)
{
struct az6007_device_state *st = adap_to_priv(adap);
struct dvb_usb_device *d = adap_to_d(adap);
pr_debug("attaching demod drxk\n");
adap->fe[0] = dvb_attach(drxk_attach, &cablestar_hdci_drxk,
&d->i2c_adap);
if (!adap->fe[0])
return -EINVAL;
adap->fe[0]->sec_priv = adap;
st->gate_ctrl = adap->fe[0]->ops.i2c_gate_ctrl;
adap->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;
az6007_ci_init(adap);
return 0;
}
static int az6007_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dvb_usb_device *d = adap_to_d(adap);
pr_debug("attaching tuner mt2063\n");
/* Attach mt2063 to DVB-C frontend */
if (adap->fe[0]->ops.i2c_gate_ctrl)
adap->fe[0]->ops.i2c_gate_ctrl(adap->fe[0], 1);
if (!dvb_attach(mt2063_attach, adap->fe[0],
&az6007_mt2063_config,
&d->i2c_adap))
return -EINVAL;
if (adap->fe[0]->ops.i2c_gate_ctrl)
adap->fe[0]->ops.i2c_gate_ctrl(adap->fe[0], 0);
return 0;
}
static int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
{
struct az6007_device_state *state = d_to_priv(d);
int ret;
pr_debug("%s()\n", __func__);
if (!state->warm) {
mutex_init(&state->mutex);
ret = az6007_write(d, AZ6007_POWER, 0, 2, NULL, 0);
if (ret < 0)
return ret;
msleep(60);
ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0);
if (ret < 0)
return ret;
msleep(100);
ret = az6007_write(d, AZ6007_POWER, 1, 3, NULL, 0);
if (ret < 0)
return ret;
msleep(20);
ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0);
if (ret < 0)
return ret;
msleep(400);
ret = az6007_write(d, FX2_SCON1, 0, 3, NULL, 0);
if (ret < 0)
return ret;
msleep(150);
ret = az6007_write(d, FX2_SCON1, 1, 3, NULL, 0);
if (ret < 0)
return ret;
msleep(430);
ret = az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0);
if (ret < 0)
return ret;
state->warm = true;
return 0;
}
if (!onoff)
return 0;
az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0);
az6007_write(d, AZ6007_TS_THROUGH, 0, 0, NULL, 0);
return 0;
}
/* I2C */
static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct az6007_device_state *st = d_to_priv(d);
int i, j, len;
int ret = 0;
u16 index;
u16 value;
int length;
u8 req, addr;
if (mutex_lock_interruptible(&st->mutex) < 0)
return -EAGAIN;
for (i = 0; i < num; i++) {
addr = msgs[i].addr << 1;
if (((i + 1) < num)
&& (msgs[i].len == 1)
&& ((msgs[i].flags & I2C_M_RD) != I2C_M_RD)
&& (msgs[i + 1].flags & I2C_M_RD)
&& (msgs[i].addr == msgs[i + 1].addr)) {
/*
* A write + read xfer for the same address, where
* the first xfer has just 1 byte length.
* Need to join both into one operation
*/
if (az6007_xfer_debug)
printk(KERN_DEBUG "az6007: I2C W/R addr=0x%x len=%d/%d\n",
addr, msgs[i].len, msgs[i + 1].len);
req = AZ6007_I2C_RD;
index = msgs[i].buf[0];
value = addr | (1 << 8);
length = 6 + msgs[i + 1].len;
len = msgs[i + 1].len;
ret = __az6007_read(d->udev, req, value, index,
st->data, length);
if (ret >= len) {
for (j = 0; j < len; j++)
msgs[i + 1].buf[j] = st->data[j + 5];
} else
ret = -EIO;
i++;
} else if (!(msgs[i].flags & I2C_M_RD)) {
/* write bytes */
if (az6007_xfer_debug)
printk(KERN_DEBUG "az6007: I2C W addr=0x%x len=%d\n",
addr, msgs[i].len);
req = AZ6007_I2C_WR;
index = msgs[i].buf[0];
value = addr | (1 << 8);
length = msgs[i].len - 1;
len = msgs[i].len - 1;
for (j = 0; j < len; j++)
st->data[j] = msgs[i].buf[j + 1];
ret = __az6007_write(d->udev, req, value, index,
st->data, length);
} else {
/* read bytes */
if (az6007_xfer_debug)
printk(KERN_DEBUG "az6007: I2C R addr=0x%x len=%d\n",
addr, msgs[i].len);
req = AZ6007_I2C_RD;
index = msgs[i].buf[0];
value = addr;
length = msgs[i].len + 6;
len = msgs[i].len;
ret = __az6007_read(d->udev, req, value, index,
st->data, length);
for (j = 0; j < len; j++)
msgs[i].buf[j] = st->data[j + 5];
}
if (ret < 0)
goto err;
}
err:
mutex_unlock(&st->mutex);
if (ret < 0) {
pr_info("%s ERROR: %i\n", __func__, ret);
return ret;
}
return num;
}
static u32 az6007_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm az6007_i2c_algo = {
.master_xfer = az6007_i2c_xfer,
.functionality = az6007_i2c_func,
};
static int az6007_identify_state(struct dvb_usb_device *d, const char **name)
{
int ret;
u8 *mac;
pr_debug("Identifying az6007 state\n");
mac = kmalloc(6, GFP_ATOMIC);
if (!mac)
return -ENOMEM;
/* Try to read the mac address */
ret = __az6007_read(d->udev, AZ6007_READ_DATA, 6, 0, mac, 6);
if (ret == 6)
ret = WARM;
else
ret = COLD;
kfree(mac);
if (ret == COLD) {
__az6007_write(d->udev, 0x09, 1, 0, NULL, 0);
__az6007_write(d->udev, 0x00, 0, 0, NULL, 0);
__az6007_write(d->udev, 0x00, 0, 0, NULL, 0);
}
pr_debug("Device is on %s state\n",
ret == WARM ? "warm" : "cold");
return ret;
}
static void az6007_usb_disconnect(struct usb_interface *intf)
{
struct dvb_usb_device *d = usb_get_intfdata(intf);
az6007_ci_uninit(d);
dvb_usbv2_disconnect(intf);
}
static int az6007_download_firmware(struct dvb_usb_device *d,
const struct firmware *fw)
{
pr_debug("Loading az6007 firmware\n");
return cypress_load_firmware(d->udev, fw, CYPRESS_FX2);
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties az6007_props = {
.driver_name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.firmware = AZ6007_FIRMWARE,
.adapter_nr = adapter_nr,
.size_of_priv = sizeof(struct az6007_device_state),
.i2c_algo = &az6007_i2c_algo,
.tuner_attach = az6007_tuner_attach,
.frontend_attach = az6007_frontend_attach,
.streaming_ctrl = az6007_streaming_ctrl,
.get_rc_config = az6007_get_rc_config,
.read_mac_address = az6007_read_mac_addr,
.download_firmware = az6007_download_firmware,
.identify_state = az6007_identify_state,
.power_ctrl = az6007_power_ctrl,
.num_adapters = 1,
.adapter = {
{ .stream = DVB_USB_STREAM_BULK(0x02, 10, 4096), }
}
};
static struct dvb_usb_device_properties az6007_cablestar_hdci_props = {
.driver_name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.firmware = AZ6007_FIRMWARE,
.adapter_nr = adapter_nr,
.size_of_priv = sizeof(struct az6007_device_state),
.i2c_algo = &az6007_i2c_algo,
.tuner_attach = az6007_tuner_attach,
.frontend_attach = az6007_cablestar_hdci_frontend_attach,
.streaming_ctrl = az6007_streaming_ctrl,
/* ditch get_rc_config as it can't work (TS35 remote, I believe it's rc5) */
.get_rc_config = NULL,
.read_mac_address = az6007_read_mac_addr,
.download_firmware = az6007_download_firmware,
.identify_state = az6007_identify_state,
.power_ctrl = az6007_power_ctrl,
.num_adapters = 1,
.adapter = {
{ .stream = DVB_USB_STREAM_BULK(0x02, 10, 4096), }
}
};
static struct usb_device_id az6007_usb_table[] = {
{DVB_USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007,
&az6007_props, "Azurewave 6007", RC_MAP_EMPTY)},
{DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7,
&az6007_props, "Terratec H7", RC_MAP_NEC_TERRATEC_CINERGY_XS)},
{DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7_2,
&az6007_props, "Terratec H7", RC_MAP_NEC_TERRATEC_CINERGY_XS)},
{DVB_USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_CABLESTAR_HDCI,
&az6007_cablestar_hdci_props, "Technisat CableStar Combo HD CI", RC_MAP_EMPTY)},
{0},
};
MODULE_DEVICE_TABLE(usb, az6007_usb_table);
static int az6007_suspend(struct usb_interface *intf, pm_message_t msg)
{
struct dvb_usb_device *d = usb_get_intfdata(intf);
az6007_ci_uninit(d);
return dvb_usbv2_suspend(intf, msg);
}
static int az6007_resume(struct usb_interface *intf)
{
struct dvb_usb_device *d = usb_get_intfdata(intf);
struct dvb_usb_adapter *adap = &d->adapter[0];
az6007_ci_init(adap);
return dvb_usbv2_resume(intf);
}
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver az6007_usb_driver = {
.name = KBUILD_MODNAME,
.id_table = az6007_usb_table,
.probe = dvb_usbv2_probe,
.disconnect = az6007_usb_disconnect,
.no_dynamic_id = 1,
.soft_unbind = 1,
/*
* FIXME: need to implement reset_resume, likely with
* dvb-usb-v2 core support
*/
.suspend = az6007_suspend,
.resume = az6007_resume,
};
module_usb_driver(az6007_usb_driver);
MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>");
MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(AZ6007_FIRMWARE);

View file

@ -0,0 +1,292 @@
/*
* Intel CE6230 DVB USB driver
*
* Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "ce6230.h"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int ce6230_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req)
{
int ret;
unsigned int pipe;
u8 request;
u8 requesttype;
u16 value;
u16 index;
u8 *buf;
request = req->cmd;
value = req->value;
index = req->index;
switch (req->cmd) {
case I2C_READ:
case DEMOD_READ:
case REG_READ:
requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
break;
case I2C_WRITE:
case DEMOD_WRITE:
case REG_WRITE:
requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
break;
default:
dev_err(&d->udev->dev, "%s: unknown command=%02x\n",
KBUILD_MODNAME, req->cmd);
ret = -EINVAL;
goto error;
}
buf = kmalloc(req->data_len, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto error;
}
if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) {
/* write */
memcpy(buf, req->data, req->data_len);
pipe = usb_sndctrlpipe(d->udev, 0);
} else {
/* read */
pipe = usb_rcvctrlpipe(d->udev, 0);
}
msleep(1); /* avoid I2C errors */
ret = usb_control_msg(d->udev, pipe, request, requesttype, value, index,
buf, req->data_len, CE6230_USB_TIMEOUT);
dvb_usb_dbg_usb_control_msg(d->udev, request, requesttype, value, index,
buf, req->data_len);
if (ret < 0)
dev_err(&d->udev->dev, "%s: usb_control_msg() failed=%d\n",
KBUILD_MODNAME, ret);
else
ret = 0;
/* read request, copy returned data to return buf */
if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
memcpy(req->data, buf, req->data_len);
kfree(buf);
error:
return ret;
}
/* I2C */
static struct zl10353_config ce6230_zl10353_config;
static int ce6230_i2c_master_xfer(struct i2c_adapter *adap,
struct i2c_msg msg[], int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int ret = 0, i = 0;
struct usb_req req;
if (num > 2)
return -EOPNOTSUPP;
memset(&req, 0, sizeof(req));
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
while (i < num) {
if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
if (msg[i].addr ==
ce6230_zl10353_config.demod_address) {
req.cmd = DEMOD_READ;
req.value = msg[i].addr >> 1;
req.index = msg[i].buf[0];
req.data_len = msg[i+1].len;
req.data = &msg[i+1].buf[0];
ret = ce6230_ctrl_msg(d, &req);
} else {
dev_err(&d->udev->dev, "%s: I2C read not " \
"implemented\n",
KBUILD_MODNAME);
ret = -EOPNOTSUPP;
}
i += 2;
} else {
if (msg[i].addr ==
ce6230_zl10353_config.demod_address) {
req.cmd = DEMOD_WRITE;
req.value = msg[i].addr >> 1;
req.index = msg[i].buf[0];
req.data_len = msg[i].len-1;
req.data = &msg[i].buf[1];
ret = ce6230_ctrl_msg(d, &req);
} else {
req.cmd = I2C_WRITE;
req.value = 0x2000 + (msg[i].addr >> 1);
req.index = 0x0000;
req.data_len = msg[i].len;
req.data = &msg[i].buf[0];
ret = ce6230_ctrl_msg(d, &req);
}
i += 1;
}
if (ret)
break;
}
mutex_unlock(&d->i2c_mutex);
return ret ? ret : i;
}
static u32 ce6230_i2c_functionality(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm ce6230_i2c_algorithm = {
.master_xfer = ce6230_i2c_master_xfer,
.functionality = ce6230_i2c_functionality,
};
/* Callbacks for DVB USB */
static struct zl10353_config ce6230_zl10353_config = {
.demod_address = 0x1e,
.adc_clock = 450000,
.if2 = 45700,
.no_tuner = 1,
.parallel_ts = 1,
.clock_ctl_1 = 0x34,
.pll_0 = 0x0e,
};
static int ce6230_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
{
struct dvb_usb_device *d = adap_to_d(adap);
dev_dbg(&d->udev->dev, "%s:\n", __func__);
adap->fe[0] = dvb_attach(zl10353_attach, &ce6230_zl10353_config,
&d->i2c_adap);
if (adap->fe[0] == NULL)
return -ENODEV;
return 0;
}
static struct mxl5005s_config ce6230_mxl5003s_config = {
.i2c_address = 0xc6,
.if_freq = IF_FREQ_4570000HZ,
.xtal_freq = CRYSTAL_FREQ_16000000HZ,
.agc_mode = MXL_SINGLE_AGC,
.tracking_filter = MXL_TF_DEFAULT,
.rssi_enable = MXL_RSSI_ENABLE,
.cap_select = MXL_CAP_SEL_ENABLE,
.div_out = MXL_DIV_OUT_4,
.clock_out = MXL_CLOCK_OUT_DISABLE,
.output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
.top = MXL5005S_TOP_25P2,
.mod_mode = MXL_DIGITAL_MODE,
.if_mode = MXL_ZERO_IF,
.AgcMasterByte = 0x00,
};
static int ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dvb_usb_device *d = adap_to_d(adap);
int ret;
dev_dbg(&d->udev->dev, "%s:\n", __func__);
ret = dvb_attach(mxl5005s_attach, adap->fe[0], &d->i2c_adap,
&ce6230_mxl5003s_config) == NULL ? -ENODEV : 0;
return ret;
}
static int ce6230_power_ctrl(struct dvb_usb_device *d, int onoff)
{
int ret;
dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
/* InterfaceNumber 1 / AlternateSetting 0 idle
InterfaceNumber 1 / AlternateSetting 1 streaming */
ret = usb_set_interface(d->udev, 1, onoff);
if (ret)
dev_err(&d->udev->dev, "%s: usb_set_interface() failed=%d\n",
KBUILD_MODNAME, ret);
return ret;
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties ce6230_props = {
.driver_name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.adapter_nr = adapter_nr,
.bInterfaceNumber = 1,
.i2c_algo = &ce6230_i2c_algorithm,
.power_ctrl = ce6230_power_ctrl,
.frontend_attach = ce6230_zl10353_frontend_attach,
.tuner_attach = ce6230_mxl5003s_tuner_attach,
.num_adapters = 1,
.adapter = {
{
.stream = {
.type = USB_BULK,
.count = 6,
.endpoint = 0x82,
.u = {
.bulk = {
.buffersize = (16 * 512),
}
}
},
}
},
};
static const struct usb_device_id ce6230_id_table[] = {
{ DVB_USB_DEVICE(USB_VID_INTEL, USB_PID_INTEL_CE9500,
&ce6230_props, "Intel CE9500 reference design", NULL) },
{ DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A310,
&ce6230_props, "AVerMedia A310 USB 2.0 DVB-T tuner", NULL) },
{ }
};
MODULE_DEVICE_TABLE(usb, ce6230_id_table);
static struct usb_driver ce6230_usb_driver = {
.name = KBUILD_MODNAME,
.id_table = ce6230_id_table,
.probe = dvb_usbv2_probe,
.disconnect = dvb_usbv2_disconnect,
.suspend = dvb_usbv2_suspend,
.resume = dvb_usbv2_resume,
.reset_resume = dvb_usbv2_reset_resume,
.no_dynamic_id = 1,
.soft_unbind = 1,
};
module_usb_driver(ce6230_usb_driver);
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
MODULE_DESCRIPTION("Intel CE6230 driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,50 @@
/*
* Intel CE6230 DVB USB driver
*
* Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef CE6230_H
#define CE6230_H
#include "dvb_usb.h"
#include "zl10353.h"
#include "mxl5005s.h"
#define CE6230_USB_TIMEOUT 1000
struct usb_req {
u8 cmd; /* [1] */
u16 value; /* [2|3] */
u16 index; /* [4|5] */
u16 data_len; /* [6|7] */
u8 *data;
};
enum ce6230_cmd {
CONFIG_READ = 0xd0, /* rd 0 (unclear) */
UNKNOWN_WRITE = 0xc7, /* wr 7 (unclear) */
I2C_READ = 0xd9, /* rd 9 (unclear) */
I2C_WRITE = 0xca, /* wr a */
DEMOD_READ = 0xdb, /* rd b */
DEMOD_WRITE = 0xcc, /* wr c */
REG_READ = 0xde, /* rd e */
REG_WRITE = 0xcf, /* wr f */
};
#endif

View file

@ -0,0 +1,409 @@
/*
* DVB USB framework
*
* Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@desy.de>
* Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef DVB_USB_H
#define DVB_USB_H
#include <linux/usb/input.h>
#include <linux/firmware.h>
#include <media/rc-core.h>
#include "dvb_frontend.h"
#include "dvb_demux.h"
#include "dvb_net.h"
#include "dmxdev.h"
#include "dvb-usb-ids.h"
/*
* device file: /dev/dvb/adapter[0-1]/frontend[0-2]
*
* |-- device
* | |-- adapter0
* | | |-- frontend0
* | | |-- frontend1
* | | `-- frontend2
* | `-- adapter1
* | |-- frontend0
* | |-- frontend1
* | `-- frontend2
*
*
* Commonly used variable names:
* d = pointer to device (struct dvb_usb_device *)
* adap = pointer to adapter (struct dvb_usb_adapter *)
* fe = pointer to frontend (struct dvb_frontend *)
*
* Use macros defined in that file to resolve needed pointers.
*/
/* helper macros for every DVB USB driver use */
#define adap_to_d(adap) (container_of(adap, struct dvb_usb_device, \
adapter[adap->id]))
#define adap_to_priv(adap) (adap_to_d(adap)->priv)
#define fe_to_adap(fe) ((struct dvb_usb_adapter *) ((fe)->dvb->priv))
#define fe_to_d(fe) (adap_to_d(fe_to_adap(fe)))
#define fe_to_priv(fe) (fe_to_d(fe)->priv)
#define d_to_priv(d) (d->priv)
#define dvb_usb_dbg_usb_control_msg(udev, r, t, v, i, b, l) { \
char *direction; \
if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \
direction = ">>>"; \
else \
direction = "<<<"; \
dev_dbg(&udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \
"%s %*ph\n", __func__, t, r, v & 0xff, v >> 8, \
i & 0xff, i >> 8, l & 0xff, l >> 8, direction, l, b); \
}
#define DVB_USB_STREAM_BULK(endpoint_, count_, size_) { \
.type = USB_BULK, \
.count = count_, \
.endpoint = endpoint_, \
.u = { \
.bulk = { \
.buffersize = size_, \
} \
} \
}
#define DVB_USB_STREAM_ISOC(endpoint_, count_, frames_, size_, interval_) { \
.type = USB_ISOC, \
.count = count_, \
.endpoint = endpoint_, \
.u = { \
.isoc = { \
.framesperurb = frames_, \
.framesize = size_,\
.interval = interval_, \
} \
} \
}
#define DVB_USB_DEVICE(vend, prod, props_, name_, rc) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
.idVendor = (vend), \
.idProduct = (prod), \
.driver_info = (kernel_ulong_t) &((const struct dvb_usb_driver_info) { \
.props = (props_), \
.name = (name_), \
.rc_map = (rc), \
})
struct dvb_usb_device;
struct dvb_usb_adapter;
/**
* structure for carrying all needed data from the device driver to the general
* dvb usb routines
* @name: device name
* @rc_map: name of rc codes table
* @props: structure containing all device properties
*/
struct dvb_usb_driver_info {
const char *name;
const char *rc_map;
const struct dvb_usb_device_properties *props;
};
/**
* structure for remote controller configuration
* @map_name: name of rc codes table
* @allowed_protos: protocol(s) supported by the driver
* @change_protocol: callback to change protocol
* @query: called to query an event from the device
* @interval: time in ms between two queries
* @driver_type: used to point if a device supports raw mode
* @bulk_mode: device supports bulk mode for rc (disable polling mode)
*/
struct dvb_usb_rc {
const char *map_name;
u64 allowed_protos;
int (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
int (*query) (struct dvb_usb_device *d);
unsigned int interval;
enum rc_driver_type driver_type;
bool bulk_mode;
};
/**
* usb streaming configration for adapter
* @type: urb type
* @count: count of used urbs
* @endpoint: stream usb endpoint number
*/
struct usb_data_stream_properties {
#define USB_BULK 1
#define USB_ISOC 2
u8 type;
u8 count;
u8 endpoint;
union {
struct {
unsigned int buffersize; /* per URB */
} bulk;
struct {
int framesperurb;
int framesize;
int interval;
} isoc;
} u;
};
/**
* properties of dvb usb device adapter
* @caps: adapter capabilities
* @pid_filter_count: pid count of adapter pid-filter
* @pid_filter_ctrl: called to enable/disable pid-filter
* @pid_filter: called to set/unset pid for filtering
* @stream: adapter usb stream configuration
*/
#define MAX_NO_OF_FE_PER_ADAP 3
struct dvb_usb_adapter_properties {
#define DVB_USB_ADAP_HAS_PID_FILTER 0x01
#define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02
#define DVB_USB_ADAP_NEED_PID_FILTERING 0x04
u8 caps;
u8 pid_filter_count;
int (*pid_filter_ctrl) (struct dvb_usb_adapter *, int);
int (*pid_filter) (struct dvb_usb_adapter *, int, u16, int);
struct usb_data_stream_properties stream;
};
/**
* struct dvb_usb_device_properties - properties of a dvb-usb-device
* @driver_name: name of the owning driver module
* @owner: owner of the dvb_adapter
* @adapter_nr: values from the DVB_DEFINE_MOD_OPT_ADAPTER_NR() macro
* @bInterfaceNumber: usb interface number driver binds
* @size_of_priv: bytes allocated for the driver private data
* @generic_bulk_ctrl_endpoint: bulk control endpoint number for sent
* @generic_bulk_ctrl_endpoint_response: bulk control endpoint number for
* receive
* @generic_bulk_ctrl_delay: delay between bulk control sent and receive message
* @identify_state: called to determine the firmware state (cold or warm) and
* return possible firmware file name to be loaded
* @firmware: name of the firmware file to be loaded
* @download_firmware: called to download the firmware
* @i2c_algo: i2c_algorithm if the device has i2c-adapter
* @num_adapters: dvb usb device adapter count
* @get_adapter_count: called to resolve adapter count
* @adapter: array of all adapter properties of device
* @power_ctrl: called to enable/disable power of the device
* @read_config: called to resolve device configuration
* @read_mac_address: called to resolve adapter mac-address
* @frontend_attach: called to attach the possible frontends
* @frontend_detach: called to detach the possible frontends
* @tuner_attach: called to attach the possible tuners
* @frontend_ctrl: called to power on/off active frontend
* @streaming_ctrl: called to start/stop the usb streaming of adapter
* @init: called after adapters are created in order to finalize device
* configuration
* @exit: called when driver is unloaded
* @get_rc_config: called to resolve used remote controller configuration
* @get_stream_config: called to resolve input and output stream configuration
* of the adapter just before streaming is started. input stream is transport
* stream from the demodulator and output stream is usb stream to host.
*/
#define MAX_NO_OF_ADAPTER_PER_DEVICE 2
struct dvb_usb_device_properties {
const char *driver_name;
struct module *owner;
short *adapter_nr;
u8 bInterfaceNumber;
unsigned int size_of_priv;
u8 generic_bulk_ctrl_endpoint;
u8 generic_bulk_ctrl_endpoint_response;
unsigned int generic_bulk_ctrl_delay;
#define WARM 0
#define COLD 1
int (*identify_state) (struct dvb_usb_device *, const char **);
const char *firmware;
#define RECONNECTS_USB 1
int (*download_firmware) (struct dvb_usb_device *,
const struct firmware *);
struct i2c_algorithm *i2c_algo;
unsigned int num_adapters;
int (*get_adapter_count) (struct dvb_usb_device *);
struct dvb_usb_adapter_properties adapter[MAX_NO_OF_ADAPTER_PER_DEVICE];
int (*power_ctrl) (struct dvb_usb_device *, int);
int (*read_config) (struct dvb_usb_device *d);
int (*read_mac_address) (struct dvb_usb_adapter *, u8 []);
int (*frontend_attach) (struct dvb_usb_adapter *);
int (*frontend_detach)(struct dvb_usb_adapter *);
int (*tuner_attach) (struct dvb_usb_adapter *);
int (*tuner_detach)(struct dvb_usb_adapter *);
int (*frontend_ctrl) (struct dvb_frontend *, int);
int (*streaming_ctrl) (struct dvb_frontend *, int);
int (*init) (struct dvb_usb_device *);
void (*exit) (struct dvb_usb_device *);
int (*get_rc_config) (struct dvb_usb_device *, struct dvb_usb_rc *);
#define DVB_USB_FE_TS_TYPE_188 0
#define DVB_USB_FE_TS_TYPE_204 1
#define DVB_USB_FE_TS_TYPE_RAW 2
int (*get_stream_config) (struct dvb_frontend *, u8 *,
struct usb_data_stream_properties *);
};
/**
* generic object of an usb stream
* @buf_num: number of buffer allocated
* @buf_size: size of each buffer in buf_list
* @buf_list: array containing all allocate buffers for streaming
* @dma_addr: list of dma_addr_t for each buffer in buf_list
*
* @urbs_initialized: number of URBs initialized
* @urbs_submitted: number of URBs submitted
*/
#define MAX_NO_URBS_FOR_DATA_STREAM 10
struct usb_data_stream {
struct usb_device *udev;
struct usb_data_stream_properties props;
#define USB_STATE_INIT 0x00
#define USB_STATE_URB_BUF 0x01
u8 state;
void (*complete) (struct usb_data_stream *, u8 *, size_t);
struct urb *urb_list[MAX_NO_URBS_FOR_DATA_STREAM];
int buf_num;
unsigned long buf_size;
u8 *buf_list[MAX_NO_URBS_FOR_DATA_STREAM];
dma_addr_t dma_addr[MAX_NO_URBS_FOR_DATA_STREAM];
int urbs_initialized;
int urbs_submitted;
void *user_priv;
};
/**
* dvb adapter object on dvb usb device
* @props: pointer to adapter properties
* @stream: adapter the usb data stream
* @id: index of this adapter (starting with 0)
* @ts_type: transport stream, input stream, type
* @suspend_resume_active: set when there is ongoing suspend / resume
* @pid_filtering: is hardware pid_filtering used or not
* @feed_count: current feed count
* @max_feed_count: maimum feed count device can handle
* @dvb_adap: adapter dvb_adapter
* @dmxdev: adapter dmxdev
* @demux: adapter software demuxer
* @dvb_net: adapter dvb_net interfaces
* @sync_mutex: mutex used to sync control and streaming of the adapter
* @fe: adapter frontends
* @fe_init: rerouted frontend-init function
* @fe_sleep: rerouted frontend-sleep function
*/
struct dvb_usb_adapter {
const struct dvb_usb_adapter_properties *props;
struct usb_data_stream stream;
u8 id;
u8 ts_type;
bool suspend_resume_active;
bool pid_filtering;
u8 feed_count;
u8 max_feed_count;
s8 active_fe;
#define ADAP_INIT 0
#define ADAP_SLEEP 1
#define ADAP_STREAMING 2
unsigned long state_bits;
/* dvb */
struct dvb_adapter dvb_adap;
struct dmxdev dmxdev;
struct dvb_demux demux;
struct dvb_net dvb_net;
struct dvb_frontend *fe[MAX_NO_OF_FE_PER_ADAP];
int (*fe_init[MAX_NO_OF_FE_PER_ADAP]) (struct dvb_frontend *);
int (*fe_sleep[MAX_NO_OF_FE_PER_ADAP]) (struct dvb_frontend *);
};
/**
* dvb usb device object
* @props: device properties
* @name: device name
* @rc_map: name of rc codes table
* @rc_polling_active: set when RC polling is active
* @udev: pointer to the device's struct usb_device
* @rc: remote controller configuration
* @powered: indicated whether the device is power or not
* @usb_mutex: mutex for usb control messages
* @i2c_mutex: mutex for i2c-transfers
* @i2c_adap: device's i2c-adapter
* @rc_dev: rc device for the remote control
* @rc_query_work: work for polling remote
* @priv: private data of the actual driver (allocate by dvb usb, size defined
* in size_of_priv of dvb_usb_properties).
*/
struct dvb_usb_device {
const struct dvb_usb_device_properties *props;
const char *name;
const char *rc_map;
bool rc_polling_active;
struct usb_device *udev;
struct dvb_usb_rc rc;
int powered;
/* locking */
struct mutex usb_mutex;
/* i2c */
struct mutex i2c_mutex;
struct i2c_adapter i2c_adap;
struct dvb_usb_adapter adapter[MAX_NO_OF_ADAPTER_PER_DEVICE];
/* remote control */
struct rc_dev *rc_dev;
char rc_phys[64];
struct delayed_work rc_query_work;
void *priv;
};
extern int dvb_usbv2_probe(struct usb_interface *,
const struct usb_device_id *);
extern void dvb_usbv2_disconnect(struct usb_interface *);
extern int dvb_usbv2_suspend(struct usb_interface *, pm_message_t);
extern int dvb_usbv2_resume(struct usb_interface *);
extern int dvb_usbv2_reset_resume(struct usb_interface *);
/* the generic read/write method for device control */
extern int dvb_usbv2_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16);
extern int dvb_usbv2_generic_write(struct dvb_usb_device *, u8 *, u16);
/* caller must hold lock when locked versions are called */
extern int dvb_usbv2_generic_rw_locked(struct dvb_usb_device *,
u8 *, u16, u8 *, u16);
extern int dvb_usbv2_generic_write_locked(struct dvb_usb_device *, u8 *, u16);
#endif

View file

@ -0,0 +1,35 @@
/*
* DVB USB framework
*
* Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@desy.de>
* Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef DVB_USB_COMMON_H
#define DVB_USB_COMMON_H
#include "dvb_usb.h"
/* commonly used methods */
extern int usb_urb_initv2(struct usb_data_stream *stream,
const struct usb_data_stream_properties *props);
extern int usb_urb_exitv2(struct usb_data_stream *stream);
extern int usb_urb_submitv2(struct usb_data_stream *stream,
struct usb_data_stream_properties *props);
extern int usb_urb_killv2(struct usb_data_stream *stream);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,104 @@
/*
* DVB USB framework
*
* Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@desy.de>
* Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "dvb_usb_common.h"
static int dvb_usb_v2_generic_io(struct dvb_usb_device *d,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
int ret, actual_length;
if (!wbuf || !wlen || !d->props->generic_bulk_ctrl_endpoint ||
!d->props->generic_bulk_ctrl_endpoint_response) {
dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, -EINVAL);
return -EINVAL;
}
dev_dbg(&d->udev->dev, "%s: >>> %*ph\n", __func__, wlen, wbuf);
ret = usb_bulk_msg(d->udev, usb_sndbulkpipe(d->udev,
d->props->generic_bulk_ctrl_endpoint), wbuf, wlen,
&actual_length, 2000);
if (ret < 0)
dev_err(&d->udev->dev, "%s: usb_bulk_msg() failed=%d\n",
KBUILD_MODNAME, ret);
else
ret = actual_length != wlen ? -EIO : 0;
/* an answer is expected, and no error before */
if (!ret && rbuf && rlen) {
if (d->props->generic_bulk_ctrl_delay)
usleep_range(d->props->generic_bulk_ctrl_delay,
d->props->generic_bulk_ctrl_delay
+ 20000);
ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev,
d->props->generic_bulk_ctrl_endpoint_response),
rbuf, rlen, &actual_length, 2000);
if (ret)
dev_err(&d->udev->dev,
"%s: 2nd usb_bulk_msg() failed=%d\n",
KBUILD_MODNAME, ret);
dev_dbg(&d->udev->dev, "%s: <<< %*ph\n", __func__,
actual_length, rbuf);
}
return ret;
}
int dvb_usbv2_generic_rw(struct dvb_usb_device *d,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
int ret;
mutex_lock(&d->usb_mutex);
ret = dvb_usb_v2_generic_io(d, wbuf, wlen, rbuf, rlen);
mutex_unlock(&d->usb_mutex);
return ret;
}
EXPORT_SYMBOL(dvb_usbv2_generic_rw);
int dvb_usbv2_generic_write(struct dvb_usb_device *d, u8 *buf, u16 len)
{
int ret;
mutex_lock(&d->usb_mutex);
ret = dvb_usb_v2_generic_io(d, buf, len, NULL, 0);
mutex_unlock(&d->usb_mutex);
return ret;
}
EXPORT_SYMBOL(dvb_usbv2_generic_write);
int dvb_usbv2_generic_rw_locked(struct dvb_usb_device *d,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
return dvb_usb_v2_generic_io(d, wbuf, wlen, rbuf, rlen);
}
EXPORT_SYMBOL(dvb_usbv2_generic_rw_locked);
int dvb_usbv2_generic_write_locked(struct dvb_usb_device *d, u8 *buf, u16 len)
{
return dvb_usb_v2_generic_io(d, buf, len, NULL, 0);
}
EXPORT_SYMBOL(dvb_usbv2_generic_write_locked);

View file

@ -0,0 +1,460 @@
/*
* Driver for DVBSky USB2.0 receiver
*
* Copyright (C) 2013 Max nibble <nibble.max@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "dvb_usb.h"
#include "m88ds3103.h"
#include "m88ts2022.h"
#define DVBSKY_MSG_DELAY 0/*2000*/
#define DVBSKY_BUF_LEN 64
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
struct dvbsky_state {
struct mutex stream_mutex;
u8 ibuf[DVBSKY_BUF_LEN];
u8 obuf[DVBSKY_BUF_LEN];
u8 last_lock;
struct i2c_client *i2c_client_tuner;
/* fe hook functions*/
int (*fe_set_voltage)(struct dvb_frontend *fe,
fe_sec_voltage_t voltage);
int (*fe_read_status)(struct dvb_frontend *fe,
fe_status_t *status);
};
static int dvbsky_usb_generic_rw(struct dvb_usb_device *d,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
int ret;
struct dvbsky_state *state = d_to_priv(d);
mutex_lock(&d->usb_mutex);
if (wlen != 0)
memcpy(state->obuf, wbuf, wlen);
ret = dvb_usbv2_generic_rw_locked(d, state->obuf, wlen,
state->ibuf, rlen);
if (!ret && (rlen != 0))
memcpy(rbuf, state->ibuf, rlen);
mutex_unlock(&d->usb_mutex);
return ret;
}
static int dvbsky_stream_ctrl(struct dvb_usb_device *d, u8 onoff)
{
struct dvbsky_state *state = d_to_priv(d);
int ret;
u8 obuf_pre[3] = { 0x37, 0, 0 };
u8 obuf_post[3] = { 0x36, 3, 0 };
mutex_lock(&state->stream_mutex);
ret = dvbsky_usb_generic_rw(d, obuf_pre, 3, NULL, 0);
if (!ret && onoff) {
msleep(20);
ret = dvbsky_usb_generic_rw(d, obuf_post, 3, NULL, 0);
}
mutex_unlock(&state->stream_mutex);
return ret;
}
static int dvbsky_streaming_ctrl(struct dvb_frontend *fe, int onoff)
{
struct dvb_usb_device *d = fe_to_d(fe);
return dvbsky_stream_ctrl(d, (onoff == 0) ? 0 : 1);
}
/* GPIO */
static int dvbsky_gpio_ctrl(struct dvb_usb_device *d, u8 gport, u8 value)
{
int ret;
u8 obuf[3], ibuf[2];
obuf[0] = 0x0e;
obuf[1] = gport;
obuf[2] = value;
ret = dvbsky_usb_generic_rw(d, obuf, 3, ibuf, 1);
if (ret)
dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
KBUILD_MODNAME, __func__, ret);
return ret;
}
/* I2C */
static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int ret = 0;
u8 ibuf[64], obuf[64];
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
if (num > 2) {
dev_err(&d->udev->dev,
"dvbsky_usb: too many i2c messages[%d] than 2.", num);
ret = -EOPNOTSUPP;
goto i2c_error;
}
if (num == 1) {
if (msg[0].len > 60) {
dev_err(&d->udev->dev,
"dvbsky_usb: too many i2c bytes[%d] than 60.",
msg[0].len);
ret = -EOPNOTSUPP;
goto i2c_error;
}
if (msg[0].flags & I2C_M_RD) {
/* single read */
obuf[0] = 0x09;
obuf[1] = 0;
obuf[2] = msg[0].len;
obuf[3] = msg[0].addr;
ret = dvbsky_usb_generic_rw(d, obuf, 4,
ibuf, msg[0].len + 1);
if (ret)
dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
KBUILD_MODNAME, __func__, ret);
if (!ret)
memcpy(msg[0].buf, &ibuf[1], msg[0].len);
} else {
/* write */
obuf[0] = 0x08;
obuf[1] = msg[0].addr;
obuf[2] = msg[0].len;
memcpy(&obuf[3], msg[0].buf, msg[0].len);
ret = dvbsky_usb_generic_rw(d, obuf,
msg[0].len + 3, ibuf, 1);
if (ret)
dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
KBUILD_MODNAME, __func__, ret);
}
} else {
if ((msg[0].len > 60) || (msg[1].len > 60)) {
dev_err(&d->udev->dev,
"dvbsky_usb: too many i2c bytes[w-%d][r-%d] than 60.",
msg[0].len, msg[1].len);
ret = -EOPNOTSUPP;
goto i2c_error;
}
/* write then read */
obuf[0] = 0x09;
obuf[1] = msg[0].len;
obuf[2] = msg[1].len;
obuf[3] = msg[0].addr;
memcpy(&obuf[4], msg[0].buf, msg[0].len);
ret = dvbsky_usb_generic_rw(d, obuf,
msg[0].len + 4, ibuf, msg[1].len + 1);
if (ret)
dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
KBUILD_MODNAME, __func__, ret);
if (!ret)
memcpy(msg[1].buf, &ibuf[1], msg[1].len);
}
i2c_error:
mutex_unlock(&d->i2c_mutex);
return (ret) ? ret : num;
}
static u32 dvbsky_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm dvbsky_i2c_algo = {
.master_xfer = dvbsky_i2c_xfer,
.functionality = dvbsky_i2c_func,
};
#if IS_ENABLED(CONFIG_RC_CORE)
static int dvbsky_rc_query(struct dvb_usb_device *d)
{
u32 code = 0xffff, scancode;
u8 rc5_command, rc5_system;
u8 obuf[2], ibuf[2], toggle;
int ret;
obuf[0] = 0x10;
ret = dvbsky_usb_generic_rw(d, obuf, 1, ibuf, 2);
if (ret)
dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
KBUILD_MODNAME, __func__, ret);
if (ret == 0)
code = (ibuf[0] << 8) | ibuf[1];
if (code != 0xffff) {
dev_dbg(&d->udev->dev, "rc code: %x\n", code);
rc5_command = code & 0x3F;
rc5_system = (code & 0x7C0) >> 6;
toggle = (code & 0x800) ? 1 : 0;
scancode = rc5_system << 8 | rc5_command;
rc_keydown(d->rc_dev, RC_TYPE_RC5, scancode, toggle);
}
return 0;
}
static int dvbsky_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
{
rc->allowed_protos = RC_BIT_RC5;
rc->query = dvbsky_rc_query;
rc->interval = 300;
return 0;
}
#else
#define dvbsky_get_rc_config NULL
#endif
static int dvbsky_usb_set_voltage(struct dvb_frontend *fe,
fe_sec_voltage_t voltage)
{
struct dvb_usb_device *d = fe_to_d(fe);
struct dvbsky_state *state = d_to_priv(d);
u8 value;
if (voltage == SEC_VOLTAGE_OFF)
value = 0;
else
value = 1;
dvbsky_gpio_ctrl(d, 0x80, value);
return state->fe_set_voltage(fe, voltage);
}
static int dvbsky_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6])
{
struct dvb_usb_device *d = adap_to_d(adap);
u8 obuf[] = { 0x1e, 0x00 };
u8 ibuf[6] = { 0 };
struct i2c_msg msg[] = {
{
.addr = 0x51,
.flags = 0,
.buf = obuf,
.len = 2,
}, {
.addr = 0x51,
.flags = I2C_M_RD,
.buf = ibuf,
.len = 6,
}
};
if (i2c_transfer(&d->i2c_adap, msg, 2) == 2)
memcpy(mac, ibuf, 6);
dev_info(&d->udev->dev, "dvbsky_usb MAC address=%pM\n", mac);
return 0;
}
static int dvbsky_usb_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct dvb_usb_device *d = fe_to_d(fe);
struct dvbsky_state *state = d_to_priv(d);
int ret;
ret = state->fe_read_status(fe, status);
/* it need resync slave fifo when signal change from unlock to lock.*/
if ((*status & FE_HAS_LOCK) && (!state->last_lock))
dvbsky_stream_ctrl(d, 1);
state->last_lock = (*status & FE_HAS_LOCK) ? 1 : 0;
return ret;
}
static const struct m88ds3103_config dvbsky_s960_m88ds3103_config = {
.i2c_addr = 0x68,
.clock = 27000000,
.i2c_wr_max = 33,
.clock_out = 0,
.ts_mode = M88DS3103_TS_CI,
.ts_clk = 16000,
.ts_clk_pol = 0,
.agc = 0x99,
.lnb_hv_pol = 1,
.lnb_en_pol = 1,
};
static int dvbsky_s960_attach(struct dvb_usb_adapter *adap)
{
struct dvbsky_state *state = adap_to_priv(adap);
struct dvb_usb_device *d = adap_to_d(adap);
int ret = 0;
/* demod I2C adapter */
struct i2c_adapter *i2c_adapter;
struct i2c_client *client;
struct i2c_board_info info;
struct m88ts2022_config m88ts2022_config = {
.clock = 27000000,
};
memset(&info, 0, sizeof(struct i2c_board_info));
/* attach demod */
adap->fe[0] = dvb_attach(m88ds3103_attach,
&dvbsky_s960_m88ds3103_config,
&d->i2c_adap,
&i2c_adapter);
if (!adap->fe[0]) {
dev_err(&d->udev->dev, "dvbsky_s960_attach fail.\n");
ret = -ENODEV;
goto fail_attach;
}
/* attach tuner */
m88ts2022_config.fe = adap->fe[0];
strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
info.addr = 0x60;
info.platform_data = &m88ts2022_config;
request_module("m88ts2022");
client = i2c_new_device(i2c_adapter, &info);
if (client == NULL || client->dev.driver == NULL) {
dvb_frontend_detach(adap->fe[0]);
ret = -ENODEV;
goto fail_attach;
}
if (!try_module_get(client->dev.driver->owner)) {
i2c_unregister_device(client);
dvb_frontend_detach(adap->fe[0]);
ret = -ENODEV;
goto fail_attach;
}
/* delegate signal strength measurement to tuner */
adap->fe[0]->ops.read_signal_strength =
adap->fe[0]->ops.tuner_ops.get_rf_strength;
/* hook fe: need to resync the slave fifo when signal locks. */
state->fe_read_status = adap->fe[0]->ops.read_status;
adap->fe[0]->ops.read_status = dvbsky_usb_read_status;
/* hook fe: LNB off/on is control by Cypress usb chip. */
state->fe_set_voltage = adap->fe[0]->ops.set_voltage;
adap->fe[0]->ops.set_voltage = dvbsky_usb_set_voltage;
state->i2c_client_tuner = client;
fail_attach:
return ret;
}
static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name)
{
dvbsky_gpio_ctrl(d, 0x04, 1);
msleep(20);
dvbsky_gpio_ctrl(d, 0x83, 0);
dvbsky_gpio_ctrl(d, 0xc0, 1);
msleep(100);
dvbsky_gpio_ctrl(d, 0x83, 1);
dvbsky_gpio_ctrl(d, 0xc0, 0);
msleep(50);
return WARM;
}
static int dvbsky_init(struct dvb_usb_device *d)
{
struct dvbsky_state *state = d_to_priv(d);
/* use default interface */
/*
ret = usb_set_interface(d->udev, 0, 0);
if (ret)
return ret;
*/
mutex_init(&state->stream_mutex);
state->last_lock = 0;
return 0;
}
static void dvbsky_exit(struct dvb_usb_device *d)
{
struct dvbsky_state *state = d_to_priv(d);
struct i2c_client *client;
client = state->i2c_client_tuner;
/* remove I2C tuner */
if (client) {
module_put(client->dev.driver->owner);
i2c_unregister_device(client);
}
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties dvbsky_s960_props = {
.driver_name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.adapter_nr = adapter_nr,
.size_of_priv = sizeof(struct dvbsky_state),
.generic_bulk_ctrl_endpoint = 0x01,
.generic_bulk_ctrl_endpoint_response = 0x81,
.generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY,
.i2c_algo = &dvbsky_i2c_algo,
.frontend_attach = dvbsky_s960_attach,
.init = dvbsky_init,
.get_rc_config = dvbsky_get_rc_config,
.streaming_ctrl = dvbsky_streaming_ctrl,
.identify_state = dvbsky_identify_state,
.exit = dvbsky_exit,
.read_mac_address = dvbsky_read_mac_addr,
.num_adapters = 1,
.adapter = {
{
.stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
}
}
};
static const struct usb_device_id dvbsky_id_table[] = {
{ DVB_USB_DEVICE(0x0572, 0x6831,
&dvbsky_s960_props, "DVBSky S960/S860", RC_MAP_DVBSKY) },
{ }
};
MODULE_DEVICE_TABLE(usb, dvbsky_id_table);
static struct usb_driver dvbsky_usb_driver = {
.name = KBUILD_MODNAME,
.id_table = dvbsky_id_table,
.probe = dvb_usbv2_probe,
.disconnect = dvb_usbv2_disconnect,
.suspend = dvb_usbv2_suspend,
.resume = dvb_usbv2_resume,
.reset_resume = dvb_usbv2_reset_resume,
.no_dynamic_id = 1,
.soft_unbind = 1,
};
module_usb_driver(dvbsky_usb_driver);
MODULE_AUTHOR("Max nibble <nibble.max@gmail.com>");
MODULE_DESCRIPTION("Driver for DVBSky USB");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,385 @@
/*
* E3C EC168 DVB USB driver
*
* Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "ec168.h"
#include "ec100.h"
#include "mxl5005s.h"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req)
{
int ret;
unsigned int pipe;
u8 request, requesttype;
u8 *buf;
switch (req->cmd) {
case DOWNLOAD_FIRMWARE:
case GPIO:
case WRITE_I2C:
case STREAMING_CTRL:
requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
request = req->cmd;
break;
case READ_I2C:
requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
request = req->cmd;
break;
case GET_CONFIG:
requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
request = CONFIG;
break;
case SET_CONFIG:
requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
request = CONFIG;
break;
case WRITE_DEMOD:
requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
request = DEMOD_RW;
break;
case READ_DEMOD:
requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
request = DEMOD_RW;
break;
default:
dev_err(&d->udev->dev, "%s: unknown command=%02x\n",
KBUILD_MODNAME, req->cmd);
ret = -EINVAL;
goto error;
}
buf = kmalloc(req->size, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto error;
}
if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) {
/* write */
memcpy(buf, req->data, req->size);
pipe = usb_sndctrlpipe(d->udev, 0);
} else {
/* read */
pipe = usb_rcvctrlpipe(d->udev, 0);
}
msleep(1); /* avoid I2C errors */
ret = usb_control_msg(d->udev, pipe, request, requesttype, req->value,
req->index, buf, req->size, EC168_USB_TIMEOUT);
dvb_usb_dbg_usb_control_msg(d->udev, request, requesttype, req->value,
req->index, buf, req->size);
if (ret < 0)
goto err_dealloc;
else
ret = 0;
/* read request, copy returned data to return buf */
if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
memcpy(req->data, buf, req->size);
kfree(buf);
return ret;
err_dealloc:
kfree(buf);
error:
dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
/* I2C */
static struct ec100_config ec168_ec100_config;
static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct ec168_req req;
int i = 0;
int ret;
if (num > 2)
return -EINVAL;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
while (i < num) {
if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
if (msg[i].addr == ec168_ec100_config.demod_address) {
req.cmd = READ_DEMOD;
req.value = 0;
req.index = 0xff00 + msg[i].buf[0]; /* reg */
req.size = msg[i+1].len; /* bytes to read */
req.data = &msg[i+1].buf[0];
ret = ec168_ctrl_msg(d, &req);
i += 2;
} else {
dev_err(&d->udev->dev, "%s: I2C read not " \
"implemented\n",
KBUILD_MODNAME);
ret = -EOPNOTSUPP;
i += 2;
}
} else {
if (msg[i].addr == ec168_ec100_config.demod_address) {
req.cmd = WRITE_DEMOD;
req.value = msg[i].buf[1]; /* val */
req.index = 0xff00 + msg[i].buf[0]; /* reg */
req.size = 0;
req.data = NULL;
ret = ec168_ctrl_msg(d, &req);
i += 1;
} else {
req.cmd = WRITE_I2C;
req.value = msg[i].buf[0]; /* val */
req.index = 0x0100 + msg[i].addr; /* I2C addr */
req.size = msg[i].len-1;
req.data = &msg[i].buf[1];
ret = ec168_ctrl_msg(d, &req);
i += 1;
}
}
if (ret)
goto error;
}
ret = i;
error:
mutex_unlock(&d->i2c_mutex);
return ret;
}
static u32 ec168_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm ec168_i2c_algo = {
.master_xfer = ec168_i2c_xfer,
.functionality = ec168_i2c_func,
};
/* Callbacks for DVB USB */
static int ec168_identify_state(struct dvb_usb_device *d, const char **name)
{
int ret;
u8 reply;
struct ec168_req req = {GET_CONFIG, 0, 1, sizeof(reply), &reply};
dev_dbg(&d->udev->dev, "%s:\n", __func__);
ret = ec168_ctrl_msg(d, &req);
if (ret)
goto error;
dev_dbg(&d->udev->dev, "%s: reply=%02x\n", __func__, reply);
if (reply == 0x01)
ret = WARM;
else
ret = COLD;
return ret;
error:
dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
static int ec168_download_firmware(struct dvb_usb_device *d,
const struct firmware *fw)
{
int ret, len, remaining;
struct ec168_req req = {DOWNLOAD_FIRMWARE, 0, 0, 0, NULL};
dev_dbg(&d->udev->dev, "%s:\n", __func__);
#define LEN_MAX 2048 /* max packet size */
for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) {
len = remaining;
if (len > LEN_MAX)
len = LEN_MAX;
req.size = len;
req.data = (u8 *) &fw->data[fw->size - remaining];
req.index = fw->size - remaining;
ret = ec168_ctrl_msg(d, &req);
if (ret) {
dev_err(&d->udev->dev,
"%s: firmware download failed=%d\n",
KBUILD_MODNAME, ret);
goto error;
}
}
req.size = 0;
/* set "warm"? */
req.cmd = SET_CONFIG;
req.value = 0;
req.index = 0x0001;
ret = ec168_ctrl_msg(d, &req);
if (ret)
goto error;
/* really needed - no idea what does */
req.cmd = GPIO;
req.value = 0;
req.index = 0x0206;
ret = ec168_ctrl_msg(d, &req);
if (ret)
goto error;
/* activate tuner I2C? */
req.cmd = WRITE_I2C;
req.value = 0;
req.index = 0x00c6;
ret = ec168_ctrl_msg(d, &req);
if (ret)
goto error;
return ret;
error:
dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
static struct ec100_config ec168_ec100_config = {
.demod_address = 0xff, /* not real address, demod is integrated */
};
static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap)
{
struct dvb_usb_device *d = adap_to_d(adap);
dev_dbg(&d->udev->dev, "%s:\n", __func__);
adap->fe[0] = dvb_attach(ec100_attach, &ec168_ec100_config,
&d->i2c_adap);
if (adap->fe[0] == NULL)
return -ENODEV;
return 0;
}
static struct mxl5005s_config ec168_mxl5003s_config = {
.i2c_address = 0xc6,
.if_freq = IF_FREQ_4570000HZ,
.xtal_freq = CRYSTAL_FREQ_16000000HZ,
.agc_mode = MXL_SINGLE_AGC,
.tracking_filter = MXL_TF_OFF,
.rssi_enable = MXL_RSSI_ENABLE,
.cap_select = MXL_CAP_SEL_ENABLE,
.div_out = MXL_DIV_OUT_4,
.clock_out = MXL_CLOCK_OUT_DISABLE,
.output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
.top = MXL5005S_TOP_25P2,
.mod_mode = MXL_DIGITAL_MODE,
.if_mode = MXL_ZERO_IF,
.AgcMasterByte = 0x00,
};
static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dvb_usb_device *d = adap_to_d(adap);
dev_dbg(&d->udev->dev, "%s:\n", __func__);
return dvb_attach(mxl5005s_attach, adap->fe[0], &d->i2c_adap,
&ec168_mxl5003s_config) == NULL ? -ENODEV : 0;
}
static int ec168_streaming_ctrl(struct dvb_frontend *fe, int onoff)
{
struct dvb_usb_device *d = fe_to_d(fe);
struct ec168_req req = {STREAMING_CTRL, 0x7f01, 0x0202, 0, NULL};
dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
if (onoff)
req.index = 0x0102;
return ec168_ctrl_msg(d, &req);
}
/* DVB USB Driver stuff */
/* bInterfaceNumber 0 is HID
* bInterfaceNumber 1 is DVB-T */
static struct dvb_usb_device_properties ec168_props = {
.driver_name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.adapter_nr = adapter_nr,
.bInterfaceNumber = 1,
.identify_state = ec168_identify_state,
.firmware = EC168_FIRMWARE,
.download_firmware = ec168_download_firmware,
.i2c_algo = &ec168_i2c_algo,
.frontend_attach = ec168_ec100_frontend_attach,
.tuner_attach = ec168_mxl5003s_tuner_attach,
.streaming_ctrl = ec168_streaming_ctrl,
.num_adapters = 1,
.adapter = {
{
.stream = DVB_USB_STREAM_BULK(0x82, 6, 32 * 512),
}
},
};
static const struct dvb_usb_driver_info ec168_driver_info = {
.name = "E3C EC168 reference design",
.props = &ec168_props,
};
static const struct usb_device_id ec168_id[] = {
{ USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168),
.driver_info = (kernel_ulong_t) &ec168_driver_info },
{ USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_2),
.driver_info = (kernel_ulong_t) &ec168_driver_info },
{ USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_3),
.driver_info = (kernel_ulong_t) &ec168_driver_info },
{ USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_4),
.driver_info = (kernel_ulong_t) &ec168_driver_info },
{ USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_5),
.driver_info = (kernel_ulong_t) &ec168_driver_info },
{}
};
MODULE_DEVICE_TABLE(usb, ec168_id);
static struct usb_driver ec168_driver = {
.name = KBUILD_MODNAME,
.id_table = ec168_id,
.probe = dvb_usbv2_probe,
.disconnect = dvb_usbv2_disconnect,
.suspend = dvb_usbv2_suspend,
.resume = dvb_usbv2_resume,
.no_dynamic_id = 1,
.soft_unbind = 1,
};
module_usb_driver(ec168_driver);
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
MODULE_DESCRIPTION("E3C EC168 driver");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(EC168_FIRMWARE);

View file

@ -0,0 +1,53 @@
/*
* E3C EC168 DVB USB driver
*
* Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef EC168_H
#define EC168_H
#include "dvb_usb.h"
#define EC168_USB_TIMEOUT 1000
#define EC168_FIRMWARE "dvb-usb-ec168.fw"
struct ec168_req {
u8 cmd; /* [1] */
u16 value; /* [2|3] */
u16 index; /* [4|5] */
u16 size; /* [6|7] */
u8 *data;
};
enum ec168_cmd {
DOWNLOAD_FIRMWARE = 0x00,
CONFIG = 0x01,
DEMOD_RW = 0x03,
GPIO = 0x04,
STREAMING_CTRL = 0x10,
READ_I2C = 0x20,
WRITE_I2C = 0x21,
HID_DOWNLOAD = 0x30,
GET_CONFIG,
SET_CONFIG,
READ_DEMOD,
WRITE_DEMOD,
};
#endif

View file

@ -0,0 +1,177 @@
/* DVB USB compliant linux driver for GL861 USB2.0 devices.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "gl861.h"
#include "zl10353.h"
#include "qt1010.h"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
u16 index;
u16 value = addr << (8 + 1);
int wo = (rbuf == NULL || rlen == 0); /* write-only */
u8 req, type;
if (wo) {
req = GL861_REQ_I2C_WRITE;
type = GL861_WRITE;
} else { /* rw */
req = GL861_REQ_I2C_READ;
type = GL861_READ;
}
switch (wlen) {
case 1:
index = wbuf[0];
break;
case 2:
index = wbuf[0];
value = value + wbuf[1];
break;
default:
dev_err(&d->udev->dev, "%s: wlen=%d, aborting\n",
KBUILD_MODNAME, wlen);
return -EINVAL;
}
msleep(1); /* avoid I2C errors */
return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type,
value, index, rbuf, rlen, 2000);
}
/* I2C */
static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
if (num > 2)
return -EINVAL;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
for (i = 0; i < num; i++) {
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf,
msg[i].len, msg[i+1].buf, msg[i+1].len) < 0)
break;
i++;
} else
if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf,
msg[i].len, NULL, 0) < 0)
break;
}
mutex_unlock(&d->i2c_mutex);
return i;
}
static u32 gl861_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm gl861_i2c_algo = {
.master_xfer = gl861_i2c_xfer,
.functionality = gl861_i2c_func,
};
/* Callbacks for DVB USB */
static struct zl10353_config gl861_zl10353_config = {
.demod_address = 0x0f,
.no_tuner = 1,
.parallel_ts = 1,
};
static int gl861_frontend_attach(struct dvb_usb_adapter *adap)
{
adap->fe[0] = dvb_attach(zl10353_attach, &gl861_zl10353_config,
&adap_to_d(adap)->i2c_adap);
if (adap->fe[0] == NULL)
return -EIO;
return 0;
}
static struct qt1010_config gl861_qt1010_config = {
.i2c_address = 0x62
};
static int gl861_tuner_attach(struct dvb_usb_adapter *adap)
{
return dvb_attach(qt1010_attach,
adap->fe[0], &adap_to_d(adap)->i2c_adap,
&gl861_qt1010_config) == NULL ? -ENODEV : 0;
}
static int gl861_init(struct dvb_usb_device *d)
{
/*
* There is 2 interfaces. Interface 0 is for TV and interface 1 is
* for HID remote controller. Interface 0 has 2 alternate settings.
* For some reason we need to set interface explicitly, defaulted
* as alternate setting 1?
*/
return usb_set_interface(d->udev, 0, 0);
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties gl861_props = {
.driver_name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.adapter_nr = adapter_nr,
.i2c_algo = &gl861_i2c_algo,
.frontend_attach = gl861_frontend_attach,
.tuner_attach = gl861_tuner_attach,
.init = gl861_init,
.num_adapters = 1,
.adapter = {
{
.stream = DVB_USB_STREAM_BULK(0x81, 7, 512),
}
}
};
static const struct usb_device_id gl861_id_table[] = {
{ DVB_USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580_55801,
&gl861_props, "MSI Mega Sky 55801 DVB-T USB2.0", NULL) },
{ DVB_USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU,
&gl861_props, "A-LINK DTU DVB-T USB2.0", NULL) },
{ }
};
MODULE_DEVICE_TABLE(usb, gl861_id_table);
static struct usb_driver gl861_usb_driver = {
.name = KBUILD_MODNAME,
.id_table = gl861_id_table,
.probe = dvb_usbv2_probe,
.disconnect = dvb_usbv2_disconnect,
.suspend = dvb_usbv2_suspend,
.resume = dvb_usbv2_resume,
.reset_resume = dvb_usbv2_reset_resume,
.no_dynamic_id = 1,
.soft_unbind = 1,
};
module_usb_driver(gl861_usb_driver);
MODULE_AUTHOR("Carl Lundqvist <comabug@gmail.com>");
MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / GL861");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,12 @@
#ifndef _DVB_USB_GL861_H_
#define _DVB_USB_GL861_H_
#include "dvb_usb.h"
#define GL861_WRITE 0x40
#define GL861_READ 0xc0
#define GL861_REQ_I2C_WRITE 0x01
#define GL861_REQ_I2C_READ 0x02
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,175 @@
/* DVB USB compliant linux driver for
*
* DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395
* LME2510C + LG TDQY-P001F
* LME2510 + LG TDQY-P001F
*
* MVB7395 (LME2510C+SHARP:BS2F7HZ7395)
* SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V)
*
* MVB001F (LME2510+LGTDQT-P001F)
* LG TDQY - P001F =(TDA8263 + TDA10086H)
*
* MVB0001F (LME2510C+LGTDQT-P001F)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
* *
* see Documentation/dvb/README.dvb-usb for more information
*/
#ifndef _DVB_USB_LME2510_H_
#define _DVB_USB_LME2510_H_
/* Streamer & PID
*
* Note: These commands do not actually stop the streaming
* but form some kind of packet filtering/stream count
* or tuning related functions.
* 06 XX
* offset 1 = 00 Enable Streaming
*
*
* PID
* 03 XX XX ----> reg number ---> setting....20 XX
* offset 1 = length
* offset 2 = start of data
* end byte -1 = 20
* end byte = clear pid always a0, other wise 9c, 9a ??
*
*/
#define LME_ST_ON_W {0x06, 0x00}
#define LME_CLEAR_PID {0x03, 0x02, 0x20, 0xa0}
#define LME_ZERO_PID {0x03, 0x06, 0x00, 0x00, 0x01, 0x00, 0x20, 0x9c}
#define LME_ALL_PIDS {0x03, 0x06, 0x00, 0xff, 0x01, 0x1f, 0x20, 0x81}
/* LNB Voltage
* 07 XX XX
* offset 1 = 01
* offset 2 = 00=Voltage low 01=Voltage high
*
* LNB Power
* 03 01 XX
* offset 2 = 00=ON 01=OFF
*/
#define LME_VOLTAGE_L {0x07, 0x01, 0x00}
#define LME_VOLTAGE_H {0x07, 0x01, 0x01}
#define LNB_ON {0x3a, 0x01, 0x00}
#define LNB_OFF {0x3a, 0x01, 0x01}
/* Initial stv0288 settings for 7395 Frontend */
static u8 s7395_inittab[] = {
0x01, 0x15,
0x02, 0x20,
0x03, 0xa0,
0x04, 0xa0,
0x05, 0x12,
0x06, 0x00,
0x09, 0x00,
0x0a, 0x04,
0x0b, 0x00,
0x0c, 0x00,
0x0d, 0x00,
0x0e, 0xc1,
0x0f, 0x54,
0x11, 0x7a,
0x12, 0x03,
0x13, 0x48,
0x14, 0x84,
0x15, 0xc5,
0x16, 0xb8,
0x17, 0x9c,
0x18, 0x00,
0x19, 0xa6,
0x1a, 0x88,
0x1b, 0x8f,
0x1c, 0xf0,
0x20, 0x0b,
0x21, 0x54,
0x22, 0xff,
0x23, 0x01,
0x28, 0x46,
0x29, 0x66,
0x2a, 0x90,
0x2b, 0xfa,
0x2c, 0xd9,
0x30, 0x0,
0x31, 0x1e,
0x32, 0x14,
0x33, 0x0f,
0x34, 0x09,
0x35, 0x0c,
0x36, 0x05,
0x37, 0x2f,
0x38, 0x16,
0x39, 0xbd,
0x3a, 0x0,
0x3b, 0x13,
0x3c, 0x11,
0x3d, 0x30,
0x40, 0x63,
0x41, 0x04,
0x42, 0x20,
0x43, 0x00,
0x44, 0x00,
0x45, 0x00,
0x46, 0x00,
0x47, 0x00,
0x4a, 0x00,
0x50, 0x10,
0x51, 0x36,
0x52, 0x21,
0x53, 0x94,
0x54, 0xb2,
0x55, 0x29,
0x56, 0x64,
0x57, 0x2b,
0x58, 0x54,
0x59, 0x86,
0x5a, 0x00,
0x5b, 0x9b,
0x5c, 0x08,
0x5d, 0x7f,
0x5e, 0xff,
0x5f, 0x8d,
0x70, 0x0,
0x71, 0x0,
0x72, 0x0,
0x74, 0x0,
0x75, 0x0,
0x76, 0x0,
0x81, 0x0,
0x82, 0x3f,
0x83, 0x3f,
0x84, 0x0,
0x85, 0x0,
0x88, 0x0,
0x89, 0x0,
0x8a, 0x0,
0x8b, 0x0,
0x8c, 0x0,
0x90, 0x0,
0x91, 0x0,
0x92, 0x0,
0x93, 0x0,
0x94, 0x1c,
0x97, 0x0,
0xa0, 0x48,
0xa1, 0x0,
0xb0, 0xb8,
0xb1, 0x3a,
0xb2, 0x10,
0xb3, 0x82,
0xb4, 0x80,
0xb5, 0x82,
0xb6, 0x82,
0xb7, 0x82,
0xb8, 0x20,
0xb9, 0x0,
0xf0, 0x0,
0xf1, 0x0,
0xf2, 0xc0,
0xff, 0xff,
};
#endif

View file

@ -0,0 +1,612 @@
/*
* mxl111sf-demod.c - driver for the MaxLinear MXL111SF DVB-T demodulator
*
* Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "mxl111sf-demod.h"
#include "mxl111sf-reg.h"
/* debug */
static int mxl111sf_demod_debug;
module_param_named(debug, mxl111sf_demod_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
#define mxl_dbg(fmt, arg...) \
if (mxl111sf_demod_debug) \
mxl_printk(KERN_DEBUG, fmt, ##arg)
/* ------------------------------------------------------------------------ */
struct mxl111sf_demod_state {
struct mxl111sf_state *mxl_state;
struct mxl111sf_demod_config *cfg;
struct dvb_frontend fe;
};
/* ------------------------------------------------------------------------ */
static int mxl111sf_demod_read_reg(struct mxl111sf_demod_state *state,
u8 addr, u8 *data)
{
return (state->cfg->read_reg) ?
state->cfg->read_reg(state->mxl_state, addr, data) :
-EINVAL;
}
static int mxl111sf_demod_write_reg(struct mxl111sf_demod_state *state,
u8 addr, u8 data)
{
return (state->cfg->write_reg) ?
state->cfg->write_reg(state->mxl_state, addr, data) :
-EINVAL;
}
static
int mxl111sf_demod_program_regs(struct mxl111sf_demod_state *state,
struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
{
return (state->cfg->program_regs) ?
state->cfg->program_regs(state->mxl_state, ctrl_reg_info) :
-EINVAL;
}
/* ------------------------------------------------------------------------ */
/* TPS */
static
int mxl1x1sf_demod_get_tps_code_rate(struct mxl111sf_demod_state *state,
fe_code_rate_t *code_rate)
{
u8 val;
int ret = mxl111sf_demod_read_reg(state, V6_CODE_RATE_TPS_REG, &val);
/* bit<2:0> - 000:1/2, 001:2/3, 010:3/4, 011:5/6, 100:7/8 */
if (mxl_fail(ret))
goto fail;
switch (val & V6_CODE_RATE_TPS_MASK) {
case 0:
*code_rate = FEC_1_2;
break;
case 1:
*code_rate = FEC_2_3;
break;
case 2:
*code_rate = FEC_3_4;
break;
case 3:
*code_rate = FEC_5_6;
break;
case 4:
*code_rate = FEC_7_8;
break;
}
fail:
return ret;
}
static
int mxl1x1sf_demod_get_tps_modulation(struct mxl111sf_demod_state *state,
fe_modulation_t *modulation)
{
u8 val;
int ret = mxl111sf_demod_read_reg(state, V6_MODORDER_TPS_REG, &val);
/* Constellation, 00 : QPSK, 01 : 16QAM, 10:64QAM */
if (mxl_fail(ret))
goto fail;
switch ((val & V6_PARAM_CONSTELLATION_MASK) >> 4) {
case 0:
*modulation = QPSK;
break;
case 1:
*modulation = QAM_16;
break;
case 2:
*modulation = QAM_64;
break;
}
fail:
return ret;
}
static
int mxl1x1sf_demod_get_tps_guard_fft_mode(struct mxl111sf_demod_state *state,
fe_transmit_mode_t *fft_mode)
{
u8 val;
int ret = mxl111sf_demod_read_reg(state, V6_MODE_TPS_REG, &val);
/* FFT Mode, 00:2K, 01:8K, 10:4K */
if (mxl_fail(ret))
goto fail;
switch ((val & V6_PARAM_FFT_MODE_MASK) >> 2) {
case 0:
*fft_mode = TRANSMISSION_MODE_2K;
break;
case 1:
*fft_mode = TRANSMISSION_MODE_8K;
break;
case 2:
*fft_mode = TRANSMISSION_MODE_4K;
break;
}
fail:
return ret;
}
static
int mxl1x1sf_demod_get_tps_guard_interval(struct mxl111sf_demod_state *state,
fe_guard_interval_t *guard)
{
u8 val;
int ret = mxl111sf_demod_read_reg(state, V6_CP_TPS_REG, &val);
/* 00:1/32, 01:1/16, 10:1/8, 11:1/4 */
if (mxl_fail(ret))
goto fail;
switch ((val & V6_PARAM_GI_MASK) >> 4) {
case 0:
*guard = GUARD_INTERVAL_1_32;
break;
case 1:
*guard = GUARD_INTERVAL_1_16;
break;
case 2:
*guard = GUARD_INTERVAL_1_8;
break;
case 3:
*guard = GUARD_INTERVAL_1_4;
break;
}
fail:
return ret;
}
static
int mxl1x1sf_demod_get_tps_hierarchy(struct mxl111sf_demod_state *state,
fe_hierarchy_t *hierarchy)
{
u8 val;
int ret = mxl111sf_demod_read_reg(state, V6_TPS_HIERACHY_REG, &val);
/* bit<6:4> - 000:Non hierarchy, 001:1, 010:2, 011:4 */
if (mxl_fail(ret))
goto fail;
switch ((val & V6_TPS_HIERARCHY_INFO_MASK) >> 6) {
case 0:
*hierarchy = HIERARCHY_NONE;
break;
case 1:
*hierarchy = HIERARCHY_1;
break;
case 2:
*hierarchy = HIERARCHY_2;
break;
case 3:
*hierarchy = HIERARCHY_4;
break;
}
fail:
return ret;
}
/* ------------------------------------------------------------------------ */
/* LOCKS */
static
int mxl1x1sf_demod_get_sync_lock_status(struct mxl111sf_demod_state *state,
int *sync_lock)
{
u8 val = 0;
int ret = mxl111sf_demod_read_reg(state, V6_SYNC_LOCK_REG, &val);
if (mxl_fail(ret))
goto fail;
*sync_lock = (val & SYNC_LOCK_MASK) >> 4;
fail:
return ret;
}
static
int mxl1x1sf_demod_get_rs_lock_status(struct mxl111sf_demod_state *state,
int *rs_lock)
{
u8 val = 0;
int ret = mxl111sf_demod_read_reg(state, V6_RS_LOCK_DET_REG, &val);
if (mxl_fail(ret))
goto fail;
*rs_lock = (val & RS_LOCK_DET_MASK) >> 3;
fail:
return ret;
}
static
int mxl1x1sf_demod_get_tps_lock_status(struct mxl111sf_demod_state *state,
int *tps_lock)
{
u8 val = 0;
int ret = mxl111sf_demod_read_reg(state, V6_TPS_LOCK_REG, &val);
if (mxl_fail(ret))
goto fail;
*tps_lock = (val & V6_PARAM_TPS_LOCK_MASK) >> 6;
fail:
return ret;
}
static
int mxl1x1sf_demod_get_fec_lock_status(struct mxl111sf_demod_state *state,
int *fec_lock)
{
u8 val = 0;
int ret = mxl111sf_demod_read_reg(state, V6_IRQ_STATUS_REG, &val);
if (mxl_fail(ret))
goto fail;
*fec_lock = (val & IRQ_MASK_FEC_LOCK) >> 4;
fail:
return ret;
}
#if 0
static
int mxl1x1sf_demod_get_cp_lock_status(struct mxl111sf_demod_state *state,
int *cp_lock)
{
u8 val = 0;
int ret = mxl111sf_demod_read_reg(state, V6_CP_LOCK_DET_REG, &val);
if (mxl_fail(ret))
goto fail;
*cp_lock = (val & V6_CP_LOCK_DET_MASK) >> 2;
fail:
return ret;
}
#endif
static int mxl1x1sf_demod_reset_irq_status(struct mxl111sf_demod_state *state)
{
return mxl111sf_demod_write_reg(state, 0x0e, 0xff);
}
/* ------------------------------------------------------------------------ */
static int mxl111sf_demod_set_frontend(struct dvb_frontend *fe)
{
struct mxl111sf_demod_state *state = fe->demodulator_priv;
int ret = 0;
struct mxl111sf_reg_ctrl_info phy_pll_patch[] = {
{0x00, 0xff, 0x01}, /* change page to 1 */
{0x40, 0xff, 0x05},
{0x40, 0xff, 0x01},
{0x41, 0xff, 0xca},
{0x41, 0xff, 0xc0},
{0x00, 0xff, 0x00}, /* change page to 0 */
{0, 0, 0}
};
mxl_dbg("()");
if (fe->ops.tuner_ops.set_params) {
ret = fe->ops.tuner_ops.set_params(fe);
if (mxl_fail(ret))
goto fail;
msleep(50);
}
ret = mxl111sf_demod_program_regs(state, phy_pll_patch);
mxl_fail(ret);
msleep(50);
ret = mxl1x1sf_demod_reset_irq_status(state);
mxl_fail(ret);
msleep(100);
fail:
return ret;
}
/* ------------------------------------------------------------------------ */
#if 0
/* resets TS Packet error count */
/* After setting 7th bit of V5_PER_COUNT_RESET_REG, it should be reset to 0. */
static
int mxl1x1sf_demod_reset_packet_error_count(struct mxl111sf_demod_state *state)
{
struct mxl111sf_reg_ctrl_info reset_per_count[] = {
{0x20, 0x01, 0x01},
{0x20, 0x01, 0x00},
{0, 0, 0}
};
return mxl111sf_demod_program_regs(state, reset_per_count);
}
#endif
/* returns TS Packet error count */
/* PER Count = FEC_PER_COUNT * (2 ** (FEC_PER_SCALE * 4)) */
static int mxl111sf_demod_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
{
struct mxl111sf_demod_state *state = fe->demodulator_priv;
u32 fec_per_count, fec_per_scale;
u8 val;
int ret;
*ucblocks = 0;
/* FEC_PER_COUNT Register */
ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_COUNT_REG, &val);
if (mxl_fail(ret))
goto fail;
fec_per_count = val;
/* FEC_PER_SCALE Register */
ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_SCALE_REG, &val);
if (mxl_fail(ret))
goto fail;
val &= V6_FEC_PER_SCALE_MASK;
val *= 4;
fec_per_scale = 1 << val;
fec_per_count *= fec_per_scale;
*ucblocks = fec_per_count;
fail:
return ret;
}
#ifdef MXL111SF_DEMOD_ENABLE_CALCULATIONS
/* FIXME: leaving this enabled breaks the build on some architectures,
* and we shouldn't have any floating point math in the kernel, anyway.
*
* These macros need to be re-written, but it's harmless to simply
* return zero for now. */
#define CALCULATE_BER(avg_errors, count) \
((u32)(avg_errors * 4)/(count*64*188*8))
#define CALCULATE_SNR(data) \
((u32)((10 * (u32)data / 64) - 2.5))
#else
#define CALCULATE_BER(avg_errors, count) 0
#define CALCULATE_SNR(data) 0
#endif
static int mxl111sf_demod_read_ber(struct dvb_frontend *fe, u32 *ber)
{
struct mxl111sf_demod_state *state = fe->demodulator_priv;
u8 val1, val2, val3;
int ret;
*ber = 0;
ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_LSB_REG, &val1);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_MSB_REG, &val2);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_demod_read_reg(state, V6_N_ACCUMULATE_REG, &val3);
if (mxl_fail(ret))
goto fail;
*ber = CALCULATE_BER((val1 | (val2 << 8)), val3);
fail:
return ret;
}
static int mxl111sf_demod_calc_snr(struct mxl111sf_demod_state *state,
u16 *snr)
{
u8 val1, val2;
int ret;
*snr = 0;
ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_LSB_REG, &val1);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_MSB_REG, &val2);
if (mxl_fail(ret))
goto fail;
*snr = CALCULATE_SNR(val1 | ((val2 & 0x03) << 8));
fail:
return ret;
}
static int mxl111sf_demod_read_snr(struct dvb_frontend *fe, u16 *snr)
{
struct mxl111sf_demod_state *state = fe->demodulator_priv;
int ret = mxl111sf_demod_calc_snr(state, snr);
if (mxl_fail(ret))
goto fail;
*snr /= 10; /* 0.1 dB */
fail:
return ret;
}
static int mxl111sf_demod_read_status(struct dvb_frontend *fe,
fe_status_t *status)
{
struct mxl111sf_demod_state *state = fe->demodulator_priv;
int ret, locked, cr_lock, sync_lock, fec_lock;
*status = 0;
ret = mxl1x1sf_demod_get_rs_lock_status(state, &locked);
if (mxl_fail(ret))
goto fail;
ret = mxl1x1sf_demod_get_tps_lock_status(state, &cr_lock);
if (mxl_fail(ret))
goto fail;
ret = mxl1x1sf_demod_get_sync_lock_status(state, &sync_lock);
if (mxl_fail(ret))
goto fail;
ret = mxl1x1sf_demod_get_fec_lock_status(state, &fec_lock);
if (mxl_fail(ret))
goto fail;
if (locked)
*status |= FE_HAS_SIGNAL;
if (cr_lock)
*status |= FE_HAS_CARRIER;
if (sync_lock)
*status |= FE_HAS_SYNC;
if (fec_lock) /* false positives? */
*status |= FE_HAS_VITERBI;
if ((locked) && (cr_lock) && (sync_lock))
*status |= FE_HAS_LOCK;
fail:
return ret;
}
static int mxl111sf_demod_read_signal_strength(struct dvb_frontend *fe,
u16 *signal_strength)
{
struct mxl111sf_demod_state *state = fe->demodulator_priv;
fe_modulation_t modulation;
u16 snr;
mxl111sf_demod_calc_snr(state, &snr);
mxl1x1sf_demod_get_tps_modulation(state, &modulation);
switch (modulation) {
case QPSK:
*signal_strength = (snr >= 1300) ?
min(65535, snr * 44) : snr * 38;
break;
case QAM_16:
*signal_strength = (snr >= 1500) ?
min(65535, snr * 38) : snr * 33;
break;
case QAM_64:
*signal_strength = (snr >= 2000) ?
min(65535, snr * 29) : snr * 25;
break;
default:
*signal_strength = 0;
return -EINVAL;
}
return 0;
}
static int mxl111sf_demod_get_frontend(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct mxl111sf_demod_state *state = fe->demodulator_priv;
mxl_dbg("()");
#if 0
p->inversion = /* FIXME */ ? INVERSION_ON : INVERSION_OFF;
#endif
if (fe->ops.tuner_ops.get_bandwidth)
fe->ops.tuner_ops.get_bandwidth(fe, &p->bandwidth_hz);
if (fe->ops.tuner_ops.get_frequency)
fe->ops.tuner_ops.get_frequency(fe, &p->frequency);
mxl1x1sf_demod_get_tps_code_rate(state, &p->code_rate_HP);
mxl1x1sf_demod_get_tps_code_rate(state, &p->code_rate_LP);
mxl1x1sf_demod_get_tps_modulation(state, &p->modulation);
mxl1x1sf_demod_get_tps_guard_fft_mode(state,
&p->transmission_mode);
mxl1x1sf_demod_get_tps_guard_interval(state,
&p->guard_interval);
mxl1x1sf_demod_get_tps_hierarchy(state,
&p->hierarchy);
return 0;
}
static
int mxl111sf_demod_get_tune_settings(struct dvb_frontend *fe,
struct dvb_frontend_tune_settings *tune)
{
tune->min_delay_ms = 1000;
return 0;
}
static void mxl111sf_demod_release(struct dvb_frontend *fe)
{
struct mxl111sf_demod_state *state = fe->demodulator_priv;
mxl_dbg("()");
kfree(state);
fe->demodulator_priv = NULL;
}
static struct dvb_frontend_ops mxl111sf_demod_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "MaxLinear MxL111SF DVB-T demodulator",
.frequency_min = 177000000,
.frequency_max = 858000000,
.frequency_stepsize = 166666,
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
FE_CAN_QAM_AUTO |
FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
},
.release = mxl111sf_demod_release,
#if 0
.init = mxl111sf_init,
.i2c_gate_ctrl = mxl111sf_i2c_gate_ctrl,
#endif
.set_frontend = mxl111sf_demod_set_frontend,
.get_frontend = mxl111sf_demod_get_frontend,
.get_tune_settings = mxl111sf_demod_get_tune_settings,
.read_status = mxl111sf_demod_read_status,
.read_signal_strength = mxl111sf_demod_read_signal_strength,
.read_ber = mxl111sf_demod_read_ber,
.read_snr = mxl111sf_demod_read_snr,
.read_ucblocks = mxl111sf_demod_read_ucblocks,
};
struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
struct mxl111sf_demod_config *cfg)
{
struct mxl111sf_demod_state *state = NULL;
mxl_dbg("()");
state = kzalloc(sizeof(struct mxl111sf_demod_state), GFP_KERNEL);
if (state == NULL)
return NULL;
state->mxl_state = mxl_state;
state->cfg = cfg;
memcpy(&state->fe.ops, &mxl111sf_demod_ops,
sizeof(struct dvb_frontend_ops));
state->fe.demodulator_priv = state;
return &state->fe;
}
EXPORT_SYMBOL_GPL(mxl111sf_demod_attach);
MODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver");
MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View file

@ -0,0 +1,55 @@
/*
* mxl111sf-demod.h - driver for the MaxLinear MXL111SF DVB-T demodulator
*
* Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __MXL111SF_DEMOD_H__
#define __MXL111SF_DEMOD_H__
#include <linux/kconfig.h>
#include "dvb_frontend.h"
#include "mxl111sf.h"
struct mxl111sf_demod_config {
int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data);
int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data);
int (*program_regs)(struct mxl111sf_state *state,
struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
};
#if IS_ENABLED(CONFIG_DVB_USB_MXL111SF)
extern
struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
struct mxl111sf_demod_config *cfg);
#else
static inline
struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
struct mxl111sf_demod_config *cfg)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
#endif /* CONFIG_DVB_USB_MXL111SF */
#endif /* __MXL111SF_DEMOD_H__ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View file

@ -0,0 +1,763 @@
/*
* mxl111sf-gpio.c - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "mxl111sf-gpio.h"
#include "mxl111sf-i2c.h"
#include "mxl111sf.h"
/* ------------------------------------------------------------------------- */
#define MXL_GPIO_MUX_REG_0 0x84
#define MXL_GPIO_MUX_REG_1 0x89
#define MXL_GPIO_MUX_REG_2 0x82
#define MXL_GPIO_DIR_INPUT 0
#define MXL_GPIO_DIR_OUTPUT 1
static int mxl111sf_set_gpo_state(struct mxl111sf_state *state, u8 pin, u8 val)
{
int ret;
u8 tmp;
mxl_debug_adv("(%d, %d)", pin, val);
if ((pin > 0) && (pin < 8)) {
ret = mxl111sf_read_reg(state, 0x19, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= ~(1 << (pin - 1));
tmp |= (val << (pin - 1));
ret = mxl111sf_write_reg(state, 0x19, tmp);
if (mxl_fail(ret))
goto fail;
} else if (pin <= 10) {
if (pin == 0)
pin += 7;
ret = mxl111sf_read_reg(state, 0x30, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= ~(1 << (pin - 3));
tmp |= (val << (pin - 3));
ret = mxl111sf_write_reg(state, 0x30, tmp);
if (mxl_fail(ret))
goto fail;
} else
ret = -EINVAL;
fail:
return ret;
}
static int mxl111sf_get_gpi_state(struct mxl111sf_state *state, u8 pin, u8 *val)
{
int ret;
u8 tmp;
mxl_debug("(0x%02x)", pin);
*val = 0;
switch (pin) {
case 0:
case 1:
case 2:
case 3:
ret = mxl111sf_read_reg(state, 0x23, &tmp);
if (mxl_fail(ret))
goto fail;
*val = (tmp >> (pin + 4)) & 0x01;
break;
case 4:
case 5:
case 6:
case 7:
ret = mxl111sf_read_reg(state, 0x2f, &tmp);
if (mxl_fail(ret))
goto fail;
*val = (tmp >> pin) & 0x01;
break;
case 8:
case 9:
case 10:
ret = mxl111sf_read_reg(state, 0x22, &tmp);
if (mxl_fail(ret))
goto fail;
*val = (tmp >> (pin - 3)) & 0x01;
break;
default:
return -EINVAL; /* invalid pin */
}
fail:
return ret;
}
struct mxl_gpio_cfg {
u8 pin;
u8 dir;
u8 val;
};
static int mxl111sf_config_gpio_pins(struct mxl111sf_state *state,
struct mxl_gpio_cfg *gpio_cfg)
{
int ret;
u8 tmp;
mxl_debug_adv("(%d, %d)", gpio_cfg->pin, gpio_cfg->dir);
switch (gpio_cfg->pin) {
case 0:
case 1:
case 2:
case 3:
ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_0, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= ~(1 << (gpio_cfg->pin + 4));
tmp |= (gpio_cfg->dir << (gpio_cfg->pin + 4));
ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_0, tmp);
if (mxl_fail(ret))
goto fail;
break;
case 4:
case 5:
case 6:
case 7:
ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_1, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= ~(1 << gpio_cfg->pin);
tmp |= (gpio_cfg->dir << gpio_cfg->pin);
ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_1, tmp);
if (mxl_fail(ret))
goto fail;
break;
case 8:
case 9:
case 10:
ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_2, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= ~(1 << (gpio_cfg->pin - 3));
tmp |= (gpio_cfg->dir << (gpio_cfg->pin - 3));
ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_2, tmp);
if (mxl_fail(ret))
goto fail;
break;
default:
return -EINVAL; /* invalid pin */
}
ret = (MXL_GPIO_DIR_OUTPUT == gpio_cfg->dir) ?
mxl111sf_set_gpo_state(state,
gpio_cfg->pin, gpio_cfg->val) :
mxl111sf_get_gpi_state(state,
gpio_cfg->pin, &gpio_cfg->val);
mxl_fail(ret);
fail:
return ret;
}
static int mxl111sf_hw_do_set_gpio(struct mxl111sf_state *state,
int gpio, int direction, int val)
{
struct mxl_gpio_cfg gpio_config = {
.pin = gpio,
.dir = direction,
.val = val,
};
mxl_debug("(%d, %d, %d)", gpio, direction, val);
return mxl111sf_config_gpio_pins(state, &gpio_config);
}
/* ------------------------------------------------------------------------- */
#define PIN_MUX_MPEG_MODE_MASK 0x40 /* 0x17 <6> */
#define PIN_MUX_MPEG_PAR_EN_MASK 0x01 /* 0x18 <0> */
#define PIN_MUX_MPEG_SER_EN_MASK 0x02 /* 0x18 <1> */
#define PIN_MUX_MPG_IN_MUX_MASK 0x80 /* 0x3D <7> */
#define PIN_MUX_BT656_ENABLE_MASK 0x04 /* 0x12 <2> */
#define PIN_MUX_I2S_ENABLE_MASK 0x40 /* 0x15 <6> */
#define PIN_MUX_SPI_MODE_MASK 0x10 /* 0x3D <4> */
#define PIN_MUX_MCLK_EN_CTRL_MASK 0x10 /* 0x82 <4> */
#define PIN_MUX_MPSYN_EN_CTRL_MASK 0x20 /* 0x82 <5> */
#define PIN_MUX_MDVAL_EN_CTRL_MASK 0x40 /* 0x82 <6> */
#define PIN_MUX_MPERR_EN_CTRL_MASK 0x80 /* 0x82 <7> */
#define PIN_MUX_MDAT_EN_0_MASK 0x10 /* 0x84 <4> */
#define PIN_MUX_MDAT_EN_1_MASK 0x20 /* 0x84 <5> */
#define PIN_MUX_MDAT_EN_2_MASK 0x40 /* 0x84 <6> */
#define PIN_MUX_MDAT_EN_3_MASK 0x80 /* 0x84 <7> */
#define PIN_MUX_MDAT_EN_4_MASK 0x10 /* 0x89 <4> */
#define PIN_MUX_MDAT_EN_5_MASK 0x20 /* 0x89 <5> */
#define PIN_MUX_MDAT_EN_6_MASK 0x40 /* 0x89 <6> */
#define PIN_MUX_MDAT_EN_7_MASK 0x80 /* 0x89 <7> */
int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state,
enum mxl111sf_mux_config pin_mux_config)
{
u8 r12, r15, r17, r18, r3D, r82, r84, r89;
int ret;
mxl_debug("(%d)", pin_mux_config);
ret = mxl111sf_read_reg(state, 0x17, &r17);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x18, &r18);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x12, &r12);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x15, &r15);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x82, &r82);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x84, &r84);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x89, &r89);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x3D, &r3D);
if (mxl_fail(ret))
goto fail;
switch (pin_mux_config) {
case PIN_MUX_TS_OUT_PARALLEL:
/* mpeg_mode = 1 */
r17 |= PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 1 */
r18 |= PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 0 */
r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 1 */
r82 |= PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 1 */
r82 |= PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 1 */
r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 1 */
r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0xF */
r84 |= 0xF0;
/* mdat_en_ctrl[7:4] = 0xF */
r89 |= 0xF0;
break;
case PIN_MUX_TS_OUT_SERIAL:
/* mpeg_mode = 1 */
r17 |= PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 1 */
r18 |= PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 1 */
r82 |= PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 1 */
r82 |= PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 1 */
r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 1 */
r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0xF */
r84 |= 0xF0;
/* mdat_en_ctrl[7:4] = 0xF */
r89 |= 0xF0;
break;
case PIN_MUX_GPIO_MODE:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 0 */
r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_TS_SERIAL_IN_MODE_0:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 1 */
r18 |= PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_TS_SERIAL_IN_MODE_1:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 1 */
r18 |= PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 1 */
r3D |= PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_TS_SPI_IN_MODE_1:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 1 */
r18 |= PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 1 */
r3D |= PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 1 */
r15 |= PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 1 */
r3D |= PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_TS_SPI_IN_MODE_0:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 1 */
r18 |= PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 1 */
r15 |= PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 1 */
r3D |= PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_TS_PARALLEL_IN:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 1 */
r18 |= PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 0 */
r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_BT656_I2S_MODE:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 0 */
r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 1 */
r12 |= PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 1 */
r15 |= PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_DEFAULT:
default:
/* mpeg_mode = 1 */
r17 |= PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 0 */
r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
}
ret = mxl111sf_write_reg(state, 0x17, r17);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x18, r18);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x12, r12);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x15, r15);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x82, r82);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x84, r84);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x89, r89);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x3D, r3D);
if (mxl_fail(ret))
goto fail;
fail:
return ret;
}
/* ------------------------------------------------------------------------- */
static int mxl111sf_hw_set_gpio(struct mxl111sf_state *state, int gpio, int val)
{
return mxl111sf_hw_do_set_gpio(state, gpio, MXL_GPIO_DIR_OUTPUT, val);
}
static int mxl111sf_hw_gpio_initialize(struct mxl111sf_state *state)
{
u8 gpioval = 0x07; /* write protect enabled, signal LEDs off */
int i, ret;
mxl_debug("()");
for (i = 3; i < 8; i++) {
ret = mxl111sf_hw_set_gpio(state, i, (gpioval >> i) & 0x01);
if (mxl_fail(ret))
break;
}
return ret;
}
#define PCA9534_I2C_ADDR (0x40 >> 1)
static int pca9534_set_gpio(struct mxl111sf_state *state, int gpio, int val)
{
u8 w[2] = { 1, 0 };
u8 r = 0;
struct i2c_msg msg[] = {
{ .addr = PCA9534_I2C_ADDR,
.flags = 0, .buf = w, .len = 1 },
{ .addr = PCA9534_I2C_ADDR,
.flags = I2C_M_RD, .buf = &r, .len = 1 },
};
mxl_debug("(%d, %d)", gpio, val);
/* read current GPIO levels from flip-flop */
i2c_transfer(&state->d->i2c_adap, msg, 2);
/* prepare write buffer with current GPIO levels */
msg[0].len = 2;
#if 0
w[0] = 1;
#endif
w[1] = r;
/* clear the desired GPIO */
w[1] &= ~(1 << gpio);
/* set the desired GPIO value */
w[1] |= ((val ? 1 : 0) << gpio);
/* write new GPIO levels to flip-flop */
i2c_transfer(&state->d->i2c_adap, &msg[0], 1);
return 0;
}
static int pca9534_init_port_expander(struct mxl111sf_state *state)
{
u8 w[2] = { 1, 0x07 }; /* write protect enabled, signal LEDs off */
struct i2c_msg msg = {
.addr = PCA9534_I2C_ADDR,
.flags = 0, .buf = w, .len = 2
};
mxl_debug("()");
i2c_transfer(&state->d->i2c_adap, &msg, 1);
/* configure all pins as outputs */
w[0] = 3;
w[1] = 0;
i2c_transfer(&state->d->i2c_adap, &msg, 1);
return 0;
}
int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val)
{
mxl_debug("(%d, %d)", gpio, val);
switch (state->gpio_port_expander) {
default:
mxl_printk(KERN_ERR,
"gpio_port_expander undefined, assuming PCA9534");
/* fall-thru */
case mxl111sf_PCA9534:
return pca9534_set_gpio(state, gpio, val);
case mxl111sf_gpio_hw:
return mxl111sf_hw_set_gpio(state, gpio, val);
}
}
static int mxl111sf_probe_port_expander(struct mxl111sf_state *state)
{
int ret;
u8 w = 1;
u8 r = 0;
struct i2c_msg msg[] = {
{ .flags = 0, .buf = &w, .len = 1 },
{ .flags = I2C_M_RD, .buf = &r, .len = 1 },
};
mxl_debug("()");
msg[0].addr = 0x70 >> 1;
msg[1].addr = 0x70 >> 1;
/* read current GPIO levels from flip-flop */
ret = i2c_transfer(&state->d->i2c_adap, msg, 2);
if (ret == 2) {
state->port_expander_addr = msg[0].addr;
state->gpio_port_expander = mxl111sf_PCA9534;
mxl_debug("found port expander at 0x%02x",
state->port_expander_addr);
return 0;
}
msg[0].addr = 0x40 >> 1;
msg[1].addr = 0x40 >> 1;
ret = i2c_transfer(&state->d->i2c_adap, msg, 2);
if (ret == 2) {
state->port_expander_addr = msg[0].addr;
state->gpio_port_expander = mxl111sf_PCA9534;
mxl_debug("found port expander at 0x%02x",
state->port_expander_addr);
return 0;
}
state->port_expander_addr = 0xff;
state->gpio_port_expander = mxl111sf_gpio_hw;
mxl_debug("using hardware gpio");
return 0;
}
int mxl111sf_init_port_expander(struct mxl111sf_state *state)
{
mxl_debug("()");
if (0x00 == state->port_expander_addr)
mxl111sf_probe_port_expander(state);
switch (state->gpio_port_expander) {
default:
mxl_printk(KERN_ERR,
"gpio_port_expander undefined, assuming PCA9534");
/* fall-thru */
case mxl111sf_PCA9534:
return pca9534_init_port_expander(state);
case mxl111sf_gpio_hw:
return mxl111sf_hw_gpio_initialize(state);
}
}
/* ------------------------------------------------------------------------ */
int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode)
{
/* GPO:
* 3 - ATSC/MH# | 1 = ATSC transport, 0 = MH transport | default 0
* 4 - ATSC_RST## | 1 = ATSC enable, 0 = ATSC Reset | default 0
* 5 - ATSC_EN | 1 = ATSC power enable, 0 = ATSC power off | default 0
* 6 - MH_RESET# | 1 = MH enable, 0 = MH Reset | default 0
* 7 - MH_EN | 1 = MH power enable, 0 = MH power off | default 0
*/
mxl_debug("(%d)", mode);
switch (mode) {
case MXL111SF_GPIO_MOD_MH:
mxl111sf_set_gpio(state, 4, 0);
mxl111sf_set_gpio(state, 5, 0);
msleep(50);
mxl111sf_set_gpio(state, 7, 1);
msleep(50);
mxl111sf_set_gpio(state, 6, 1);
msleep(50);
mxl111sf_set_gpio(state, 3, 0);
break;
case MXL111SF_GPIO_MOD_ATSC:
mxl111sf_set_gpio(state, 6, 0);
mxl111sf_set_gpio(state, 7, 0);
msleep(50);
mxl111sf_set_gpio(state, 5, 1);
msleep(50);
mxl111sf_set_gpio(state, 4, 1);
msleep(50);
mxl111sf_set_gpio(state, 3, 1);
break;
default: /* DVBT / STANDBY */
mxl111sf_init_port_expander(state);
break;
}
return 0;
}
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View file

@ -0,0 +1,56 @@
/*
* mxl111sf-gpio.h - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _DVB_USB_MXL111SF_GPIO_H_
#define _DVB_USB_MXL111SF_GPIO_H_
#include "mxl111sf.h"
int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val);
int mxl111sf_init_port_expander(struct mxl111sf_state *state);
#define MXL111SF_GPIO_MOD_DVBT 0
#define MXL111SF_GPIO_MOD_MH 1
#define MXL111SF_GPIO_MOD_ATSC 2
int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode);
enum mxl111sf_mux_config {
PIN_MUX_DEFAULT = 0,
PIN_MUX_TS_OUT_PARALLEL,
PIN_MUX_TS_OUT_SERIAL,
PIN_MUX_GPIO_MODE,
PIN_MUX_TS_SERIAL_IN_MODE_0,
PIN_MUX_TS_SERIAL_IN_MODE_1,
PIN_MUX_TS_SPI_IN_MODE_0,
PIN_MUX_TS_SPI_IN_MODE_1,
PIN_MUX_TS_PARALLEL_IN,
PIN_MUX_BT656_I2S_MODE,
};
int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state,
enum mxl111sf_mux_config pin_mux_config);
#endif /* _DVB_USB_MXL111SF_GPIO_H_ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View file

@ -0,0 +1,850 @@
/*
* mxl111sf-i2c.c - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "mxl111sf-i2c.h"
#include "mxl111sf.h"
/* SW-I2C ----------------------------------------------------------------- */
#define SW_I2C_ADDR 0x1a
#define SW_I2C_EN 0x02
#define SW_SCL_OUT 0x04
#define SW_SDA_OUT 0x08
#define SW_SDA_IN 0x04
#define SW_I2C_BUSY_ADDR 0x2f
#define SW_I2C_BUSY 0x02
static int mxl111sf_i2c_bitbang_sendbyte(struct mxl111sf_state *state,
u8 byte)
{
int i, ret;
u8 data = 0;
mxl_i2c("(0x%02x)", byte);
ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data);
if (mxl_fail(ret))
goto fail;
for (i = 0; i < 8; i++) {
data = (byte & (0x80 >> i)) ? SW_SDA_OUT : 0;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | data);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | data | SW_SCL_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | data);
if (mxl_fail(ret))
goto fail;
}
/* last bit was 0 so we need to release SDA */
if (!(byte & 1)) {
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
}
/* CLK high for ACK readback */
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data);
if (mxl_fail(ret))
goto fail;
/* drop the CLK after getting ACK, SDA will go high right away */
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
if (data & SW_SDA_IN)
ret = -EIO;
fail:
return ret;
}
static int mxl111sf_i2c_bitbang_recvbyte(struct mxl111sf_state *state,
u8 *pbyte)
{
int i, ret;
u8 byte = 0;
u8 data = 0;
mxl_i2c("()");
*pbyte = 0;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
for (i = 0; i < 8; i++) {
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN |
SW_SCL_OUT | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data);
if (mxl_fail(ret))
goto fail;
if (data & SW_SDA_IN)
byte |= (0x80 >> i);
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
}
*pbyte = byte;
fail:
return ret;
}
static int mxl111sf_i2c_start(struct mxl111sf_state *state)
{
int ret;
mxl_i2c("()");
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN); /* start */
mxl_fail(ret);
fail:
return ret;
}
static int mxl111sf_i2c_stop(struct mxl111sf_state *state)
{
int ret;
mxl_i2c("()");
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN); /* stop */
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_SCL_OUT | SW_SDA_OUT);
mxl_fail(ret);
fail:
return ret;
}
static int mxl111sf_i2c_ack(struct mxl111sf_state *state)
{
int ret;
u8 b = 0;
mxl_i2c("()");
ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &b);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN);
if (mxl_fail(ret))
goto fail;
/* pull SDA low */
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SDA_OUT);
mxl_fail(ret);
fail:
return ret;
}
static int mxl111sf_i2c_nack(struct mxl111sf_state *state)
{
int ret;
mxl_i2c("()");
/* SDA high to signal last byte read from slave */
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SDA_OUT);
mxl_fail(ret);
fail:
return ret;
}
/* ------------------------------------------------------------------------ */
static int mxl111sf_i2c_sw_xfer_msg(struct mxl111sf_state *state,
struct i2c_msg *msg)
{
int i, ret;
mxl_i2c("()");
if (msg->flags & I2C_M_RD) {
ret = mxl111sf_i2c_start(state);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_i2c_bitbang_sendbyte(state,
(msg->addr << 1) | 0x01);
if (mxl_fail(ret)) {
mxl111sf_i2c_stop(state);
goto fail;
}
for (i = 0; i < msg->len; i++) {
ret = mxl111sf_i2c_bitbang_recvbyte(state,
&msg->buf[i]);
if (mxl_fail(ret)) {
mxl111sf_i2c_stop(state);
goto fail;
}
if (i < msg->len - 1)
mxl111sf_i2c_ack(state);
}
mxl111sf_i2c_nack(state);
ret = mxl111sf_i2c_stop(state);
if (mxl_fail(ret))
goto fail;
} else {
ret = mxl111sf_i2c_start(state);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_i2c_bitbang_sendbyte(state,
(msg->addr << 1) & 0xfe);
if (mxl_fail(ret)) {
mxl111sf_i2c_stop(state);
goto fail;
}
for (i = 0; i < msg->len; i++) {
ret = mxl111sf_i2c_bitbang_sendbyte(state,
msg->buf[i]);
if (mxl_fail(ret)) {
mxl111sf_i2c_stop(state);
goto fail;
}
}
/* FIXME: we only want to do this on the last transaction */
mxl111sf_i2c_stop(state);
}
fail:
return ret;
}
/* HW-I2C ----------------------------------------------------------------- */
#define USB_WRITE_I2C_CMD 0x99
#define USB_READ_I2C_CMD 0xdd
#define USB_END_I2C_CMD 0xfe
#define USB_WRITE_I2C_CMD_LEN 26
#define USB_READ_I2C_CMD_LEN 24
#define I2C_MUX_REG 0x30
#define I2C_CONTROL_REG 0x00
#define I2C_SLAVE_ADDR_REG 0x08
#define I2C_DATA_REG 0x0c
#define I2C_INT_STATUS_REG 0x10
static int mxl111sf_i2c_send_data(struct mxl111sf_state *state,
u8 index, u8 *wdata)
{
int ret = mxl111sf_ctrl_msg(state->d, wdata[0],
&wdata[1], 25, NULL, 0);
mxl_fail(ret);
return ret;
}
static int mxl111sf_i2c_get_data(struct mxl111sf_state *state,
u8 index, u8 *wdata, u8 *rdata)
{
int ret = mxl111sf_ctrl_msg(state->d, wdata[0],
&wdata[1], 25, rdata, 24);
mxl_fail(ret);
return ret;
}
static u8 mxl111sf_i2c_check_status(struct mxl111sf_state *state)
{
u8 status = 0;
u8 buf[26];
mxl_i2c_adv("()");
buf[0] = USB_READ_I2C_CMD;
buf[1] = 0x00;
buf[2] = I2C_INT_STATUS_REG;
buf[3] = 0x00;
buf[4] = 0x00;
buf[5] = USB_END_I2C_CMD;
mxl111sf_i2c_get_data(state, 0, buf, buf);
if (buf[1] & 0x04)
status = 1;
return status;
}
static u8 mxl111sf_i2c_check_fifo(struct mxl111sf_state *state)
{
u8 status = 0;
u8 buf[26];
mxl_i2c("()");
buf[0] = USB_READ_I2C_CMD;
buf[1] = 0x00;
buf[2] = I2C_MUX_REG;
buf[3] = 0x00;
buf[4] = 0x00;
buf[5] = I2C_INT_STATUS_REG;
buf[6] = 0x00;
buf[7] = 0x00;
buf[8] = USB_END_I2C_CMD;
mxl111sf_i2c_get_data(state, 0, buf, buf);
if (0x08 == (buf[1] & 0x08))
status = 1;
if ((buf[5] & 0x02) == 0x02)
mxl_i2c("(buf[5] & 0x02) == 0x02"); /* FIXME */
return status;
}
static int mxl111sf_i2c_readagain(struct mxl111sf_state *state,
u8 count, u8 *rbuf)
{
u8 i2c_w_data[26];
u8 i2c_r_data[24];
u8 i = 0;
u8 fifo_status = 0;
int status = 0;
mxl_i2c("read %d bytes", count);
while ((fifo_status == 0) && (i++ < 5))
fifo_status = mxl111sf_i2c_check_fifo(state);
i2c_w_data[0] = 0xDD;
i2c_w_data[1] = 0x00;
for (i = 2; i < 26; i++)
i2c_w_data[i] = 0xFE;
for (i = 0; i < count; i++) {
i2c_w_data[2+(i*3)] = 0x0C;
i2c_w_data[3+(i*3)] = 0x00;
i2c_w_data[4+(i*3)] = 0x00;
}
mxl111sf_i2c_get_data(state, 0, i2c_w_data, i2c_r_data);
/* Check for I2C NACK status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("error!");
} else {
for (i = 0; i < count; i++) {
rbuf[i] = i2c_r_data[(i*3)+1];
mxl_i2c("%02x\t %02x",
i2c_r_data[(i*3)+1],
i2c_r_data[(i*3)+2]);
}
status = 1;
}
return status;
}
#define HWI2C400 1
static int mxl111sf_i2c_hw_xfer_msg(struct mxl111sf_state *state,
struct i2c_msg *msg)
{
int i, k, ret = 0;
u16 index = 0;
u8 buf[26];
u8 i2c_r_data[24];
u16 block_len;
u16 left_over_len;
u8 rd_status[8];
u8 ret_status;
u8 readbuff[26];
mxl_i2c("addr: 0x%02x, read buff len: %d, write buff len: %d",
msg->addr, (msg->flags & I2C_M_RD) ? msg->len : 0,
(!(msg->flags & I2C_M_RD)) ? msg->len : 0);
for (index = 0; index < 26; index++)
buf[index] = USB_END_I2C_CMD;
/* command to indicate data payload is destined for I2C interface */
buf[0] = USB_WRITE_I2C_CMD;
buf[1] = 0x00;
/* enable I2C interface */
buf[2] = I2C_MUX_REG;
buf[3] = 0x80;
buf[4] = 0x00;
/* enable I2C interface */
buf[5] = I2C_MUX_REG;
buf[6] = 0x81;
buf[7] = 0x00;
/* set Timeout register on I2C interface */
buf[8] = 0x14;
buf[9] = 0xff;
buf[10] = 0x00;
#if 0
/* enable Interrupts on I2C interface */
buf[8] = 0x24;
buf[9] = 0xF7;
buf[10] = 0x00;
#endif
buf[11] = 0x24;
buf[12] = 0xF7;
buf[13] = 0x00;
ret = mxl111sf_i2c_send_data(state, 0, buf);
/* write data on I2C bus */
if (!(msg->flags & I2C_M_RD) && (msg->len > 0)) {
mxl_i2c("%d\t%02x", msg->len, msg->buf[0]);
/* control register on I2C interface to initialize I2C bus */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0x5E;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
/* I2C Slave device Address */
buf[5] = I2C_SLAVE_ADDR_REG;
buf[6] = (msg->addr);
buf[7] = 0x00;
buf[8] = USB_END_I2C_CMD;
ret = mxl111sf_i2c_send_data(state, 0, buf);
/* check for slave device status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("NACK writing slave address %02x",
msg->addr);
/* if NACK, stop I2C bus and exit */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0x4E;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
ret = -EIO;
goto exit;
}
/* I2C interface can do I2C operations in block of 8 bytes of
I2C data. calculation to figure out number of blocks of i2c
data required to program */
block_len = (msg->len / 8);
left_over_len = (msg->len % 8);
index = 0;
mxl_i2c("block_len %d, left_over_len %d",
block_len, left_over_len);
for (index = 0; index < block_len; index++) {
for (i = 0; i < 8; i++) {
/* write data on I2C interface */
buf[2+(i*3)] = I2C_DATA_REG;
buf[3+(i*3)] = msg->buf[(index*8)+i];
buf[4+(i*3)] = 0x00;
}
ret = mxl111sf_i2c_send_data(state, 0, buf);
/* check for I2C NACK status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("NACK writing slave address %02x",
msg->addr);
/* if NACK, stop I2C bus and exit */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0x4E;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
ret = -EIO;
goto exit;
}
}
if (left_over_len) {
for (k = 0; k < 26; k++)
buf[k] = USB_END_I2C_CMD;
buf[0] = 0x99;
buf[1] = 0x00;
for (i = 0; i < left_over_len; i++) {
buf[2+(i*3)] = I2C_DATA_REG;
buf[3+(i*3)] = msg->buf[(index*8)+i];
mxl_i2c("index = %d %d data %d",
index, i, msg->buf[(index*8)+i]);
buf[4+(i*3)] = 0x00;
}
ret = mxl111sf_i2c_send_data(state, 0, buf);
/* check for I2C NACK status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("NACK writing slave address %02x",
msg->addr);
/* if NACK, stop I2C bus and exit */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0x4E;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
ret = -EIO;
goto exit;
}
}
/* issue I2C STOP after write */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0x4E;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
}
/* read data from I2C bus */
if ((msg->flags & I2C_M_RD) && (msg->len > 0)) {
mxl_i2c("read buf len %d", msg->len);
/* command to indicate data payload is
destined for I2C interface */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0xDF;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
/* I2C xfer length */
buf[5] = 0x14;
buf[6] = (msg->len & 0xFF);
buf[7] = 0;
/* I2C slave device Address */
buf[8] = I2C_SLAVE_ADDR_REG;
buf[9] = msg->addr;
buf[10] = 0x00;
buf[11] = USB_END_I2C_CMD;
ret = mxl111sf_i2c_send_data(state, 0, buf);
/* check for I2C NACK status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("NACK reading slave address %02x",
msg->addr);
/* if NACK, stop I2C bus and exit */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0xC7;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
ret = -EIO;
goto exit;
}
/* I2C interface can do I2C operations in block of 8 bytes of
I2C data. calculation to figure out number of blocks of
i2c data required to program */
block_len = ((msg->len) / 8);
left_over_len = ((msg->len) % 8);
index = 0;
mxl_i2c("block_len %d, left_over_len %d",
block_len, left_over_len);
/* command to read data from I2C interface */
buf[0] = USB_READ_I2C_CMD;
buf[1] = 0x00;
for (index = 0; index < block_len; index++) {
/* setup I2C read request packet on I2C interface */
for (i = 0; i < 8; i++) {
buf[2+(i*3)] = I2C_DATA_REG;
buf[3+(i*3)] = 0x00;
buf[4+(i*3)] = 0x00;
}
ret = mxl111sf_i2c_get_data(state, 0, buf, i2c_r_data);
/* check for I2C NACK status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("NACK reading slave address %02x",
msg->addr);
/* if NACK, stop I2C bus and exit */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0xC7;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
ret = -EIO;
goto exit;
}
/* copy data from i2c data payload to read buffer */
for (i = 0; i < 8; i++) {
rd_status[i] = i2c_r_data[(i*3)+2];
if (rd_status[i] == 0x04) {
if (i < 7) {
mxl_i2c("i2c fifo empty!"
" @ %d", i);
msg->buf[(index*8)+i] =
i2c_r_data[(i*3)+1];
/* read again */
ret_status =
mxl111sf_i2c_readagain(
state, 8-(i+1),
readbuff);
if (ret_status == 1) {
for (k = 0;
k < 8-(i+1);
k++) {
msg->buf[(index*8)+(k+i+1)] =
readbuff[k];
mxl_i2c("read data: %02x\t %02x",
msg->buf[(index*8)+(k+i)],
(index*8)+(k+i));
mxl_i2c("read data: %02x\t %02x",
msg->buf[(index*8)+(k+i+1)],
readbuff[k]);
}
goto stop_copy;
} else {
mxl_i2c("readagain "
"ERROR!");
}
} else {
msg->buf[(index*8)+i] =
i2c_r_data[(i*3)+1];
}
} else {
msg->buf[(index*8)+i] =
i2c_r_data[(i*3)+1];
}
}
stop_copy:
;
}
if (left_over_len) {
for (k = 0; k < 26; k++)
buf[k] = USB_END_I2C_CMD;
buf[0] = 0xDD;
buf[1] = 0x00;
for (i = 0; i < left_over_len; i++) {
buf[2+(i*3)] = I2C_DATA_REG;
buf[3+(i*3)] = 0x00;
buf[4+(i*3)] = 0x00;
}
ret = mxl111sf_i2c_get_data(state, 0, buf,
i2c_r_data);
/* check for I2C NACK status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("NACK reading slave address %02x",
msg->addr);
/* if NACK, stop I2C bus and exit */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0xC7;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
ret = -EIO;
goto exit;
}
for (i = 0; i < left_over_len; i++) {
msg->buf[(block_len*8)+i] =
i2c_r_data[(i*3)+1];
mxl_i2c("read data: %02x\t %02x",
i2c_r_data[(i*3)+1],
i2c_r_data[(i*3)+2]);
}
}
/* indicate I2C interface to issue NACK
after next I2C read op */
buf[0] = USB_WRITE_I2C_CMD;
buf[1] = 0x00;
/* control register */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0x17;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
buf[5] = USB_END_I2C_CMD;
ret = mxl111sf_i2c_send_data(state, 0, buf);
/* control register */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0xC7;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
}
exit:
/* STOP and disable I2C MUX */
buf[0] = USB_WRITE_I2C_CMD;
buf[1] = 0x00;
/* de-initilize I2C BUS */
buf[5] = USB_END_I2C_CMD;
mxl111sf_i2c_send_data(state, 0, buf);
/* Control Register */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0xDF;
buf[4] = 0x03;
/* disable I2C interface */
buf[5] = I2C_MUX_REG;
buf[6] = 0x00;
buf[7] = 0x00;
/* de-initilize I2C BUS */
buf[8] = USB_END_I2C_CMD;
mxl111sf_i2c_send_data(state, 0, buf);
/* disable I2C interface */
buf[2] = I2C_MUX_REG;
buf[3] = 0x81;
buf[4] = 0x00;
/* disable I2C interface */
buf[5] = I2C_MUX_REG;
buf[6] = 0x00;
buf[7] = 0x00;
/* disable I2C interface */
buf[8] = I2C_MUX_REG;
buf[9] = 0x00;
buf[10] = 0x00;
buf[11] = USB_END_I2C_CMD;
mxl111sf_i2c_send_data(state, 0, buf);
return ret;
}
/* ------------------------------------------------------------------------ */
int mxl111sf_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg msg[], int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct mxl111sf_state *state = d->priv;
int hwi2c = (state->chip_rev > MXL111SF_V6);
int i, ret;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
for (i = 0; i < num; i++) {
ret = (hwi2c) ?
mxl111sf_i2c_hw_xfer_msg(state, &msg[i]) :
mxl111sf_i2c_sw_xfer_msg(state, &msg[i]);
if (mxl_fail(ret)) {
mxl_debug_adv("failed with error %d on i2c "
"transaction %d of %d, %sing %d bytes "
"to/from 0x%02x", ret, i+1, num,
(msg[i].flags & I2C_M_RD) ?
"read" : "writ",
msg[i].len, msg[i].addr);
break;
}
}
mutex_unlock(&d->i2c_mutex);
return i == num ? num : -EREMOTEIO;
}
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View file

@ -0,0 +1,35 @@
/*
* mxl111sf-i2c.h - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _DVB_USB_MXL111SF_I2C_H_
#define _DVB_USB_MXL111SF_I2C_H_
#include <linux/i2c.h>
int mxl111sf_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg msg[], int num);
#endif /* _DVB_USB_MXL111SF_I2C_H_ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View file

@ -0,0 +1,343 @@
/*
* mxl111sf-phy.c - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "mxl111sf-phy.h"
#include "mxl111sf-reg.h"
int mxl111sf_init_tuner_demod(struct mxl111sf_state *state)
{
struct mxl111sf_reg_ctrl_info mxl_111_overwrite_default[] = {
{0x07, 0xff, 0x0c},
{0x58, 0xff, 0x9d},
{0x09, 0xff, 0x00},
{0x06, 0xff, 0x06},
{0xc8, 0xff, 0x40}, /* ED_LE_WIN_OLD = 0 */
{0x8d, 0x01, 0x01}, /* NEGATE_Q */
{0x32, 0xff, 0xac}, /* DIG_RFREFSELECT = 12 */
{0x42, 0xff, 0x43}, /* DIG_REG_AMP = 4 */
{0x74, 0xff, 0xc4}, /* SSPUR_FS_PRIO = 4 */
{0x71, 0xff, 0xe6}, /* SPUR_ROT_PRIO_VAL = 1 */
{0x83, 0xff, 0x64}, /* INF_FILT1_THD_SC = 100 */
{0x85, 0xff, 0x64}, /* INF_FILT2_THD_SC = 100 */
{0x88, 0xff, 0xf0}, /* INF_THD = 240 */
{0x6f, 0xf0, 0xb0}, /* DFE_DLY = 11 */
{0x00, 0xff, 0x01}, /* Change to page 1 */
{0x81, 0xff, 0x11}, /* DSM_FERR_BYPASS = 1 */
{0xf4, 0xff, 0x07}, /* DIG_FREQ_CORR = 1 */
{0xd4, 0x1f, 0x0f}, /* SPUR_TEST_NOISE_TH = 15 */
{0xd6, 0xff, 0x0c}, /* SPUR_TEST_NOISE_PAPR = 12 */
{0x00, 0xff, 0x00}, /* Change to page 0 */
{0, 0, 0}
};
mxl_debug("()");
return mxl111sf_ctrl_program_regs(state, mxl_111_overwrite_default);
}
int mxl1x1sf_soft_reset(struct mxl111sf_state *state)
{
int ret;
mxl_debug("()");
ret = mxl111sf_write_reg(state, 0xff, 0x00); /* AIC */
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x02, 0x01); /* get out of reset */
mxl_fail(ret);
fail:
return ret;
}
int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode)
{
int ret;
mxl_debug("(%s)", MXL_SOC_MODE == mode ?
"MXL_SOC_MODE" : "MXL_TUNER_MODE");
/* set device mode */
ret = mxl111sf_write_reg(state, 0x03,
MXL_SOC_MODE == mode ? 0x01 : 0x00);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg_mask(state,
0x7d, 0x40, MXL_SOC_MODE == mode ?
0x00 : /* enable impulse noise filter,
INF_BYP = 0 */
0x40); /* disable impulse noise filter,
INF_BYP = 1 */
if (mxl_fail(ret))
goto fail;
state->device_mode = mode;
fail:
return ret;
}
/* power up tuner */
int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff)
{
mxl_debug("(%d)", onoff);
return mxl111sf_write_reg(state, 0x01, onoff ? 0x01 : 0x00);
}
int mxl111sf_disable_656_port(struct mxl111sf_state *state)
{
mxl_debug("()");
return mxl111sf_write_reg_mask(state, 0x12, 0x04, 0x00);
}
int mxl111sf_enable_usb_output(struct mxl111sf_state *state)
{
mxl_debug("()");
return mxl111sf_write_reg_mask(state, 0x17, 0x40, 0x00);
}
/* initialize TSIF as input port of MxL1X1SF for MPEG2 data transfer */
int mxl111sf_config_mpeg_in(struct mxl111sf_state *state,
unsigned int parallel_serial,
unsigned int msb_lsb_1st,
unsigned int clock_phase,
unsigned int mpeg_valid_pol,
unsigned int mpeg_sync_pol)
{
int ret;
u8 mode, tmp;
mxl_debug("(%u,%u,%u,%u,%u)", parallel_serial, msb_lsb_1st,
clock_phase, mpeg_valid_pol, mpeg_sync_pol);
/* Enable PIN MUX */
ret = mxl111sf_write_reg(state, V6_PIN_MUX_MODE_REG, V6_ENABLE_PIN_MUX);
mxl_fail(ret);
/* Configure MPEG Clock phase */
mxl111sf_read_reg(state, V6_MPEG_IN_CLK_INV_REG, &mode);
if (clock_phase == TSIF_NORMAL)
mode &= ~V6_INVERTED_CLK_PHASE;
else
mode |= V6_INVERTED_CLK_PHASE;
ret = mxl111sf_write_reg(state, V6_MPEG_IN_CLK_INV_REG, mode);
mxl_fail(ret);
/* Configure data input mode, MPEG Valid polarity, MPEG Sync polarity
* Get current configuration */
ret = mxl111sf_read_reg(state, V6_MPEG_IN_CTRL_REG, &mode);
mxl_fail(ret);
/* Data Input mode */
if (parallel_serial == TSIF_INPUT_PARALLEL) {
/* Disable serial mode */
mode &= ~V6_MPEG_IN_DATA_SERIAL;
/* Enable Parallel mode */
mode |= V6_MPEG_IN_DATA_PARALLEL;
} else {
/* Disable Parallel mode */
mode &= ~V6_MPEG_IN_DATA_PARALLEL;
/* Enable Serial Mode */
mode |= V6_MPEG_IN_DATA_SERIAL;
/* If serial interface is chosen, configure
MSB or LSB order in transmission */
ret = mxl111sf_read_reg(state,
V6_MPEG_INOUT_BIT_ORDER_CTRL_REG,
&tmp);
mxl_fail(ret);
if (msb_lsb_1st == MPEG_SER_MSB_FIRST_ENABLED)
tmp |= V6_MPEG_SER_MSB_FIRST;
else
tmp &= ~V6_MPEG_SER_MSB_FIRST;
ret = mxl111sf_write_reg(state,
V6_MPEG_INOUT_BIT_ORDER_CTRL_REG,
tmp);
mxl_fail(ret);
}
/* MPEG Sync polarity */
if (mpeg_sync_pol == TSIF_NORMAL)
mode &= ~V6_INVERTED_MPEG_SYNC;
else
mode |= V6_INVERTED_MPEG_SYNC;
/* MPEG Valid polarity */
if (mpeg_valid_pol == 0)
mode &= ~V6_INVERTED_MPEG_VALID;
else
mode |= V6_INVERTED_MPEG_VALID;
ret = mxl111sf_write_reg(state, V6_MPEG_IN_CTRL_REG, mode);
mxl_fail(ret);
return ret;
}
int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size)
{
static struct mxl111sf_reg_ctrl_info init_i2s[] = {
{0x1b, 0xff, 0x1e}, /* pin mux mode, Choose 656/I2S input */
{0x15, 0x60, 0x60}, /* Enable I2S */
{0x17, 0xe0, 0x20}, /* Input, MPEG MODE USB,
Inverted 656 Clock, I2S_SOFT_RESET,
0 : Normal operation, 1 : Reset State */
#if 0
{0x12, 0x01, 0x00}, /* AUDIO_IRQ_CLR (Overflow Indicator) */
#endif
{0x00, 0xff, 0x02}, /* Change to Control Page */
{0x26, 0x0d, 0x0d}, /* I2S_MODE & BT656_SRC_SEL for FPGA only */
{0x00, 0xff, 0x00},
{0, 0, 0}
};
int ret;
mxl_debug("(0x%02x)", sample_size);
ret = mxl111sf_ctrl_program_regs(state, init_i2s);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, V6_I2S_NUM_SAMPLES_REG, sample_size);
mxl_fail(ret);
fail:
return ret;
}
int mxl111sf_disable_i2s_port(struct mxl111sf_state *state)
{
static struct mxl111sf_reg_ctrl_info disable_i2s[] = {
{0x15, 0x40, 0x00},
{0, 0, 0}
};
mxl_debug("()");
return mxl111sf_ctrl_program_regs(state, disable_i2s);
}
int mxl111sf_config_i2s(struct mxl111sf_state *state,
u8 msb_start_pos, u8 data_width)
{
int ret;
u8 tmp;
mxl_debug("(0x%02x, 0x%02x)", msb_start_pos, data_width);
ret = mxl111sf_read_reg(state, V6_I2S_STREAM_START_BIT_REG, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= 0xe0;
tmp |= msb_start_pos;
ret = mxl111sf_write_reg(state, V6_I2S_STREAM_START_BIT_REG, tmp);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, V6_I2S_STREAM_END_BIT_REG, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= 0xe0;
tmp |= data_width;
ret = mxl111sf_write_reg(state, V6_I2S_STREAM_END_BIT_REG, tmp);
mxl_fail(ret);
fail:
return ret;
}
int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff)
{
u8 val;
int ret;
mxl_debug("(%d)", onoff);
ret = mxl111sf_write_reg(state, 0x00, 0x02);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, V8_SPI_MODE_REG, &val);
if (mxl_fail(ret))
goto fail;
if (onoff)
val |= 0x04;
else
val &= ~0x04;
ret = mxl111sf_write_reg(state, V8_SPI_MODE_REG, val);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x00, 0x00);
mxl_fail(ret);
fail:
return ret;
}
int mxl111sf_idac_config(struct mxl111sf_state *state,
u8 control_mode, u8 current_setting,
u8 current_value, u8 hysteresis_value)
{
int ret;
u8 val;
/* current value will be set for both automatic & manual IDAC control */
val = current_value;
if (control_mode == IDAC_MANUAL_CONTROL) {
/* enable manual control of IDAC */
val |= IDAC_MANUAL_CONTROL_BIT_MASK;
if (current_setting == IDAC_CURRENT_SINKING_ENABLE)
/* enable current sinking in manual mode */
val |= IDAC_CURRENT_SINKING_BIT_MASK;
else
/* disable current sinking in manual mode */
val &= ~IDAC_CURRENT_SINKING_BIT_MASK;
} else {
/* disable manual control of IDAC */
val &= ~IDAC_MANUAL_CONTROL_BIT_MASK;
/* set hysteresis value reg: 0x0B<5:0> */
ret = mxl111sf_write_reg(state, V6_IDAC_HYSTERESIS_REG,
(hysteresis_value & 0x3F));
mxl_fail(ret);
}
ret = mxl111sf_write_reg(state, V6_IDAC_SETTINGS_REG, val);
mxl_fail(ret);
return ret;
}
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View file

@ -0,0 +1,53 @@
/*
* mxl111sf-phy.h - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _DVB_USB_MXL111SF_PHY_H_
#define _DVB_USB_MXL111SF_PHY_H_
#include "mxl111sf.h"
int mxl1x1sf_soft_reset(struct mxl111sf_state *state);
int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode);
int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff);
int mxl111sf_disable_656_port(struct mxl111sf_state *state);
int mxl111sf_init_tuner_demod(struct mxl111sf_state *state);
int mxl111sf_enable_usb_output(struct mxl111sf_state *state);
int mxl111sf_config_mpeg_in(struct mxl111sf_state *state,
unsigned int parallel_serial,
unsigned int msb_lsb_1st,
unsigned int clock_phase,
unsigned int mpeg_valid_pol,
unsigned int mpeg_sync_pol);
int mxl111sf_config_i2s(struct mxl111sf_state *state,
u8 msb_start_pos, u8 data_width);
int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size);
int mxl111sf_disable_i2s_port(struct mxl111sf_state *state);
int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff);
int mxl111sf_idac_config(struct mxl111sf_state *state,
u8 control_mode, u8 current_setting,
u8 current_value, u8 hysteresis_value);
#endif /* _DVB_USB_MXL111SF_PHY_H_ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View file

@ -0,0 +1,179 @@
/*
* mxl111sf-reg.h - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _DVB_USB_MXL111SF_REG_H_
#define _DVB_USB_MXL111SF_REG_H_
#define CHIP_ID_REG 0xFC
#define TOP_CHIP_REV_ID_REG 0xFA
#define V6_SNR_RB_LSB_REG 0x27
#define V6_SNR_RB_MSB_REG 0x28
#define V6_N_ACCUMULATE_REG 0x11
#define V6_RS_AVG_ERRORS_LSB_REG 0x2C
#define V6_RS_AVG_ERRORS_MSB_REG 0x2D
#define V6_IRQ_STATUS_REG 0x24
#define IRQ_MASK_FEC_LOCK 0x10
#define V6_SYNC_LOCK_REG 0x28
#define SYNC_LOCK_MASK 0x10
#define V6_RS_LOCK_DET_REG 0x28
#define RS_LOCK_DET_MASK 0x08
#define V6_INITACQ_NODETECT_REG 0x20
#define V6_FORCE_NFFT_CPSIZE_REG 0x20
#define V6_CODE_RATE_TPS_REG 0x29
#define V6_CODE_RATE_TPS_MASK 0x07
#define V6_CP_LOCK_DET_REG 0x28
#define V6_CP_LOCK_DET_MASK 0x04
#define V6_TPS_HIERACHY_REG 0x29
#define V6_TPS_HIERARCHY_INFO_MASK 0x40
#define V6_MODORDER_TPS_REG 0x2A
#define V6_PARAM_CONSTELLATION_MASK 0x30
#define V6_MODE_TPS_REG 0x2A
#define V6_PARAM_FFT_MODE_MASK 0x0C
#define V6_CP_TPS_REG 0x29
#define V6_PARAM_GI_MASK 0x30
#define V6_TPS_LOCK_REG 0x2A
#define V6_PARAM_TPS_LOCK_MASK 0x40
#define V6_FEC_PER_COUNT_REG 0x2E
#define V6_FEC_PER_SCALE_REG 0x2B
#define V6_FEC_PER_SCALE_MASK 0x03
#define V6_FEC_PER_CLR_REG 0x20
#define V6_FEC_PER_CLR_MASK 0x01
#define V6_PIN_MUX_MODE_REG 0x1B
#define V6_ENABLE_PIN_MUX 0x1E
#define V6_I2S_NUM_SAMPLES_REG 0x16
#define V6_MPEG_IN_CLK_INV_REG 0x17
#define V6_MPEG_IN_CTRL_REG 0x18
#define V6_INVERTED_CLK_PHASE 0x20
#define V6_MPEG_IN_DATA_PARALLEL 0x01
#define V6_MPEG_IN_DATA_SERIAL 0x02
#define V6_INVERTED_MPEG_SYNC 0x04
#define V6_INVERTED_MPEG_VALID 0x08
#define TSIF_INPUT_PARALLEL 0
#define TSIF_INPUT_SERIAL 1
#define TSIF_NORMAL 0
#define V6_MPEG_INOUT_BIT_ORDER_CTRL_REG 0x19
#define V6_MPEG_SER_MSB_FIRST 0x80
#define MPEG_SER_MSB_FIRST_ENABLED 0x01
#define V6_656_I2S_BUFF_STATUS_REG 0x2F
#define V6_656_OVERFLOW_MASK_BIT 0x08
#define V6_I2S_OVERFLOW_MASK_BIT 0x01
#define V6_I2S_STREAM_START_BIT_REG 0x14
#define V6_I2S_STREAM_END_BIT_REG 0x15
#define I2S_RIGHT_JUSTIFIED 0
#define I2S_LEFT_JUSTIFIED 1
#define I2S_DATA_FORMAT 2
#define V6_TUNER_LOOP_THRU_CONTROL_REG 0x09
#define V6_ENABLE_LOOP_THRU 0x01
#define TOTAL_NUM_IF_OUTPUT_FREQ 16
#define TUNER_NORMAL_IF_SPECTRUM 0x0
#define TUNER_INVERT_IF_SPECTRUM 0x10
#define V6_TUNER_IF_SEL_REG 0x06
#define V6_TUNER_IF_FCW_REG 0x3C
#define V6_TUNER_IF_FCW_BYP_REG 0x3D
#define V6_RF_LOCK_STATUS_REG 0x23
#define NUM_DIG_TV_CHANNEL 1000
#define V6_DIG_CLK_FREQ_SEL_REG 0x07
#define V6_REF_SYNTH_INT_REG 0x5C
#define V6_REF_SYNTH_REMAIN_REG 0x58
#define V6_DIG_RFREFSELECT_REG 0x32
#define V6_XTAL_CLK_OUT_GAIN_REG 0x31
#define V6_TUNER_LOOP_THRU_CTRL_REG 0x09
#define V6_DIG_XTAL_ENABLE_REG 0x06
#define V6_DIG_XTAL_BIAS_REG 0x66
#define V6_XTAL_CAP_REG 0x08
#define V6_GPO_CTRL_REG 0x18
#define MXL_GPO_0 0x00
#define MXL_GPO_1 0x01
#define V6_GPO_0_MASK 0x10
#define V6_GPO_1_MASK 0x20
#define V6_111SF_GPO_CTRL_REG 0x19
#define MXL_111SF_GPO_1 0x00
#define MXL_111SF_GPO_2 0x01
#define MXL_111SF_GPO_3 0x02
#define MXL_111SF_GPO_4 0x03
#define MXL_111SF_GPO_5 0x04
#define MXL_111SF_GPO_6 0x05
#define MXL_111SF_GPO_7 0x06
#define MXL_111SF_GPO_0_MASK 0x01
#define MXL_111SF_GPO_1_MASK 0x02
#define MXL_111SF_GPO_2_MASK 0x04
#define MXL_111SF_GPO_3_MASK 0x08
#define MXL_111SF_GPO_4_MASK 0x10
#define MXL_111SF_GPO_5_MASK 0x20
#define MXL_111SF_GPO_6_MASK 0x40
#define V6_ATSC_CONFIG_REG 0x0A
#define MXL_MODE_REG 0x03
#define START_TUNE_REG 0x1C
#define V6_IDAC_HYSTERESIS_REG 0x0B
#define V6_IDAC_SETTINGS_REG 0x0C
#define IDAC_MANUAL_CONTROL 1
#define IDAC_CURRENT_SINKING_ENABLE 1
#define IDAC_MANUAL_CONTROL_BIT_MASK 0x80
#define IDAC_CURRENT_SINKING_BIT_MASK 0x40
#define V8_SPI_MODE_REG 0xE9
#define V6_DIG_RF_PWR_LSB_REG 0x46
#define V6_DIG_RF_PWR_MSB_REG 0x47
#endif /* _DVB_USB_MXL111SF_REG_H_ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View file

@ -0,0 +1,525 @@
/*
* mxl111sf-tuner.c - driver for the MaxLinear MXL111SF CMOS tuner
*
* Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "mxl111sf-tuner.h"
#include "mxl111sf-phy.h"
#include "mxl111sf-reg.h"
/* debug */
static int mxl111sf_tuner_debug;
module_param_named(debug, mxl111sf_tuner_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
#define mxl_dbg(fmt, arg...) \
if (mxl111sf_tuner_debug) \
mxl_printk(KERN_DEBUG, fmt, ##arg)
/* ------------------------------------------------------------------------ */
struct mxl111sf_tuner_state {
struct mxl111sf_state *mxl_state;
struct mxl111sf_tuner_config *cfg;
enum mxl_if_freq if_freq;
u32 frequency;
u32 bandwidth;
};
static int mxl111sf_tuner_read_reg(struct mxl111sf_tuner_state *state,
u8 addr, u8 *data)
{
return (state->cfg->read_reg) ?
state->cfg->read_reg(state->mxl_state, addr, data) :
-EINVAL;
}
static int mxl111sf_tuner_write_reg(struct mxl111sf_tuner_state *state,
u8 addr, u8 data)
{
return (state->cfg->write_reg) ?
state->cfg->write_reg(state->mxl_state, addr, data) :
-EINVAL;
}
static int mxl111sf_tuner_program_regs(struct mxl111sf_tuner_state *state,
struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
{
return (state->cfg->program_regs) ?
state->cfg->program_regs(state->mxl_state, ctrl_reg_info) :
-EINVAL;
}
static int mxl1x1sf_tuner_top_master_ctrl(struct mxl111sf_tuner_state *state,
int onoff)
{
return (state->cfg->top_master_ctrl) ?
state->cfg->top_master_ctrl(state->mxl_state, onoff) :
-EINVAL;
}
/* ------------------------------------------------------------------------ */
static struct mxl111sf_reg_ctrl_info mxl_phy_tune_rf[] = {
{0x1d, 0x7f, 0x00}, /* channel bandwidth section 1/2/3,
DIG_MODEINDEX, _A, _CSF, */
{0x1e, 0xff, 0x00}, /* channel frequency (lo and fractional) */
{0x1f, 0xff, 0x00}, /* channel frequency (hi for integer portion) */
{0, 0, 0}
};
/* ------------------------------------------------------------------------ */
static struct mxl111sf_reg_ctrl_info *mxl111sf_calc_phy_tune_regs(u32 freq,
u8 bw)
{
u8 filt_bw;
/* set channel bandwidth */
switch (bw) {
case 0: /* ATSC */
filt_bw = 25;
break;
case 1: /* QAM */
filt_bw = 69;
break;
case 6:
filt_bw = 21;
break;
case 7:
filt_bw = 42;
break;
case 8:
filt_bw = 63;
break;
default:
pr_err("%s: invalid bandwidth setting!", __func__);
return NULL;
}
/* calculate RF channel */
freq /= 1000000;
freq *= 64;
#if 0
/* do round */
freq += 0.5;
#endif
/* set bandwidth */
mxl_phy_tune_rf[0].data = filt_bw;
/* set RF */
mxl_phy_tune_rf[1].data = (freq & 0xff);
mxl_phy_tune_rf[2].data = (freq >> 8) & 0xff;
/* start tune */
return mxl_phy_tune_rf;
}
static int mxl1x1sf_tuner_set_if_output_freq(struct mxl111sf_tuner_state *state)
{
int ret;
u8 ctrl;
#if 0
u16 iffcw;
u32 if_freq;
#endif
mxl_dbg("(IF polarity = %d, IF freq = 0x%02x)",
state->cfg->invert_spectrum, state->cfg->if_freq);
/* set IF polarity */
ctrl = state->cfg->invert_spectrum;
ctrl |= state->cfg->if_freq;
ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_SEL_REG, ctrl);
if (mxl_fail(ret))
goto fail;
#if 0
if_freq /= 1000000;
/* do round */
if_freq += 0.5;
if (MXL_IF_LO == state->cfg->if_freq) {
ctrl = 0x08;
iffcw = (u16)(if_freq / (108 * 4096));
} else if (MXL_IF_HI == state->cfg->if_freq) {
ctrl = 0x08;
iffcw = (u16)(if_freq / (216 * 4096));
} else {
ctrl = 0;
iffcw = 0;
}
ctrl |= (iffcw >> 8);
#endif
ret = mxl111sf_tuner_read_reg(state, V6_TUNER_IF_FCW_BYP_REG, &ctrl);
if (mxl_fail(ret))
goto fail;
ctrl &= 0xf0;
ctrl |= 0x90;
ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_BYP_REG, ctrl);
if (mxl_fail(ret))
goto fail;
#if 0
ctrl = iffcw & 0x00ff;
#endif
ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_REG, ctrl);
if (mxl_fail(ret))
goto fail;
state->if_freq = state->cfg->if_freq;
fail:
return ret;
}
static int mxl1x1sf_tune_rf(struct dvb_frontend *fe, u32 freq, u8 bw)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
static struct mxl111sf_reg_ctrl_info *reg_ctrl_array;
int ret;
u8 mxl_mode;
mxl_dbg("(freq = %d, bw = 0x%x)", freq, bw);
/* stop tune */
ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 0);
if (mxl_fail(ret))
goto fail;
/* check device mode */
ret = mxl111sf_tuner_read_reg(state, MXL_MODE_REG, &mxl_mode);
if (mxl_fail(ret))
goto fail;
/* Fill out registers for channel tune */
reg_ctrl_array = mxl111sf_calc_phy_tune_regs(freq, bw);
if (!reg_ctrl_array)
return -EINVAL;
ret = mxl111sf_tuner_program_regs(state, reg_ctrl_array);
if (mxl_fail(ret))
goto fail;
if ((mxl_mode & MXL_DEV_MODE_MASK) == MXL_TUNER_MODE) {
/* IF tuner mode only */
mxl1x1sf_tuner_top_master_ctrl(state, 0);
mxl1x1sf_tuner_top_master_ctrl(state, 1);
mxl1x1sf_tuner_set_if_output_freq(state);
}
ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 1);
if (mxl_fail(ret))
goto fail;
if (state->cfg->ant_hunt)
state->cfg->ant_hunt(fe);
fail:
return ret;
}
static int mxl1x1sf_tuner_get_lock_status(struct mxl111sf_tuner_state *state,
int *rf_synth_lock,
int *ref_synth_lock)
{
int ret;
u8 data;
*rf_synth_lock = 0;
*ref_synth_lock = 0;
ret = mxl111sf_tuner_read_reg(state, V6_RF_LOCK_STATUS_REG, &data);
if (mxl_fail(ret))
goto fail;
*ref_synth_lock = ((data & 0x03) == 0x03) ? 1 : 0;
*rf_synth_lock = ((data & 0x0c) == 0x0c) ? 1 : 0;
fail:
return ret;
}
#if 0
static int mxl1x1sf_tuner_loop_thru_ctrl(struct mxl111sf_tuner_state *state,
int onoff)
{
return mxl111sf_tuner_write_reg(state, V6_TUNER_LOOP_THRU_CTRL_REG,
onoff ? 1 : 0);
}
#endif
/* ------------------------------------------------------------------------ */
static int mxl111sf_tuner_set_params(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
u32 delsys = c->delivery_system;
struct mxl111sf_tuner_state *state = fe->tuner_priv;
int ret;
u8 bw;
mxl_dbg("()");
switch (delsys) {
case SYS_ATSC:
case SYS_ATSCMH:
bw = 0; /* ATSC */
break;
case SYS_DVBC_ANNEX_B:
bw = 1; /* US CABLE */
break;
case SYS_DVBT:
switch (c->bandwidth_hz) {
case 6000000:
bw = 6;
break;
case 7000000:
bw = 7;
break;
case 8000000:
bw = 8;
break;
default:
pr_err("%s: bandwidth not set!", __func__);
return -EINVAL;
}
break;
default:
pr_err("%s: modulation type not supported!", __func__);
return -EINVAL;
}
ret = mxl1x1sf_tune_rf(fe, c->frequency, bw);
if (mxl_fail(ret))
goto fail;
state->frequency = c->frequency;
state->bandwidth = c->bandwidth_hz;
fail:
return ret;
}
/* ------------------------------------------------------------------------ */
#if 0
static int mxl111sf_tuner_init(struct dvb_frontend *fe)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
int ret;
/* wake from standby handled by usb driver */
return ret;
}
static int mxl111sf_tuner_sleep(struct dvb_frontend *fe)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
int ret;
/* enter standby mode handled by usb driver */
return ret;
}
#endif
/* ------------------------------------------------------------------------ */
static int mxl111sf_tuner_get_status(struct dvb_frontend *fe, u32 *status)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
int rf_locked, ref_locked, ret;
*status = 0;
ret = mxl1x1sf_tuner_get_lock_status(state, &rf_locked, &ref_locked);
if (mxl_fail(ret))
goto fail;
mxl_info("%s%s", rf_locked ? "rf locked " : "",
ref_locked ? "ref locked" : "");
if ((rf_locked) || (ref_locked))
*status |= TUNER_STATUS_LOCKED;
fail:
return ret;
}
static int mxl111sf_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
u8 val1, val2;
int ret;
*strength = 0;
ret = mxl111sf_tuner_write_reg(state, 0x00, 0x02);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_LSB_REG, &val1);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_MSB_REG, &val2);
if (mxl_fail(ret))
goto fail;
*strength = val1 | ((val2 & 0x07) << 8);
fail:
ret = mxl111sf_tuner_write_reg(state, 0x00, 0x00);
mxl_fail(ret);
return ret;
}
/* ------------------------------------------------------------------------ */
static int mxl111sf_tuner_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
*frequency = state->frequency;
return 0;
}
static int mxl111sf_tuner_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
*bandwidth = state->bandwidth;
return 0;
}
static int mxl111sf_tuner_get_if_frequency(struct dvb_frontend *fe,
u32 *frequency)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
*frequency = 0;
switch (state->if_freq) {
case MXL_IF_4_0: /* 4.0 MHz */
*frequency = 4000000;
break;
case MXL_IF_4_5: /* 4.5 MHz */
*frequency = 4500000;
break;
case MXL_IF_4_57: /* 4.57 MHz */
*frequency = 4570000;
break;
case MXL_IF_5_0: /* 5.0 MHz */
*frequency = 5000000;
break;
case MXL_IF_5_38: /* 5.38 MHz */
*frequency = 5380000;
break;
case MXL_IF_6_0: /* 6.0 MHz */
*frequency = 6000000;
break;
case MXL_IF_6_28: /* 6.28 MHz */
*frequency = 6280000;
break;
case MXL_IF_7_2: /* 7.2 MHz */
*frequency = 7200000;
break;
case MXL_IF_35_25: /* 35.25 MHz */
*frequency = 35250000;
break;
case MXL_IF_36: /* 36 MHz */
*frequency = 36000000;
break;
case MXL_IF_36_15: /* 36.15 MHz */
*frequency = 36150000;
break;
case MXL_IF_44: /* 44 MHz */
*frequency = 44000000;
break;
}
return 0;
}
static int mxl111sf_tuner_release(struct dvb_frontend *fe)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
mxl_dbg("()");
kfree(state);
fe->tuner_priv = NULL;
return 0;
}
/* ------------------------------------------------------------------------- */
static struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = {
.info = {
.name = "MaxLinear MxL111SF",
#if 0
.frequency_min = ,
.frequency_max = ,
.frequency_step = ,
#endif
},
#if 0
.init = mxl111sf_tuner_init,
.sleep = mxl111sf_tuner_sleep,
#endif
.set_params = mxl111sf_tuner_set_params,
.get_status = mxl111sf_tuner_get_status,
.get_rf_strength = mxl111sf_get_rf_strength,
.get_frequency = mxl111sf_tuner_get_frequency,
.get_bandwidth = mxl111sf_tuner_get_bandwidth,
.get_if_frequency = mxl111sf_tuner_get_if_frequency,
.release = mxl111sf_tuner_release,
};
struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
struct mxl111sf_state *mxl_state,
struct mxl111sf_tuner_config *cfg)
{
struct mxl111sf_tuner_state *state = NULL;
mxl_dbg("()");
state = kzalloc(sizeof(struct mxl111sf_tuner_state), GFP_KERNEL);
if (state == NULL)
return NULL;
state->mxl_state = mxl_state;
state->cfg = cfg;
memcpy(&fe->ops.tuner_ops, &mxl111sf_tuner_tuner_ops,
sizeof(struct dvb_tuner_ops));
fe->tuner_priv = state;
return fe;
}
EXPORT_SYMBOL_GPL(mxl111sf_tuner_attach);
MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver");
MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* ---------------------------------------------------------------------------
* Local variables:
* c-basic-offset: 8
* End:
*/

View file

@ -0,0 +1,88 @@
/*
* mxl111sf-tuner.h - driver for the MaxLinear MXL111SF CMOS tuner
*
* Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __MXL111SF_TUNER_H__
#define __MXL111SF_TUNER_H__
#include <linux/kconfig.h>
#include "dvb_frontend.h"
#include "mxl111sf.h"
enum mxl_if_freq {
#if 0
MXL_IF_LO = 0x00, /* other IF < 9MHz */
#endif
MXL_IF_4_0 = 0x01, /* 4.0 MHz */
MXL_IF_4_5 = 0x02, /* 4.5 MHz */
MXL_IF_4_57 = 0x03, /* 4.57 MHz */
MXL_IF_5_0 = 0x04, /* 5.0 MHz */
MXL_IF_5_38 = 0x05, /* 5.38 MHz */
MXL_IF_6_0 = 0x06, /* 6.0 MHz */
MXL_IF_6_28 = 0x07, /* 6.28 MHz */
MXL_IF_7_2 = 0x08, /* 7.2 MHz */
MXL_IF_35_25 = 0x09, /* 35.25 MHz */
MXL_IF_36 = 0x0a, /* 36 MHz */
MXL_IF_36_15 = 0x0b, /* 36.15 MHz */
MXL_IF_44 = 0x0c, /* 44 MHz */
#if 0
MXL_IF_HI = 0x0f, /* other IF > 35 MHz and < 45 MHz */
#endif
};
struct mxl111sf_tuner_config {
enum mxl_if_freq if_freq;
unsigned int invert_spectrum:1;
int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data);
int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data);
int (*program_regs)(struct mxl111sf_state *state,
struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
int (*top_master_ctrl)(struct mxl111sf_state *state, int onoff);
int (*ant_hunt)(struct dvb_frontend *fe);
};
/* ------------------------------------------------------------------------ */
#if IS_ENABLED(CONFIG_DVB_USB_MXL111SF)
extern
struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
struct mxl111sf_state *mxl_state,
struct mxl111sf_tuner_config *cfg);
#else
static inline
struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
struct mxl111sf_state *mxl_state,
struct mxl111sf_tuner_config *cfg)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
#endif
#endif /* __MXL111SF_TUNER_H__ */
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* ---------------------------------------------------------------------------
* Local variables:
* c-basic-offset: 8
* End:
*/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,160 @@
/*
* Copyright (C) 2010-2014 Michael Krufky (mkrufky@linuxtv.org)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#ifndef _DVB_USB_MXL111SF_H_
#define _DVB_USB_MXL111SF_H_
#ifdef DVB_USB_LOG_PREFIX
#undef DVB_USB_LOG_PREFIX
#endif
#define DVB_USB_LOG_PREFIX "mxl111sf"
#include "dvb_usb.h"
#include <media/tveeprom.h>
#define MXL_EP1_REG_READ 1
#define MXL_EP2_REG_WRITE 2
#define MXL_EP3_INTERRUPT 3
#define MXL_EP4_MPEG2 4
#define MXL_EP5_I2S 5
#define MXL_EP6_656 6
#define MXL_EP6_MPEG2 6
#ifdef USING_ENUM_mxl111sf_current_mode
enum mxl111sf_current_mode {
mxl_mode_dvbt = MXL_EP4_MPEG2,
mxl_mode_mh = MXL_EP5_I2S,
mxl_mode_atsc = MXL_EP6_MPEG2,
};
#endif
enum mxl111sf_gpio_port_expander {
mxl111sf_gpio_hw,
mxl111sf_PCA9534,
};
struct mxl111sf_adap_state {
int alt_mode;
int gpio_mode;
int device_mode;
int ep6_clockphase;
int (*fe_init)(struct dvb_frontend *);
int (*fe_sleep)(struct dvb_frontend *);
};
struct mxl111sf_state {
struct dvb_usb_device *d;
enum mxl111sf_gpio_port_expander gpio_port_expander;
u8 port_expander_addr;
u8 chip_id;
u8 chip_ver;
#define MXL111SF_V6 1
#define MXL111SF_V8_100 2
#define MXL111SF_V8_200 3
u8 chip_rev;
#ifdef USING_ENUM_mxl111sf_current_mode
enum mxl111sf_current_mode current_mode;
#endif
#define MXL_TUNER_MODE 0
#define MXL_SOC_MODE 1
#define MXL_DEV_MODE_MASK 0x01
#if 1
int device_mode;
#endif
/* use usb alt setting 1 for EP4 ISOC transfer (dvb-t),
EP5 BULK transfer (atsc-mh),
EP6 BULK transfer (atsc/qam),
use usb alt setting 2 for EP4 BULK transfer (dvb-t),
EP5 ISOC transfer (atsc-mh),
EP6 ISOC transfer (atsc/qam),
*/
int alt_mode;
int gpio_mode;
struct tveeprom tv;
struct mutex fe_lock;
u8 num_frontends;
struct mxl111sf_adap_state adap_state[3];
};
int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data);
int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data);
struct mxl111sf_reg_ctrl_info {
u8 addr;
u8 mask;
u8 data;
};
int mxl111sf_write_reg_mask(struct mxl111sf_state *state,
u8 addr, u8 mask, u8 data);
int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state,
struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
/* needed for hardware i2c functions in mxl111sf-i2c.c:
* mxl111sf_i2c_send_data / mxl111sf_i2c_get_data */
int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen);
#define mxl_printk(kern, fmt, arg...) \
printk(kern "%s: " fmt "\n", __func__, ##arg)
#define mxl_info(fmt, arg...) \
mxl_printk(KERN_INFO, fmt, ##arg)
extern int dvb_usb_mxl111sf_debug;
#define mxl_debug(fmt, arg...) \
if (dvb_usb_mxl111sf_debug) \
mxl_printk(KERN_DEBUG, fmt, ##arg)
#define MXL_I2C_DBG 0x04
#define MXL_ADV_DBG 0x10
#define mxl_debug_adv(fmt, arg...) \
if (dvb_usb_mxl111sf_debug & MXL_ADV_DBG) \
mxl_printk(KERN_DEBUG, fmt, ##arg)
#define mxl_i2c(fmt, arg...) \
if (dvb_usb_mxl111sf_debug & MXL_I2C_DBG) \
mxl_printk(KERN_DEBUG, fmt, ##arg)
#define mxl_i2c_adv(fmt, arg...) \
if ((dvb_usb_mxl111sf_debug & (MXL_I2C_DBG | MXL_ADV_DBG)) == \
(MXL_I2C_DBG | MXL_ADV_DBG)) \
mxl_printk(KERN_DEBUG, fmt, ##arg)
/* The following allows the mxl_fail() macro defined below to work
* in externel modules, such as mxl111sf-tuner.ko, even though
* dvb_usb_mxl111sf_debug is not defined within those modules */
#if (defined(__MXL111SF_TUNER_H__)) || (defined(__MXL111SF_DEMOD_H__))
#define MXL_ADV_DEBUG_ENABLED MXL_ADV_DBG
#else
#define MXL_ADV_DEBUG_ENABLED dvb_usb_mxl111sf_debug
#endif
#define mxl_fail(ret) \
({ \
int __ret; \
__ret = (ret < 0); \
if ((__ret) && (MXL_ADV_DEBUG_ENABLED & MXL_ADV_DBG)) \
mxl_printk(KERN_ERR, "error %d on line %d", \
ret, __LINE__); \
__ret; \
})
#endif /* _DVB_USB_MXL111SF_H_ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,255 @@
/*
* Realtek RTL28xxU DVB USB driver
*
* Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
* Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef RTL28XXU_H
#define RTL28XXU_H
#include "dvb_usb.h"
/*
* USB commands
* (usb_control_msg() index parameter)
*/
#define DEMOD 0x0000
#define USB 0x0100
#define SYS 0x0200
#define I2C 0x0300
#define I2C_DA 0x0600
#define CMD_WR_FLAG 0x0010
#define CMD_DEMOD_RD 0x0000
#define CMD_DEMOD_WR 0x0010
#define CMD_USB_RD 0x0100
#define CMD_USB_WR 0x0110
#define CMD_SYS_RD 0x0200
#define CMD_IR_RD 0x0201
#define CMD_IR_WR 0x0211
#define CMD_SYS_WR 0x0210
#define CMD_I2C_RD 0x0300
#define CMD_I2C_WR 0x0310
#define CMD_I2C_DA_RD 0x0600
#define CMD_I2C_DA_WR 0x0610
struct rtl28xxu_priv {
u8 chip_id;
u8 tuner;
char *tuner_name;
u8 page; /* integrated demod active register page */
struct i2c_adapter *demod_i2c_adapter;
bool rc_active;
struct i2c_client *client;
};
enum rtl28xxu_chip_id {
CHIP_ID_NONE,
CHIP_ID_RTL2831U,
CHIP_ID_RTL2832U,
};
/* XXX: Hack. This must be keep sync with rtl2832 demod driver. */
enum rtl28xxu_tuner {
TUNER_NONE,
TUNER_RTL2830_QT1010 = 0x10,
TUNER_RTL2830_MT2060,
TUNER_RTL2830_MXL5005S,
TUNER_RTL2832_MT2266 = 0x20,
TUNER_RTL2832_FC2580,
TUNER_RTL2832_MT2063,
TUNER_RTL2832_MAX3543,
TUNER_RTL2832_TUA9001,
TUNER_RTL2832_MXL5007T,
TUNER_RTL2832_FC0012,
TUNER_RTL2832_E4000,
TUNER_RTL2832_TDA18272,
TUNER_RTL2832_FC0013,
TUNER_RTL2832_R820T,
TUNER_RTL2832_R828D,
};
struct rtl28xxu_req {
u16 value;
u16 index;
u16 size;
u8 *data;
};
struct rtl28xxu_reg_val {
u16 reg;
u8 val;
};
struct rtl28xxu_reg_val_mask {
u16 reg;
u8 val;
u8 mask;
};
/*
* memory map
*
* 0x0000 DEMOD : demodulator
* 0x2000 USB : SIE, USB endpoint, debug, DMA
* 0x3000 SYS : system
* 0xfc00 RC : remote controller (not RTL2831U)
*/
/*
* USB registers
*/
/* SIE Control Registers */
#define USB_SYSCTL 0x2000 /* USB system control */
#define USB_SYSCTL_0 0x2000 /* USB system control */
#define USB_SYSCTL_1 0x2001 /* USB system control */
#define USB_SYSCTL_2 0x2002 /* USB system control */
#define USB_SYSCTL_3 0x2003 /* USB system control */
#define USB_IRQSTAT 0x2008 /* SIE interrupt status */
#define USB_IRQEN 0x200C /* SIE interrupt enable */
#define USB_CTRL 0x2010 /* USB control */
#define USB_STAT 0x2014 /* USB status */
#define USB_DEVADDR 0x2018 /* USB device address */
#define USB_TEST 0x201C /* USB test mode */
#define USB_FRAME_NUMBER 0x2020 /* frame number */
#define USB_FIFO_ADDR 0x2028 /* address of SIE FIFO RAM */
#define USB_FIFO_CMD 0x202A /* SIE FIFO RAM access command */
#define USB_FIFO_DATA 0x2030 /* SIE FIFO RAM data */
/* Endpoint Registers */
#define EP0_SETUPA 0x20F8 /* EP 0 setup packet lower byte */
#define EP0_SETUPB 0x20FC /* EP 0 setup packet higher byte */
#define USB_EP0_CFG 0x2104 /* EP 0 configure */
#define USB_EP0_CTL 0x2108 /* EP 0 control */
#define USB_EP0_STAT 0x210C /* EP 0 status */
#define USB_EP0_IRQSTAT 0x2110 /* EP 0 interrupt status */
#define USB_EP0_IRQEN 0x2114 /* EP 0 interrupt enable */
#define USB_EP0_MAXPKT 0x2118 /* EP 0 max packet size */
#define USB_EP0_BC 0x2120 /* EP 0 FIFO byte counter */
#define USB_EPA_CFG 0x2144 /* EP A configure */
#define USB_EPA_CFG_0 0x2144 /* EP A configure */
#define USB_EPA_CFG_1 0x2145 /* EP A configure */
#define USB_EPA_CFG_2 0x2146 /* EP A configure */
#define USB_EPA_CFG_3 0x2147 /* EP A configure */
#define USB_EPA_CTL 0x2148 /* EP A control */
#define USB_EPA_CTL_0 0x2148 /* EP A control */
#define USB_EPA_CTL_1 0x2149 /* EP A control */
#define USB_EPA_CTL_2 0x214A /* EP A control */
#define USB_EPA_CTL_3 0x214B /* EP A control */
#define USB_EPA_STAT 0x214C /* EP A status */
#define USB_EPA_IRQSTAT 0x2150 /* EP A interrupt status */
#define USB_EPA_IRQEN 0x2154 /* EP A interrupt enable */
#define USB_EPA_MAXPKT 0x2158 /* EP A max packet size */
#define USB_EPA_MAXPKT_0 0x2158 /* EP A max packet size */
#define USB_EPA_MAXPKT_1 0x2159 /* EP A max packet size */
#define USB_EPA_MAXPKT_2 0x215A /* EP A max packet size */
#define USB_EPA_MAXPKT_3 0x215B /* EP A max packet size */
#define USB_EPA_FIFO_CFG 0x2160 /* EP A FIFO configure */
#define USB_EPA_FIFO_CFG_0 0x2160 /* EP A FIFO configure */
#define USB_EPA_FIFO_CFG_1 0x2161 /* EP A FIFO configure */
#define USB_EPA_FIFO_CFG_2 0x2162 /* EP A FIFO configure */
#define USB_EPA_FIFO_CFG_3 0x2163 /* EP A FIFO configure */
/* Debug Registers */
#define USB_PHYTSTDIS 0x2F04 /* PHY test disable */
#define USB_TOUT_VAL 0x2F08 /* USB time-out time */
#define USB_VDRCTRL 0x2F10 /* UTMI vendor signal control */
#define USB_VSTAIN 0x2F14 /* UTMI vendor signal status in */
#define USB_VLOADM 0x2F18 /* UTMI load vendor signal status in */
#define USB_VSTAOUT 0x2F1C /* UTMI vendor signal status out */
#define USB_UTMI_TST 0x2F80 /* UTMI test */
#define USB_UTMI_STATUS 0x2F84 /* UTMI status */
#define USB_TSTCTL 0x2F88 /* test control */
#define USB_TSTCTL2 0x2F8C /* test control 2 */
#define USB_PID_FORCE 0x2F90 /* force PID */
#define USB_PKTERR_CNT 0x2F94 /* packet error counter */
#define USB_RXERR_CNT 0x2F98 /* RX error counter */
#define USB_MEM_BIST 0x2F9C /* MEM BIST test */
#define USB_SLBBIST 0x2FA0 /* self-loop-back BIST */
#define USB_CNTTEST 0x2FA4 /* counter test */
#define USB_PHYTST 0x2FC0 /* USB PHY test */
#define USB_DBGIDX 0x2FF0 /* select individual block debug signal */
#define USB_DBGMUX 0x2FF4 /* debug signal module mux */
/*
* SYS registers
*/
/* demod control registers */
#define SYS_SYS0 0x3000 /* include DEMOD_CTL, GPO, GPI, GPOE */
#define SYS_DEMOD_CTL 0x3000 /* control register for DVB-T demodulator */
/* GPIO registers */
#define SYS_GPIO_OUT_VAL 0x3001 /* output value of GPIO */
#define SYS_GPIO_IN_VAL 0x3002 /* input value of GPIO */
#define SYS_GPIO_OUT_EN 0x3003 /* output enable of GPIO */
#define SYS_SYS1 0x3004 /* include GPD, SYSINTE, SYSINTS, GP_CFG0 */
#define SYS_GPIO_DIR 0x3004 /* direction control for GPIO */
#define SYS_SYSINTE 0x3005 /* system interrupt enable */
#define SYS_SYSINTS 0x3006 /* system interrupt status */
#define SYS_GPIO_CFG0 0x3007 /* PAD configuration for GPIO0-GPIO3 */
#define SYS_SYS2 0x3008 /* include GP_CFG1 and 3 reserved bytes */
#define SYS_GPIO_CFG1 0x3008 /* PAD configuration for GPIO4 */
#define SYS_DEMOD_CTL1 0x300B
/* IrDA registers */
#define SYS_IRRC_PSR 0x3020 /* IR protocol selection */
#define SYS_IRRC_PER 0x3024 /* IR protocol extension */
#define SYS_IRRC_SF 0x3028 /* IR sampling frequency */
#define SYS_IRRC_DPIR 0x302C /* IR data package interval */
#define SYS_IRRC_CR 0x3030 /* IR control */
#define SYS_IRRC_RP 0x3034 /* IR read port */
#define SYS_IRRC_SR 0x3038 /* IR status */
/* I2C master registers */
#define SYS_I2CCR 0x3040 /* I2C clock */
#define SYS_I2CMCR 0x3044 /* I2C master control */
#define SYS_I2CMSTR 0x3048 /* I2C master SCL timing */
#define SYS_I2CMSR 0x304C /* I2C master status */
#define SYS_I2CMFR 0x3050 /* I2C master FIFO */
/*
* IR registers
*/
#define IR_RX_BUF 0xFC00
#define IR_RX_IE 0xFD00
#define IR_RX_IF 0xFD01
#define IR_RX_CTRL 0xFD02
#define IR_RX_CFG 0xFD03
#define IR_MAX_DURATION0 0xFD04
#define IR_MAX_DURATION1 0xFD05
#define IR_IDLE_LEN0 0xFD06
#define IR_IDLE_LEN1 0xFD07
#define IR_GLITCH_LEN 0xFD08
#define IR_RX_BUF_CTRL 0xFD09
#define IR_RX_BUF_DATA 0xFD0A
#define IR_RX_BC 0xFD0B
#define IR_RX_CLK 0xFD0C
#define IR_RX_C_COUNT_L 0xFD0D
#define IR_RX_C_COUNT_H 0xFD0E
#define IR_SUSPEND_CTRL 0xFD10
#define IR_ERR_TOL_CTRL 0xFD11
#define IR_UNIT_LEN 0xFD12
#define IR_ERR_TOL_LEN 0xFD13
#define IR_MAX_H_TOL_LEN 0xFD14
#define IR_MAX_L_TOL_LEN 0xFD15
#define IR_MASK_CTRL 0xFD16
#define IR_MASK_DATA 0xFD17
#define IR_RES_MASK_ADDR 0xFD18
#define IR_RES_MASK_T_LEN 0xFD19
#endif

View file

@ -0,0 +1,362 @@
/* usb-urb.c is part of the DVB USB library.
*
* Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
* see dvb-usb-init.c for copyright information.
*
* This file keeps functions for initializing and handling the
* BULK and ISOC USB data transfers in a generic way.
* Can be used for DVB-only and also, that's the plan, for
* Hybrid USB devices (analog and DVB).
*/
#include "dvb_usb_common.h"
/* URB stuff for streaming */
int usb_urb_reconfig(struct usb_data_stream *stream,
struct usb_data_stream_properties *props);
static void usb_urb_complete(struct urb *urb)
{
struct usb_data_stream *stream = urb->context;
int ptype = usb_pipetype(urb->pipe);
int i;
u8 *b;
dev_dbg_ratelimited(&stream->udev->dev,
"%s: %s urb completed status=%d length=%d/%d pack_num=%d errors=%d\n",
__func__, ptype == PIPE_ISOCHRONOUS ? "isoc" : "bulk",
urb->status, urb->actual_length,
urb->transfer_buffer_length,
urb->number_of_packets, urb->error_count);
switch (urb->status) {
case 0: /* success */
case -ETIMEDOUT: /* NAK */
break;
case -ECONNRESET: /* kill */
case -ENOENT:
case -ESHUTDOWN:
return;
default: /* error */
dev_dbg_ratelimited(&stream->udev->dev,
"%s: urb completition failed=%d\n",
__func__, urb->status);
break;
}
b = (u8 *) urb->transfer_buffer;
switch (ptype) {
case PIPE_ISOCHRONOUS:
for (i = 0; i < urb->number_of_packets; i++) {
if (urb->iso_frame_desc[i].status != 0)
dev_dbg(&stream->udev->dev,
"%s: iso frame descriptor has an error=%d\n",
__func__,
urb->iso_frame_desc[i].status);
else if (urb->iso_frame_desc[i].actual_length > 0)
stream->complete(stream,
b + urb->iso_frame_desc[i].offset,
urb->iso_frame_desc[i].actual_length);
urb->iso_frame_desc[i].status = 0;
urb->iso_frame_desc[i].actual_length = 0;
}
break;
case PIPE_BULK:
if (urb->actual_length > 0)
stream->complete(stream, b, urb->actual_length);
break;
default:
dev_err(&stream->udev->dev,
"%s: unknown endpoint type in completition handler\n",
KBUILD_MODNAME);
return;
}
usb_submit_urb(urb, GFP_ATOMIC);
}
int usb_urb_killv2(struct usb_data_stream *stream)
{
int i;
for (i = 0; i < stream->urbs_submitted; i++) {
dev_dbg(&stream->udev->dev, "%s: kill urb=%d\n", __func__, i);
/* stop the URB */
usb_kill_urb(stream->urb_list[i]);
}
stream->urbs_submitted = 0;
return 0;
}
int usb_urb_submitv2(struct usb_data_stream *stream,
struct usb_data_stream_properties *props)
{
int i, ret;
if (props) {
ret = usb_urb_reconfig(stream, props);
if (ret < 0)
return ret;
}
for (i = 0; i < stream->urbs_initialized; i++) {
dev_dbg(&stream->udev->dev, "%s: submit urb=%d\n", __func__, i);
ret = usb_submit_urb(stream->urb_list[i], GFP_ATOMIC);
if (ret) {
dev_err(&stream->udev->dev,
"%s: could not submit urb no. %d - get them all back\n",
KBUILD_MODNAME, i);
usb_urb_killv2(stream);
return ret;
}
stream->urbs_submitted++;
}
return 0;
}
static int usb_urb_free_urbs(struct usb_data_stream *stream)
{
int i;
usb_urb_killv2(stream);
for (i = stream->urbs_initialized - 1; i >= 0; i--) {
if (stream->urb_list[i]) {
dev_dbg(&stream->udev->dev, "%s: free urb=%d\n",
__func__, i);
/* free the URBs */
usb_free_urb(stream->urb_list[i]);
}
}
stream->urbs_initialized = 0;
return 0;
}
static int usb_urb_alloc_bulk_urbs(struct usb_data_stream *stream)
{
int i, j;
/* allocate the URBs */
for (i = 0; i < stream->props.count; i++) {
dev_dbg(&stream->udev->dev, "%s: alloc urb=%d\n", __func__, i);
stream->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
if (!stream->urb_list[i]) {
dev_dbg(&stream->udev->dev, "%s: failed\n", __func__);
for (j = 0; j < i; j++)
usb_free_urb(stream->urb_list[j]);
return -ENOMEM;
}
usb_fill_bulk_urb(stream->urb_list[i],
stream->udev,
usb_rcvbulkpipe(stream->udev,
stream->props.endpoint),
stream->buf_list[i],
stream->props.u.bulk.buffersize,
usb_urb_complete, stream);
stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
stream->urb_list[i]->transfer_dma = stream->dma_addr[i];
stream->urbs_initialized++;
}
return 0;
}
static int usb_urb_alloc_isoc_urbs(struct usb_data_stream *stream)
{
int i, j;
/* allocate the URBs */
for (i = 0; i < stream->props.count; i++) {
struct urb *urb;
int frame_offset = 0;
dev_dbg(&stream->udev->dev, "%s: alloc urb=%d\n", __func__, i);
stream->urb_list[i] = usb_alloc_urb(
stream->props.u.isoc.framesperurb, GFP_ATOMIC);
if (!stream->urb_list[i]) {
dev_dbg(&stream->udev->dev, "%s: failed\n", __func__);
for (j = 0; j < i; j++)
usb_free_urb(stream->urb_list[j]);
return -ENOMEM;
}
urb = stream->urb_list[i];
urb->dev = stream->udev;
urb->context = stream;
urb->complete = usb_urb_complete;
urb->pipe = usb_rcvisocpipe(stream->udev,
stream->props.endpoint);
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
urb->interval = stream->props.u.isoc.interval;
urb->number_of_packets = stream->props.u.isoc.framesperurb;
urb->transfer_buffer_length = stream->props.u.isoc.framesize *
stream->props.u.isoc.framesperurb;
urb->transfer_buffer = stream->buf_list[i];
urb->transfer_dma = stream->dma_addr[i];
for (j = 0; j < stream->props.u.isoc.framesperurb; j++) {
urb->iso_frame_desc[j].offset = frame_offset;
urb->iso_frame_desc[j].length =
stream->props.u.isoc.framesize;
frame_offset += stream->props.u.isoc.framesize;
}
stream->urbs_initialized++;
}
return 0;
}
static int usb_free_stream_buffers(struct usb_data_stream *stream)
{
if (stream->state & USB_STATE_URB_BUF) {
while (stream->buf_num) {
stream->buf_num--;
dev_dbg(&stream->udev->dev, "%s: free buf=%d\n",
__func__, stream->buf_num);
usb_free_coherent(stream->udev, stream->buf_size,
stream->buf_list[stream->buf_num],
stream->dma_addr[stream->buf_num]);
}
}
stream->state &= ~USB_STATE_URB_BUF;
return 0;
}
static int usb_alloc_stream_buffers(struct usb_data_stream *stream, int num,
unsigned long size)
{
stream->buf_num = 0;
stream->buf_size = size;
dev_dbg(&stream->udev->dev,
"%s: all in all I will use %lu bytes for streaming\n",
__func__, num * size);
for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) {
stream->buf_list[stream->buf_num] = usb_alloc_coherent(
stream->udev, size, GFP_ATOMIC,
&stream->dma_addr[stream->buf_num]);
if (!stream->buf_list[stream->buf_num]) {
dev_dbg(&stream->udev->dev, "%s: alloc buf=%d failed\n",
__func__, stream->buf_num);
usb_free_stream_buffers(stream);
return -ENOMEM;
}
dev_dbg(&stream->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n",
__func__, stream->buf_num,
stream->buf_list[stream->buf_num],
(long long)stream->dma_addr[stream->buf_num]);
memset(stream->buf_list[stream->buf_num], 0, size);
stream->state |= USB_STATE_URB_BUF;
}
return 0;
}
int usb_urb_reconfig(struct usb_data_stream *stream,
struct usb_data_stream_properties *props)
{
int buf_size;
if (!props)
return 0;
/* check allocated buffers are large enough for the request */
if (props->type == USB_BULK) {
buf_size = stream->props.u.bulk.buffersize;
} else if (props->type == USB_ISOC) {
buf_size = props->u.isoc.framesize * props->u.isoc.framesperurb;
} else {
dev_err(&stream->udev->dev, "%s: invalid endpoint type=%d\n",
KBUILD_MODNAME, props->type);
return -EINVAL;
}
if (stream->buf_num < props->count || stream->buf_size < buf_size) {
dev_err(&stream->udev->dev,
"%s: cannot reconfigure as allocated buffers are too small\n",
KBUILD_MODNAME);
return -EINVAL;
}
/* check if all fields are same */
if (stream->props.type == props->type &&
stream->props.count == props->count &&
stream->props.endpoint == props->endpoint) {
if (props->type == USB_BULK &&
props->u.bulk.buffersize ==
stream->props.u.bulk.buffersize)
return 0;
else if (props->type == USB_ISOC &&
props->u.isoc.framesperurb ==
stream->props.u.isoc.framesperurb &&
props->u.isoc.framesize ==
stream->props.u.isoc.framesize &&
props->u.isoc.interval ==
stream->props.u.isoc.interval)
return 0;
}
dev_dbg(&stream->udev->dev, "%s: re-alloc urbs\n", __func__);
usb_urb_free_urbs(stream);
memcpy(&stream->props, props, sizeof(*props));
if (props->type == USB_BULK)
return usb_urb_alloc_bulk_urbs(stream);
else if (props->type == USB_ISOC)
return usb_urb_alloc_isoc_urbs(stream);
return 0;
}
int usb_urb_initv2(struct usb_data_stream *stream,
const struct usb_data_stream_properties *props)
{
int ret;
if (!stream || !props)
return -EINVAL;
memcpy(&stream->props, props, sizeof(*props));
if (!stream->complete) {
dev_err(&stream->udev->dev,
"%s: there is no data callback - this doesn't make sense\n",
KBUILD_MODNAME);
return -EINVAL;
}
switch (stream->props.type) {
case USB_BULK:
ret = usb_alloc_stream_buffers(stream, stream->props.count,
stream->props.u.bulk.buffersize);
if (ret < 0)
return ret;
return usb_urb_alloc_bulk_urbs(stream);
case USB_ISOC:
ret = usb_alloc_stream_buffers(stream, stream->props.count,
stream->props.u.isoc.framesize *
stream->props.u.isoc.framesperurb);
if (ret < 0)
return ret;
return usb_urb_alloc_isoc_urbs(stream);
default:
dev_err(&stream->udev->dev,
"%s: unknown urb-type for data transfer\n",
KBUILD_MODNAME);
return -EINVAL;
}
}
int usb_urb_exitv2(struct usb_data_stream *stream)
{
usb_urb_free_urbs(stream);
usb_free_stream_buffers(stream);
return 0;
}

Some files were not shown because too many files have changed in this diff Show more