add LGDT3303 tuner and XC3028L demod drivers
This commit is contained in:
parent
730b47d6fe
commit
9c53d3c419
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: files.i2c,v 1.36 2011/07/11 00:30:23 jakllsch Exp $
|
||||
# $NetBSD: files.i2c,v 1.37 2011/07/11 18:00:06 jmcneill Exp $
|
||||
|
||||
defflag opt_i2cbus.h I2C_SCAN
|
||||
define i2cbus { }
|
||||
|
@ -17,8 +17,16 @@ file dev/i2c/i2c_bitbang.c i2c_bitbang
|
|||
define au8522: i2cexec
|
||||
file dev/i2c/au8522.c au8522
|
||||
|
||||
# LG DT3303 decoder
|
||||
define lg3303: i2cexec
|
||||
file dev/i2c/lg3303.c lg3303
|
||||
|
||||
# Xceive XC3028 tuner
|
||||
define xc3028: i2cexec, firmload
|
||||
file dev/i2c/xc3028.c xc3028
|
||||
|
||||
# Xceive XC5000 tuner
|
||||
define xc5k: i2cexec
|
||||
define xc5k: i2cexec, firmload
|
||||
file dev/i2c/xc5k.c xc5k
|
||||
|
||||
# Generic PLL-based tuners
|
||||
|
|
|
@ -0,0 +1,359 @@
|
|||
/* $NetBSD: lg3303.c,v 1.1 2011/07/11 18:00:06 jmcneill Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright 2007 Jason Harmening
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: lg3303.c,v 1.1 2011/07/11 18:00:06 jmcneill Exp $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/module.h>
|
||||
|
||||
#include <dev/i2c/i2cvar.h>
|
||||
#include <dev/i2c/lg3303var.h>
|
||||
#include <dev/dtv/dtvif.h>
|
||||
|
||||
#define REG_TOP_CONTROL 0x00
|
||||
#define REG_IRQ_MASK 0x01
|
||||
#define REG_IRQ_STATUS 0x02
|
||||
#define REG_VSB_CARRIER_FREQ0 0x16
|
||||
#define REG_VSB_CARRIER_FREQ1 0x17
|
||||
#define REG_VSB_CARRIER_FREQ2 0x18
|
||||
#define REG_VSB_CARRIER_FREQ3 0x19
|
||||
#define REG_CARRIER_MSEQAM1 0x1a
|
||||
#define REG_CARRIER_MSEQAM2 0x1b
|
||||
#define REG_CARRIER_LOCK 0x1c
|
||||
#define REG_TIMING_RECOVERY 0x1d
|
||||
#define REG_AGC_DELAY0 0x2a
|
||||
#define REG_AGC_DELAY1 0x2b
|
||||
#define REG_AGC_DELAY2 0x2c
|
||||
#define REG_AGC_RF_BANDWIDTH0 0x2d
|
||||
#define REG_AGC_RF_BANDWIDTH1 0x2e
|
||||
#define REG_AGC_RF_BANDWIDTH2 0x2f
|
||||
#define REG_AGC_LOOP_BANDWIDTH0 0x30
|
||||
#define REG_AGC_LOOP_BANDWIDTH1 0x31
|
||||
#define REG_AGC_FUNC_CTRL1 0x32
|
||||
#define REG_AGC_FUNC_CTRL2 0x33
|
||||
#define REG_AGC_FUNC_CTRL3 0x34
|
||||
#define REG_AGC_RFIF_ACC0 0x39
|
||||
#define REG_AGC_RFIF_ACC1 0x3a
|
||||
#define REG_AGC_RFIF_ACC2 0x3b
|
||||
#define REG_AGC_STATUS 0x3f
|
||||
#define REG_SYNC_STATUS_VSB 0x43
|
||||
#define REG_DEMUX_CONTROL 0x66
|
||||
#define REG_EQPH_ERR0 0x6e
|
||||
#define REG_EQ_ERR1 0x6f
|
||||
#define REG_EQ_ERR2 0x70
|
||||
#define REG_PH_ERR1 0x71
|
||||
#define REG_PH_ERR2 0x72
|
||||
#define REG_PACKET_ERR_COUNTER1 0x8b
|
||||
#define REG_PACKET_ERR_COUNTER2 0x8c
|
||||
|
||||
#define LG3303_DEFAULT_DELAY 250000
|
||||
|
||||
static int lg3303_reset(struct lg3303 *);
|
||||
static int lg3303_init(struct lg3303 *);
|
||||
|
||||
struct lg3303 *
|
||||
lg3303_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr)
|
||||
{
|
||||
struct lg3303 *lg;
|
||||
|
||||
lg = kmem_alloc(sizeof(*lg), KM_SLEEP);
|
||||
if (lg == NULL)
|
||||
return NULL;
|
||||
lg->parent = parent;
|
||||
lg->i2c = i2c;
|
||||
lg->i2c_addr = addr;
|
||||
lg->current_modulation = -1;
|
||||
|
||||
if (lg3303_init(lg) != 0) {
|
||||
kmem_free(lg, sizeof(*lg));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device_printf(lg->parent, "lg3303: found @ 0x%02x\n", addr);
|
||||
|
||||
return lg;
|
||||
}
|
||||
|
||||
void
|
||||
lg3303_close(struct lg3303 *lg)
|
||||
{
|
||||
kmem_free(lg, sizeof(*lg));
|
||||
}
|
||||
|
||||
static int
|
||||
lg3303_write(struct lg3303 *lg, uint8_t *buf, size_t len)
|
||||
{
|
||||
unsigned int i;
|
||||
uint8_t *p = buf;
|
||||
int error;
|
||||
|
||||
for (i = 0; i < len - 1; i += 2) {
|
||||
error = iic_exec(lg->i2c, I2C_OP_WRITE_WITH_STOP, lg->i2c_addr,
|
||||
p, 2, NULL, 0, 0);
|
||||
if (error)
|
||||
return error;
|
||||
p += 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lg3303_read(struct lg3303 *lg, uint8_t reg, uint8_t *buf, size_t len)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = iic_exec(lg->i2c, I2C_OP_WRITE, lg->i2c_addr,
|
||||
®, sizeof(reg), NULL, 0, 0);
|
||||
if (error)
|
||||
return error;
|
||||
return iic_exec(lg->i2c, I2C_OP_READ, lg->i2c_addr,
|
||||
NULL, 0, buf, len, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
lg3303_reset(struct lg3303 *lg)
|
||||
{
|
||||
uint8_t buffer[] = {REG_IRQ_STATUS, 0x00};
|
||||
int error = lg3303_write(lg, buffer, 2);
|
||||
if (error == 0) {
|
||||
buffer[1] = 0x01;
|
||||
error = lg3303_write(lg, buffer, 2);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
lg3303_init(struct lg3303 *lg)
|
||||
{
|
||||
//static uint8_t init_data[] = {0x4c, 0x14, 0x87, 0xf3};
|
||||
static uint8_t init_data[] = {0x4c, 0x14};
|
||||
size_t len;
|
||||
int error;
|
||||
|
||||
#if notyet
|
||||
if (clock_polarity == DVB_IFC_POS_POL)
|
||||
len = 4;
|
||||
else
|
||||
#endif
|
||||
len = 2;
|
||||
|
||||
error = lg3303_write(lg, init_data, len);
|
||||
if (error == 0)
|
||||
lg3303_reset(lg);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
lg3303_set_modulation(struct lg3303 *lg, fe_modulation_t modulation)
|
||||
{
|
||||
int error;
|
||||
static uint8_t vsb_data[] = {
|
||||
0x04, 0x00,
|
||||
0x0d, 0x40,
|
||||
0x0e, 0x87,
|
||||
0x0f, 0x8e,
|
||||
0x10, 0x01,
|
||||
0x47, 0x8b
|
||||
};
|
||||
static uint8_t qam_data[] = {
|
||||
0x04, 0x00,
|
||||
0x0d, 0x00,
|
||||
0x0e, 0x00,
|
||||
0x0f, 0x00,
|
||||
0x10, 0x00,
|
||||
0x51, 0x63,
|
||||
0x47, 0x66,
|
||||
0x48, 0x66,
|
||||
0x4d, 0x1a,
|
||||
0x49, 0x08,
|
||||
0x4a, 0x9b
|
||||
};
|
||||
|
||||
error = lg3303_reset(lg);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (lg->current_modulation != modulation) {
|
||||
uint8_t top_ctrl[] = {REG_TOP_CONTROL, 0x00};
|
||||
#if 0
|
||||
if (m_input == DVB_INPUT_SERIAL)
|
||||
top_ctrl[1] = 0x40;
|
||||
#endif
|
||||
|
||||
switch (modulation) {
|
||||
case VSB_8:
|
||||
top_ctrl[1] |= 0x03;
|
||||
error = lg3303_write(lg, vsb_data, sizeof(vsb_data));
|
||||
if (error)
|
||||
return error;
|
||||
break;
|
||||
case QAM_256:
|
||||
top_ctrl[1] |= 0x01;
|
||||
/* FALLTHROUGH */
|
||||
case QAM_64:
|
||||
error = lg3303_write(lg, qam_data, sizeof(qam_data));
|
||||
if (error)
|
||||
return error;
|
||||
break;
|
||||
default:
|
||||
device_printf(lg->parent,
|
||||
"lg3303: unsupported modulation type (%d)\n",
|
||||
modulation);
|
||||
return EINVAL;
|
||||
}
|
||||
error = lg3303_write(lg, top_ctrl, sizeof(top_ctrl));
|
||||
if (error)
|
||||
return error;
|
||||
lg->current_modulation = modulation;
|
||||
lg3303_reset(lg);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
fe_status_t
|
||||
lg3303_get_dtv_status(struct lg3303 *lg)
|
||||
{
|
||||
uint8_t reg = 0, value = 0x00;
|
||||
fe_status_t festatus = 0;
|
||||
int error = 0;
|
||||
|
||||
error = lg3303_read(lg, 0x58, &value, sizeof(value));
|
||||
if (error)
|
||||
return 0;
|
||||
|
||||
if (value & 0x01)
|
||||
festatus |= FE_HAS_SIGNAL;
|
||||
|
||||
error = lg3303_read(lg, REG_CARRIER_LOCK, &value, sizeof(value));
|
||||
if (error)
|
||||
return 0;
|
||||
|
||||
switch (lg->current_modulation) {
|
||||
case VSB_8:
|
||||
if (value & 0x80)
|
||||
festatus |= FE_HAS_CARRIER;
|
||||
reg = 0x38;
|
||||
break;
|
||||
case QAM_64:
|
||||
case QAM_256:
|
||||
if ((value & 0x07) == 0x07)
|
||||
festatus |= FE_HAS_CARRIER;
|
||||
reg = 0x8a;
|
||||
break;
|
||||
default:
|
||||
device_printf(lg->parent,
|
||||
"lg3303: unsupported modulation type (%d)\n",
|
||||
lg->current_modulation);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
error = lg3303_read(lg, reg, &value, sizeof(value));
|
||||
if (!error && (value & 0x01))
|
||||
festatus |= FE_HAS_LOCK;
|
||||
|
||||
if (festatus & FE_HAS_LOCK)
|
||||
festatus |= (FE_HAS_SYNC | FE_HAS_VITERBI);
|
||||
|
||||
return festatus;
|
||||
}
|
||||
|
||||
#if notyet
|
||||
int lg3303::get_signal(dvb_signal &signal)
|
||||
{
|
||||
int error = check_for_lock(signal.locked);
|
||||
uint32_t noise, snr_const;
|
||||
uint8_t buffer[5];
|
||||
uint8_t reg;
|
||||
if (error || !signal.locked)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
signal.ber = 0;
|
||||
switch(m_modulation)
|
||||
{
|
||||
case DVB_MOD_VSB_8:
|
||||
reg = REG_EQPH_ERR0;
|
||||
if ((error = m_device.transact(®, sizeof(reg), buffer, sizeof(buffer))))
|
||||
{
|
||||
LIBTUNERERR << "LG3303: Unable to retrieve 8-VSB noise value" << endl;
|
||||
return error;
|
||||
}
|
||||
noise = ((buffer[0] & 7) << 16) | (buffer[3] << 8) | buffer[4];
|
||||
snr_const = 25600;
|
||||
break;
|
||||
case DVB_MOD_QAM_64:
|
||||
case DVB_MOD_QAM_256:
|
||||
reg = REG_CARRIER_MSEQAM1;
|
||||
if ((error = m_device.transact(®, sizeof(reg), buffer, 2)))
|
||||
{
|
||||
LIBTUNERERR << "LG3303: Unable to retrieve QAM noise value" << endl;
|
||||
return error;
|
||||
}
|
||||
noise = (buffer[0] << 8) | buffer[1];
|
||||
if (m_modulation == DVB_MOD_QAM_64)
|
||||
{
|
||||
snr_const = 688128;
|
||||
}
|
||||
else
|
||||
{
|
||||
snr_const = 696320;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LIBTUNERERR << "LG3303: Unsupported modulation type" << endl;
|
||||
return EINVAL;
|
||||
}
|
||||
signal.snr = 10.0 * log10((double)snr_const / noise);
|
||||
signal.strength = (signal.snr / 35) * 100;
|
||||
reg = REG_PACKET_ERR_COUNTER1;
|
||||
if ((error = m_device.transact(®, sizeof(reg), buffer, 2)))
|
||||
{
|
||||
LIBTUNERERR << "LG3303: Unable to retrieve packet error count" << endl;
|
||||
return error;
|
||||
}
|
||||
signal.uncorrected_blocks = (buffer[0] << 8) | buffer[1];
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
MODULE(MODULE_CLASS_DRIVER, lg3303, NULL);
|
||||
|
||||
static int
|
||||
lg3303_modcmd(modcmd_t cmd, void *opaque)
|
||||
{
|
||||
if (cmd == MODULE_CMD_INIT || cmd == MODULE_CMD_FINI)
|
||||
return 0;
|
||||
return ENOTTY;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/* $NetBSD: lg3303var.h,v 1.1 2011/07/11 18:00:06 jmcneill Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2011 Jared D. McNeill <jmcneill@invisible.ca>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _LG3303VAR_H
|
||||
#define _LG3303VAR_H
|
||||
|
||||
#include <dev/i2c/i2cvar.h>
|
||||
#include <dev/dtv/dtvio.h>
|
||||
|
||||
struct lg3303 {
|
||||
device_t parent;
|
||||
i2c_tag_t i2c;
|
||||
i2c_addr_t i2c_addr;
|
||||
|
||||
fe_modulation_t current_modulation;
|
||||
};
|
||||
|
||||
struct lg3303 * lg3303_open(device_t, i2c_tag_t, i2c_addr_t);
|
||||
void lg3303_close(struct lg3303 *);
|
||||
int lg3303_set_modulation(struct lg3303 *, fe_modulation_t);
|
||||
fe_status_t lg3303_get_dtv_status(struct lg3303 *);
|
||||
|
||||
#endif /* !_LG3303VAR_H */
|
|
@ -0,0 +1,582 @@
|
|||
/* $NetBSD: xc3028.c,v 1.1 2011/07/11 18:00:06 jmcneill Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2011 Jared D. McNeill <jmcneill@invisible.ca>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Xceive XC3028L
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: xc3028.c,v 1.1 2011/07/11 18:00:06 jmcneill Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/module.h>
|
||||
|
||||
#include <dev/firmload.h>
|
||||
#include <dev/i2c/i2cvar.h>
|
||||
|
||||
#include <dev/i2c/xc3028reg.h>
|
||||
#include <dev/i2c/xc3028var.h>
|
||||
|
||||
#define XC3028_FIRMWARE_DRVNAME "xc3028"
|
||||
|
||||
#define XC3028_FREQ_MIN 1000000
|
||||
#define XC3028_FREQ_MAX 1023000000
|
||||
|
||||
#define XC3028_FW_BASE (1 << 0)
|
||||
#define XC3028_FW_D2633 (1 << 4)
|
||||
#define XC3028_FW_DTV6 (1 << 5)
|
||||
#define XC3028_FW_QAM (1 << 6)
|
||||
#define XC3028_FW_ATSC (1 << 16)
|
||||
#define XC3028_FW_LG60 (1 << 18)
|
||||
#define XC3028_FW_F6MHZ (1 << 27)
|
||||
#define XC3028_FW_SCODE (1 << 29)
|
||||
#define XC3028_FW_HAS_IF (1 << 30)
|
||||
|
||||
#define XC3028_FW_DEFAULT (XC3028_FW_ATSC|XC3028_FW_D2633|XC3028_FW_DTV6)
|
||||
|
||||
static kmutex_t xc3028_firmware_lock;
|
||||
|
||||
static int xc3028_reset(struct xc3028 *);
|
||||
static int xc3028_read_2(struct xc3028 *, uint16_t, uint16_t *);
|
||||
static int xc3028_write_buffer(struct xc3028 *, const uint8_t *, size_t);
|
||||
static int xc3028_firmware_open(struct xc3028 *);
|
||||
static int xc3028_firmware_parse(struct xc3028 *, const uint8_t *, size_t);
|
||||
static int xc3028_firmware_upload(struct xc3028 *, struct xc3028_fw *);
|
||||
static int xc3028_scode_upload(struct xc3028 *, struct xc3028_fw *);
|
||||
static void xc3028_dump_fw(struct xc3028 *, struct xc3028_fw *,
|
||||
const char *);
|
||||
|
||||
static const char *
|
||||
xc3028_name(struct xc3028 *xc)
|
||||
{
|
||||
if (xc->type == XC3028L)
|
||||
return "xc3028l";
|
||||
else
|
||||
return "xc3028";
|
||||
}
|
||||
|
||||
static const char *
|
||||
xc3028_firmware_name(struct xc3028 *xc)
|
||||
{
|
||||
if (xc->type == XC3028L)
|
||||
return "xc3028L-v36.fw";
|
||||
else
|
||||
return "xc3028-v27.fw";
|
||||
}
|
||||
|
||||
static int
|
||||
xc3028_reset(struct xc3028 *xc)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (xc->reset)
|
||||
error = xc->reset(xc->reset_priv);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static struct xc3028_fw *
|
||||
xc3028_get_basefw(struct xc3028 *xc)
|
||||
{
|
||||
struct xc3028_fw *fw;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < xc->nfw; i++) {
|
||||
fw = &xc->fw[i];
|
||||
if (fw->type == XC3028_FW_BASE)
|
||||
return fw;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct xc3028_fw *
|
||||
xc3028_get_stdfw(struct xc3028 *xc)
|
||||
{
|
||||
struct xc3028_fw *fw;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < xc->nfw; i++) {
|
||||
fw = &xc->fw[i];
|
||||
if (fw->type == (XC3028_FW_D2633|XC3028_FW_DTV6|XC3028_FW_ATSC))
|
||||
return fw;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct xc3028_fw *
|
||||
xc3028_get_scode(struct xc3028 *xc)
|
||||
{
|
||||
struct xc3028_fw *fw;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < xc->nfw; i++) {
|
||||
fw = &xc->fw[i];
|
||||
if (fw->type ==
|
||||
(XC3028_FW_DTV6|XC3028_FW_QAM|XC3028_FW_ATSC|XC3028_FW_LG60|
|
||||
XC3028_FW_F6MHZ|XC3028_FW_SCODE|XC3028_FW_HAS_IF) &&
|
||||
fw->int_freq == 6200)
|
||||
return fw;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
xc3028_firmware_open(struct xc3028 *xc)
|
||||
{
|
||||
firmware_handle_t fwh;
|
||||
struct xc3028_fw *basefw, *stdfw, *scode;
|
||||
uint8_t *fw = NULL;
|
||||
uint16_t xcversion = 0;
|
||||
size_t fwlen;
|
||||
int error;
|
||||
|
||||
mutex_enter(&xc3028_firmware_lock);
|
||||
|
||||
error = firmware_open(XC3028_FIRMWARE_DRVNAME,
|
||||
xc3028_firmware_name(xc), &fwh);
|
||||
if (error)
|
||||
goto done;
|
||||
fwlen = firmware_get_size(fwh);
|
||||
fw = firmware_malloc(fwlen);
|
||||
if (fw == NULL) {
|
||||
firmware_close(fwh);
|
||||
error = ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
error = firmware_read(fwh, 0, fw, fwlen);
|
||||
firmware_close(fwh);
|
||||
if (error)
|
||||
goto done;
|
||||
|
||||
device_printf(xc->parent, "%s: loading firmware '%s/%s'\n",
|
||||
xc3028_name(xc), XC3028_FIRMWARE_DRVNAME, xc3028_firmware_name(xc));
|
||||
error = xc3028_firmware_parse(xc, fw, fwlen);
|
||||
if (!error) {
|
||||
basefw = xc3028_get_basefw(xc);
|
||||
stdfw = xc3028_get_stdfw(xc);
|
||||
scode = xc3028_get_scode(xc);
|
||||
if (basefw && stdfw) {
|
||||
xc3028_reset(xc);
|
||||
xc3028_dump_fw(xc, basefw, "base");
|
||||
error = xc3028_firmware_upload(xc, basefw);
|
||||
if (error)
|
||||
return error;
|
||||
xc3028_dump_fw(xc, stdfw, "std");
|
||||
error = xc3028_firmware_upload(xc, stdfw);
|
||||
if (error)
|
||||
return error;
|
||||
if (scode) {
|
||||
xc3028_dump_fw(xc, scode, "scode");
|
||||
error = xc3028_scode_upload(xc, scode);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
} else
|
||||
error = ENODEV;
|
||||
}
|
||||
if (!error) {
|
||||
xc3028_read_2(xc, XC3028_REG_VERSION, &xcversion);
|
||||
|
||||
device_printf(xc->parent, "%s: hw %d.%d, fw %d.%d\n",
|
||||
xc3028_name(xc),
|
||||
(xcversion >> 12) & 0xf, (xcversion >> 8) & 0xf,
|
||||
(xcversion >> 4) & 0xf, (xcversion >> 0) & 0xf);
|
||||
}
|
||||
|
||||
done:
|
||||
if (fw)
|
||||
firmware_free(fw, 0);
|
||||
mutex_exit(&xc3028_firmware_lock);
|
||||
|
||||
if (error)
|
||||
aprint_error_dev(xc->parent,
|
||||
"%s: couldn't open firmware '%s/%s' (error=%d)\n",
|
||||
xc3028_name(xc), XC3028_FIRMWARE_DRVNAME,
|
||||
xc3028_firmware_name(xc), error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static const char *xc3028_fw_types[] = {
|
||||
"BASE",
|
||||
"F8MHZ",
|
||||
"MTS",
|
||||
"D2620",
|
||||
"D2633",
|
||||
"DTV6",
|
||||
"QAM",
|
||||
"DTV7",
|
||||
"DTV78",
|
||||
"DTV8",
|
||||
"FM",
|
||||
"INPUT1",
|
||||
"LCD",
|
||||
"NOGD",
|
||||
"INIT1",
|
||||
"MONO",
|
||||
"ATSC",
|
||||
"IF",
|
||||
"LG60",
|
||||
"ATI638",
|
||||
"OREN538",
|
||||
"OREN36",
|
||||
"TOYOTA388",
|
||||
"TOYOTA794",
|
||||
"DIBCOM52",
|
||||
"ZARLINK456",
|
||||
"CHINA",
|
||||
"F6MHZ",
|
||||
"INPUT2",
|
||||
"SCODE",
|
||||
"HAS_IF",
|
||||
};
|
||||
|
||||
static void
|
||||
xc3028_dump_fw(struct xc3028 *xc, struct xc3028_fw *xcfw, const char *type)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
device_printf(xc->parent, "%s: %s:", xc3028_name(xc), type);
|
||||
if (xcfw == NULL) {
|
||||
printf(" <none>\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < __arraycount(xc3028_fw_types); i++) {
|
||||
if (xcfw->type & (1 << i))
|
||||
printf(" %s", xc3028_fw_types[i]);
|
||||
}
|
||||
if (xcfw->type & (1 << 30))
|
||||
printf("_%d", xcfw->int_freq);
|
||||
if (xcfw->id)
|
||||
printf(" id=%llx", xcfw->id);
|
||||
printf(" size=%u\n", xcfw->data_size);
|
||||
}
|
||||
|
||||
static int
|
||||
xc3028_firmware_parse(struct xc3028 *xc, const uint8_t *fw, size_t fwlen)
|
||||
{
|
||||
const uint8_t *p = fw, *endp = p + fwlen;
|
||||
char fwname[32 + 1];
|
||||
uint16_t fwver, narr;
|
||||
unsigned int index;
|
||||
struct xc3028_fw *xcfw;
|
||||
|
||||
if (fwlen < 36)
|
||||
return EINVAL;
|
||||
|
||||
/* first 32 bytes are the firmware name string */
|
||||
memset(fwname, 0, sizeof(fwname));
|
||||
memcpy(fwname, p, sizeof(fwname) - 1);
|
||||
p += (sizeof(fwname) - 1);
|
||||
|
||||
fwver = le16dec(p);
|
||||
p += sizeof(fwver);
|
||||
narr = le16dec(p);
|
||||
p += sizeof(narr);
|
||||
|
||||
aprint_debug_dev(xc->parent, "%s: fw type %s, ver %d.%d, %d images\n",
|
||||
xc3028_name(xc), fwname, fwver >> 8, fwver & 0xff, narr);
|
||||
|
||||
xc->fw = kmem_zalloc(sizeof(*xc->fw) * narr, KM_SLEEP);
|
||||
if (xc->fw == NULL)
|
||||
return ENOMEM;
|
||||
xc->nfw = narr;
|
||||
|
||||
for (index = 0; index < xc->nfw && p < endp; index++) {
|
||||
xcfw = &xc->fw[index];
|
||||
|
||||
if (endp - p < 16)
|
||||
goto corrupt;
|
||||
|
||||
xcfw->type = le32dec(p);
|
||||
p += sizeof(xcfw->type);
|
||||
|
||||
xcfw->id = le64dec(p);
|
||||
p += sizeof(xcfw->id);
|
||||
|
||||
if (xcfw->type & XC3028_FW_HAS_IF) {
|
||||
xcfw->int_freq = le16dec(p);
|
||||
p += sizeof(xcfw->int_freq);
|
||||
if ((uint32_t)(endp - p) < sizeof(xcfw->data_size))
|
||||
goto corrupt;
|
||||
}
|
||||
|
||||
xcfw->data_size = le32dec(p);
|
||||
p += sizeof(xcfw->data_size);
|
||||
|
||||
if (xcfw->data_size == 0 ||
|
||||
xcfw->data_size > (uint32_t)(endp - p))
|
||||
goto corrupt;
|
||||
xcfw->data = kmem_alloc(xcfw->data_size, KM_SLEEP);
|
||||
if (xcfw->data == NULL)
|
||||
goto corrupt;
|
||||
memcpy(xcfw->data, p, xcfw->data_size);
|
||||
p += xcfw->data_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
corrupt:
|
||||
aprint_error_dev(xc->parent, "%s: fw image corrupt\n", xc3028_name(xc));
|
||||
for (index = 0; index < xc->nfw; index++) {
|
||||
if (xc->fw[index].data)
|
||||
kmem_free(xc->fw[index].data, xc->fw[index].data_size);
|
||||
}
|
||||
kmem_free(xc->fw, sizeof(*xc->fw) * xc->nfw);
|
||||
xc->nfw = 0;
|
||||
|
||||
return ENXIO;
|
||||
}
|
||||
|
||||
static int
|
||||
xc3028_firmware_upload(struct xc3028 *xc, struct xc3028_fw *xcfw)
|
||||
{
|
||||
const uint8_t *fw = xcfw->data, *p;
|
||||
uint32_t fwlen = xcfw->data_size;
|
||||
uint8_t cmd[64];
|
||||
unsigned int i;
|
||||
uint16_t len, rem;
|
||||
size_t wrlen;
|
||||
int error;
|
||||
|
||||
for (i = 0; i < fwlen - 2;) {
|
||||
len = le16dec(&fw[i]);
|
||||
i += 2;
|
||||
if (len == 0xffff)
|
||||
break;
|
||||
|
||||
/* reset command */
|
||||
if (len == 0x0000) {
|
||||
error = xc3028_reset(xc);
|
||||
if (error)
|
||||
return error;
|
||||
continue;
|
||||
}
|
||||
/* reset clk command */
|
||||
if (len == 0xff00) {
|
||||
continue;
|
||||
}
|
||||
/* delay command */
|
||||
if (len & 0x8000) {
|
||||
delay((len & 0x7fff) * 1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + len > fwlen) {
|
||||
printf("weird len, i=%u len=%u fwlen=%u'\n", i, len, fwlen);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
cmd[0] = fw[i];
|
||||
p = &fw[i + 1];
|
||||
rem = len - 1;
|
||||
while (rem > 0) {
|
||||
wrlen = min(rem, __arraycount(cmd) - 1);
|
||||
memcpy(&cmd[1], p, wrlen);
|
||||
error = xc3028_write_buffer(xc, cmd, wrlen + 1);
|
||||
if (error)
|
||||
return error;
|
||||
p += wrlen;
|
||||
rem -= wrlen;
|
||||
}
|
||||
i += len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
xc3028_scode_upload(struct xc3028 *xc, struct xc3028_fw *xcfw)
|
||||
{
|
||||
static uint8_t scode_init[] = { 0xa0, 0x00, 0x00, 0x00 };
|
||||
static uint8_t scode_fini[] = { 0x00, 0x8c };
|
||||
int error;
|
||||
|
||||
if (xcfw->data_size < 12)
|
||||
return EINVAL;
|
||||
error = xc3028_write_buffer(xc, scode_init, sizeof(scode_init));
|
||||
if (error)
|
||||
return error;
|
||||
error = xc3028_write_buffer(xc, xcfw->data, 12);
|
||||
if (error)
|
||||
return error;
|
||||
error = xc3028_write_buffer(xc, scode_fini, sizeof(scode_fini));
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
xc3028_read_2(struct xc3028 *xc, uint16_t reg, uint16_t *val)
|
||||
{
|
||||
uint8_t cmd[2], resp[2];
|
||||
int error;
|
||||
|
||||
cmd[0] = reg >> 8;
|
||||
cmd[1] = reg & 0xff;
|
||||
error = iic_exec(xc->i2c, I2C_OP_WRITE, xc->i2c_addr,
|
||||
cmd, sizeof(cmd), NULL, 0, 0);
|
||||
if (error)
|
||||
return error;
|
||||
resp[0] = resp[1] = 0;
|
||||
error = iic_exec(xc->i2c, I2C_OP_READ, xc->i2c_addr,
|
||||
NULL, 0, resp, sizeof(resp), 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
*val = (resp[0] << 8) | resp[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
xc3028_write_buffer(struct xc3028 *xc, const uint8_t *data, size_t datalen)
|
||||
{
|
||||
return iic_exec(xc->i2c, I2C_OP_WRITE_WITH_STOP, xc->i2c_addr,
|
||||
data, datalen, NULL, 0, 0);
|
||||
}
|
||||
|
||||
#if notyet
|
||||
static int
|
||||
xc3028_write_2(struct xc3028 *xc, uint16_t reg, uint16_t val)
|
||||
{
|
||||
uint8_t data[4];
|
||||
|
||||
data[0] = reg >> 8;
|
||||
data[1] = reg & 0xff;
|
||||
data[2] = val >> 8;
|
||||
data[3] = val & 0xff;
|
||||
|
||||
return xc3028_write_buffer(xc, data, sizeof(data));
|
||||
}
|
||||
#endif
|
||||
|
||||
struct xc3028 *
|
||||
xc3028_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr,
|
||||
xc3028_reset_cb reset, void *reset_priv, enum xc3028_type type)
|
||||
{
|
||||
struct xc3028 *xc;
|
||||
|
||||
xc = kmem_alloc(sizeof(*xc), KM_SLEEP);
|
||||
if (xc == NULL)
|
||||
return NULL;
|
||||
xc->parent = parent;
|
||||
xc->i2c = i2c;
|
||||
xc->i2c_addr = addr;
|
||||
xc->reset = reset;
|
||||
xc->reset_priv = reset_priv;
|
||||
xc->type = type;
|
||||
|
||||
if (xc3028_firmware_open(xc)) {
|
||||
aprint_error_dev(parent, "%s: fw open failed\n",
|
||||
xc3028_name(xc));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return xc;
|
||||
|
||||
failed:
|
||||
kmem_free(xc, sizeof(*xc));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
xc3028_close(struct xc3028 *xc)
|
||||
{
|
||||
unsigned int index;
|
||||
|
||||
if (xc->fw) {
|
||||
for (index = 0; index < xc->nfw; index++) {
|
||||
if (xc->fw[index].data)
|
||||
kmem_free(xc->fw[index].data,
|
||||
xc->fw[index].data_size);
|
||||
}
|
||||
kmem_free(xc->fw, sizeof(*xc->fw) * xc->nfw);
|
||||
}
|
||||
kmem_free(xc, sizeof(*xc));
|
||||
}
|
||||
|
||||
int
|
||||
xc3028_tune_dtv(struct xc3028 *xc, const struct dvb_frontend_parameters *params)
|
||||
{
|
||||
static uint8_t freq_init[] = { 0x80, 0x02, 0x00, 0x00 };
|
||||
uint8_t freq_buf[4];
|
||||
uint32_t div, offset = 0;
|
||||
int error;
|
||||
|
||||
if (params->u.vsb.modulation == VSB_8) {
|
||||
offset = 1750000;
|
||||
} else {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
div = (params->frequency - offset + 15625 / 2) / 15625;
|
||||
|
||||
error = xc3028_write_buffer(xc, freq_init, sizeof(freq_init));
|
||||
if (error)
|
||||
return error;
|
||||
delay(10000);
|
||||
|
||||
freq_buf[0] = (div >> 24) & 0xff;
|
||||
freq_buf[1] = (div >> 16) & 0xff;
|
||||
freq_buf[2] = (div >> 8) & 0xff;
|
||||
freq_buf[3] = (div >> 0) & 0xff;
|
||||
error = xc3028_write_buffer(xc, freq_buf, sizeof(freq_buf));
|
||||
if (error)
|
||||
return error;
|
||||
delay(100000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE(MODULE_CLASS_DRIVER, xc3028, NULL);
|
||||
|
||||
static int
|
||||
xc3028_modcmd(modcmd_t cmd, void *opaque)
|
||||
{
|
||||
switch (cmd) {
|
||||
case MODULE_CMD_INIT:
|
||||
mutex_init(&xc3028_firmware_lock, MUTEX_DEFAULT, IPL_NONE);
|
||||
return 0;
|
||||
case MODULE_CMD_FINI:
|
||||
mutex_destroy(&xc3028_firmware_lock);
|
||||
return 0;
|
||||
default:
|
||||
return ENOTTY;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/* $NetBSD: xc3028reg.h,v 1.1 2011/07/11 18:00:06 jmcneill Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2011 Jared D. McNeill <jmcneill@invisible.ca>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _XC3028REG_H
|
||||
#define _XC3028REG_H
|
||||
|
||||
#define XC3028_REG_FREQ_ERROR 0x0001
|
||||
#define XC3028_REG_LOCK 0x0002
|
||||
#define XC3028_REG_VERSION 0x0004
|
||||
#define XC3028_REG_PRODUCT_ID 0x0008
|
||||
#define XC3028_REG_HSYNC_FREQ 0x0010
|
||||
#define XC3028_REG_FRAME_LINES 0x0020
|
||||
#define XC3028_REG_QUALITY 0x0040
|
||||
#define XC3028_REG_ADC_ENVELOPE 0x0100
|
||||
|
||||
#endif /* !_XC3028REG_H */
|
|
@ -0,0 +1,70 @@
|
|||
/* $NetBSD: xc3028var.h,v 1.1 2011/07/11 18:00:06 jmcneill Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2011 Jared D. McNeill <jmcneill@invisible.ca>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _XC3028VAR_H
|
||||
#define _XC3028VAR_H
|
||||
|
||||
#include <dev/i2c/i2cvar.h>
|
||||
#include <dev/dtv/dtvio.h>
|
||||
|
||||
typedef int (*xc3028_reset_cb)(void *);
|
||||
|
||||
enum xc3028_type {
|
||||
XC3028,
|
||||
XC3028L,
|
||||
};
|
||||
|
||||
struct xc3028_fw {
|
||||
uint32_t type;
|
||||
uint64_t id;
|
||||
uint16_t int_freq;
|
||||
uint8_t *data;
|
||||
uint32_t data_size;
|
||||
};
|
||||
|
||||
struct xc3028 {
|
||||
device_t parent;
|
||||
i2c_tag_t i2c;
|
||||
i2c_addr_t i2c_addr;
|
||||
|
||||
xc3028_reset_cb reset;
|
||||
void *reset_priv;
|
||||
|
||||
enum xc3028_type type;
|
||||
|
||||
struct xc3028_fw *fw;
|
||||
unsigned int nfw;
|
||||
};
|
||||
|
||||
struct xc3028 * xc3028_open(device_t, i2c_tag_t, i2c_addr_t,
|
||||
xc3028_reset_cb, void *, enum xc3028_type);
|
||||
void xc3028_close(struct xc3028 *);
|
||||
int xc3028_tune_dtv(struct xc3028 *,
|
||||
const struct dvb_frontend_parameters *);
|
||||
|
||||
#endif /* !_XC3028VAR_H */
|
Loading…
Reference in New Issue