ドライバ本体を追加

とりあえずS側のみ
This commit is contained in:
nns779
2018-02-10 22:08:44 +09:00
parent 69b817a93c
commit 1952d22a6b
17 changed files with 3300 additions and 0 deletions

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@
Module.symvers
modules.order
*.o
*.rc
*.ko
*.o.cmd
*.ko.cmd

29
driver/Makefile Normal file
View File

@@ -0,0 +1,29 @@
KVER = $(shell uname -r)
KBUILD_DIR := /lib/modules/$(KVER)/build
INSTALL_DIR := /lib/modules/$(KVER)/misc
CURRENT_DIR = $(shell pwd)
VERBOSE := 0
DEBUG := 0
TARGET := px4_drv.ko
ccflags-y := -I$(M)/../include
ifneq ($(DEBUG),0)
ccflags-y += -DDEBUG -g
endif
obj-m := px4_drv.o
px4_drv-objs := px4.o it930x-bus.o it930x.o tc90522.o rt710.o ringbuffer.o
all:
$(MAKE) -C $(KBUILD_DIR) M=$(CURRENT_DIR) KBUILD_VERBOSE=$(VERBOSE) modules
clean:
$(MAKE) -C $(KBUILD_DIR) M=$(CURRENT_DIR) KBUILD_VERBOSE=$(VERBOSE) clean
install:
install -D -v -m 644 $(TARGET) $(INSTALL_DIR)/$(TARGET)
install -D -v -m 644 ../etc/90-px4.rules /etc/udev/rules.d/90-px4.rules
depmod -a $(KVER)
uninstall:
rm -v $(INSTALL_DIR)/$(TARGET) /etc/udev/rules.d/90-px4.rules

22
driver/i2c_comm.h Normal file
View File

@@ -0,0 +1,22 @@
// i2c_comm.h
#ifndef __I2C_COMM_H__
#define __I2C_COMM_H__
struct i2c_comm_master {
int (*wr) (void *priv, u8 addr, const u8 * data, int len);
int (*rd) (void *priv, u8 addr, u8 *data, int len);
void *priv;
};
static inline int i2c_comm_master_write(struct i2c_comm_master *m, u8 addr, const u8 *data, int len)
{
return m->wr(m->priv, addr, data, len);
}
static inline int i2c_comm_master_read(struct i2c_comm_master *m, u8 addr, u8 *data, int len)
{
return m->rd(m->priv, addr, data, len);
}
#endif

374
driver/it930x-bus.c Normal file
View File

@@ -0,0 +1,374 @@
// it930x-bus.c
// IT930x bus functions
#include "print_format.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/atomic.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include "it930x-config.h"
#include "it930x-bus.h"
struct context {
struct it930x_bus *bus;
it930x_bus_on_stream_t on_stream;
void *ctx;
};
struct priv_data {
struct urb **urbs;
struct context ctx;
atomic_t start;
};
static int it930x_usb_ctrl_tx(struct it930x_bus *bus, const void *buf, int len, void *opt)
{
int ret = 0, rlen = 0;
struct usb_device *dev = bus->usb.dev;
#if 0
int sent;
const u8 *p = buf;
#endif
if (len > IT930X_USB_MAX_CONTROL_TRANSFER_SIZE || !buf || !len)
return -EINVAL;
#if 0
while (len > 0) {
int s = (len < IT930X_USB_MAX_CONTROL_PACKET_SIZE) ? len : IT930X_USB_MAX_CONTROL_PACKET_SIZE;
ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, 0x02), p, s, &rlen, bus->usb.timeout);
if (ret)
break;
p += rlen;
len -= rlen;
}
#else
/* Endpoint 0x02: Control IN */
ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, 0x02), (void *)buf, len, &rlen, bus->usb.timeout);
#endif
if (ret)
pr_debug("it930x_usb_ctrl_tx: Failed. (ret: %d)\n", ret);
msleep(1);
return ret;
}
static int it930x_usb_ctrl_rx(struct it930x_bus *bus, void *buf, int *len, void *opt)
{
int ret = 0, rlen;
struct usb_device *dev = bus->usb.dev;
if (!buf || !len || !*len)
return -EINVAL;
/* Endpoint 0x81: Control OUT */
ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, 0x81), buf, *len, &rlen, bus->usb.timeout);
if (ret)
pr_debug("it930x_usb_ctrl_rx: Failed. (ret: %d)\n", ret);
else
*len = rlen;
msleep(1);
return ret;
}
static int it930x_usb_stream_rx(struct it930x_bus *bus, void *buf, int *len)
{
int ret = 0, rlen;
struct usb_device *dev = bus->usb.dev;
if (!buf | !len)
return -EINVAL;
/* Endpoint 0x84: Stream OUT */
ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, 0x84), buf, (*len) ? *len : bus->usb.stream_xfer_size, &rlen, bus->usb.timeout);
if (ret)
pr_debug("it930x_usb_stream_rx: Failed. (ret: %d)\n", ret);
else
*len = rlen;
return ret;
}
static void free_urbs(struct usb_device *dev, struct urb **urbs, u32 n, bool b)
{
u32 i;
if (!urbs)
return;
for (i = 0; i < n; i++) {
struct urb *urb = urbs[i];
if (urb != NULL) {
if (urb->transfer_buffer) {
usb_free_coherent(dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma);
urb->transfer_buffer = NULL;
urb->transfer_buffer_length = 0;
}
if (b) {
usb_free_urb(urb);
urbs[i] = NULL;
}
}
}
}
static void it930x_usb_complete(struct urb *urb)
{
struct context *ctx = urb->context;
if (!urb->status) {
int ret = 0;
if (urb->actual_length)
ret = ctx->on_stream(ctx->ctx, urb->transfer_buffer, urb->actual_length);
if (!ret) {
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret)
pr_debug("it930x_usb_complete: usb_submit_urb() failed. (ret: %d)\n", ret);
}
} else if (urb->status != -ENOENT) {
pr_debug("it930x_usb_complete: status: %d\n", urb->status);
}
}
static int it930x_usb_start_streaming(struct it930x_bus *bus, it930x_bus_on_stream_t on_stream, void *context)
{
int ret = 0;
u32 i, n, l;
struct usb_device *dev = bus->usb.dev;
struct priv_data *priv = bus->usb.priv;
struct urb **urbs = priv->urbs;
struct context *ctx = &priv->ctx;
if (!on_stream)
return -EINVAL;
pr_debug("it930x_usb_start_streaming\n");
if (atomic_read(&priv->start))
return 0;
atomic_set(&priv->start, 1);
n = bus->usb.stream_urb_num;
l = bus->usb.stream_xfer_size;
ctx->on_stream = on_stream;
ctx->ctx = context;
for (i = 0; i < n; i++) {
void *p;
dma_addr_t dma;
p = usb_alloc_coherent(dev, l, GFP_ATOMIC, &dma);
if (!p) {
free_urbs(dev, urbs, n, false);
ret = -ENOMEM;
break;
}
usb_fill_bulk_urb(urbs[i], dev, usb_rcvbulkpipe(dev, 0x84), p, l, it930x_usb_complete, ctx);
urbs[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
urbs[i]->transfer_dma = dma;
}
for (i = 0; i < n; i++) {
ret = usb_submit_urb(urbs[i], GFP_ATOMIC);
if (ret) {
int j;
pr_debug("it930x_usb_start_streaming: usb_submit_urb() failed. (i: %u, ret: %d)\n", i, ret);
for (j = 0; j < i; j++)
usb_kill_urb(urbs[j]);
free_urbs(dev, urbs, n, false);
break;
}
}
if (ret)
atomic_set(&priv->start, 0);
return ret;
}
static int it930x_usb_stop_streaming(struct it930x_bus *bus)
{
u32 i, n;
struct usb_device *dev = bus->usb.dev;
struct priv_data *priv = bus->usb.priv;
struct urb **urbs = priv->urbs;
pr_debug("it930x_usb_stop_streaming\n");
if (!atomic_read(&priv->start))
return 0;
n = bus->usb.stream_urb_num;
for (i = 0; i < n; i++)
usb_kill_urb(urbs[i]);
free_urbs(dev, urbs, n, false);
atomic_set(&priv->start, 0);
return 0;
}
int it930x_bus_init(struct it930x_bus *bus)
{
int ret = 0;
if (!bus)
return -EINVAL;
switch(bus->type) {
case IT930X_BUS_USB:
if (!bus->usb.dev) {
ret = -EINVAL;
} else {
u32 i, num;
struct priv_data *priv;
struct urb **urbs;
bus->ops.ctrl_tx = it930x_usb_ctrl_tx;
bus->ops.ctrl_rx = it930x_usb_ctrl_rx;
bus->ops.stream_rx = it930x_usb_stream_rx;
bus->ops.start_streaming = it930x_usb_start_streaming;
bus->ops.stop_streaming = it930x_usb_stop_streaming;
num = bus->usb.stream_urb_num;
priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
if (!priv) {
ret = -ENOMEM;
break;
}
priv->ctx.bus = bus;
atomic_set(&priv->start, 0);
urbs = kcalloc(num, sizeof(*urbs), GFP_KERNEL);
if (!urbs) {
kfree(priv);
ret = -ENOMEM;
break;
}
for (i = 0; i < num; i++) {
urbs[i] = usb_alloc_urb(0, GFP_ATOMIC | __GFP_ZERO);
if (!urbs[i]) {
free_urbs(bus->usb.dev, urbs, num, true);
kfree(urbs);
kfree(priv);
ret = -ENOMEM;
break;
}
}
usb_get_dev(bus->usb.dev);
priv->urbs = urbs;
bus->usb.priv = priv;
}
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
int it930x_bus_term(struct it930x_bus *bus)
{
int ret = 0;
if (!bus) {
ret = -EINVAL;
goto exit;
}
switch(bus->type) {
case IT930X_BUS_USB:
{
struct priv_data *priv = bus->usb.priv;
if (priv) {
if (priv->urbs) {
free_urbs(bus->usb.dev, priv->urbs, bus->usb.stream_urb_num, true);
kfree(priv->urbs);
}
kfree(priv);
}
if (bus->usb.dev)
usb_put_dev(bus->usb.dev);
break;
}
default:
break;
}
memset(bus, 0, sizeof(struct it930x_bus));
exit:
return ret;
}
int it930x_bus_ctrl_tx(struct it930x_bus *bus, const void *buf, int len, void *opt)
{
if (!bus || !bus->ops.ctrl_tx)
return -EINVAL;
return bus->ops.ctrl_tx(bus, buf, len, opt);
}
int it930x_bus_ctrl_rx(struct it930x_bus *bus, void *buf, int *len, void *opt)
{
if (!bus || !bus->ops.ctrl_rx)
return -EINVAL;
return bus->ops.ctrl_rx(bus, buf, len, opt);
}
int it930x_bus_stream_rx(struct it930x_bus *bus, void *buf, int *len)
{
if (!bus || !bus->ops.stream_rx)
return -EINVAL;
return bus->ops.stream_rx(bus, buf, len);
}
int it930x_bus_start_streaming(struct it930x_bus *bus, it930x_bus_on_stream_t on_stream, void *context)
{
if (!bus || !bus->ops.start_streaming)
return -EINVAL;
return bus->ops.start_streaming(bus, on_stream, context);
}
int it930x_bus_stop_streaming(struct it930x_bus *bus)
{
if (!bus || !bus->ops.stop_streaming)
return -EINVAL;
return bus->ops.stop_streaming(bus);
}

49
driver/it930x-bus.h Normal file
View File

@@ -0,0 +1,49 @@
// it930x-bus.h
#ifndef __IT930X_BUS_H__
#define __IT930X_BUS_H__
#include <linux/usb.h>
#include "it930x-config.h"
typedef enum {
IT930X_BUS_NONE = 0,
IT930X_BUS_USB,
} it930x_bus_type_t;
typedef int (*it930x_bus_on_stream_t)(void *context, void *buf, u32 len);
struct it930x_bus;
struct it930x_bus_operations {
int (*ctrl_tx)(struct it930x_bus *bus, const void *buf, int len, void *opt);
int (*ctrl_rx)(struct it930x_bus *bus, void *buf, int *len, void *opt);
int (*stream_rx)(struct it930x_bus *bus, void *buf, int *len);
int (*start_streaming)(struct it930x_bus *bus, it930x_bus_on_stream_t on_stream, void *context);
int (*stop_streaming)(struct it930x_bus *bus);
};
struct it930x_bus {
it930x_bus_type_t type;
union {
struct {
struct usb_device *dev;
int timeout;
u32 stream_xfer_size;
u32 stream_urb_num;
void *priv;
} usb;
};
struct it930x_bus_operations ops;
};
int it930x_bus_init(struct it930x_bus *bus);
int it930x_bus_term(struct it930x_bus *bus);
int it930x_bus_ctrl_tx(struct it930x_bus *bus, const void *buf, int len, void *opt);
int it930x_bus_ctrl_rx(struct it930x_bus *bus, void *buf, int *len, void *opt);
int it930x_bus_stream_rx(struct it930x_bus *bus, void *buf, int *len);
int it930x_bus_start_streaming(struct it930x_bus *bus, it930x_bus_on_stream_t on_stream, void *context);
int it930x_bus_stop_streaming(struct it930x_bus *bus);
#endif

12
driver/it930x-config.h Normal file
View File

@@ -0,0 +1,12 @@
// it930x-config.h
#ifndef __IT930X_CONFIG_H__
#define __IT930X_CONFIG_H__
// USB
#define IT930X_USB_MAX_CONTROL_TRANSFER_SIZE 63
// I2C
#define IT930X_I2C_SPEED 0x07
#endif

700
driver/it930x.c Normal file
View File

@@ -0,0 +1,700 @@
// it930x.c
// ITE IT930x driver
#include "print_format.h"
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/firmware.h>
#include "it930x-config.h"
#include "it930x-bus.h"
#include "it930x.h"
#define reg_addr_len(reg) (((reg) & 0xff000000) ? 4 : (((reg) & 0x00ff0000) ? 3 : (((reg) & 0x0000ff00) ? 2 : 1)))
struct ctrl_buf {
u8 *buf;
u8 len;
};
static u16 calc_checksum(const void *buf, size_t len)
{
int i;
const u8 *b;
u16 c = 0;
i = len / 2;
b = buf;
while (i--) {
c += ((b[0] << 8) | (b[1]));
b += 2;
}
if (len % 2)
c += (b[0] << 8);
return ~c;
}
static int it930x_control(struct it930x_bridge *it930x, u16 cmd, struct ctrl_buf *buf, struct ctrl_buf *rbuf, u8 *rcode, bool no_rx)
{
int ret;
u8 *b, l;
u16 csum1, csum2;
int rl = 255;
if (!buf || buf->len > (255 - 4 - 2)) {
pr_debug("it930x_control: Invalid parameter.\n");
return -EINVAL;
}
b = it930x->buf;
l = 3 + buf->len + 2;
b[0] = l;
b[1] = (cmd >> 8) & 0xff;
b[2] = cmd & 0xff;
b[3] = it930x->sequence++;
if (buf->buf)
memcpy(&b[4], buf->buf, buf->len);
csum1 = calc_checksum(&b[1], l - 2);
b[l - 1] = (csum1 >> 8) & 0xff;
b[l] = csum1 & 0xff;
ret = it930x_bus_ctrl_tx(&it930x->bus, b, l + 1, NULL);
if (ret) {
pr_debug("it930x_control: it930x_bus_ctrl_tx() failed. (cmd: %04x, len: %u, ret: %d)\n", cmd, buf->len, ret);
return ret;
}
if (no_rx)
return 0;
ret = it930x_bus_ctrl_rx(&it930x->bus, b, &rl, NULL);
if (ret) {
pr_debug("it930x_control: it930x_bus_ctrl_rx() failed. (cmd: %04x, len: %u, rlen: %u, ret: %d)\n", cmd, buf->len, rl, ret);
return ret;
}
if (rl < 5) {
pr_debug("it930x_control: No enough response length. (cmd: %04x, len: %u, rlen: %u)\n", cmd, buf->len, rl);
return -EBADMSG;
}
csum1 = calc_checksum(&b[1], rl - 3);
csum2 = (((b[rl - 2] & 0xff) << 8) | (b[rl - 1] & 0xff));
if (csum1 != csum2) {
pr_debug("it930x_control: Incorrect checksum! (cmd: %04x, len: %u, rlen: %u, csum1: %04x, csum2: %04x)\n", cmd, buf->len, rl, csum1, csum2);
return -EBADMSG;
}
if (b[2]) {
pr_debug("it930x_control: Failed. (cmd: %04x, len: %u, rlen: %u, rcode: %u, csum: %04x)\n", cmd, buf->len, rl, b[2], csum1);
ret = -EIO;
} else if (rbuf) {
if (rbuf->buf) {
rbuf->len = ((rl - 3 - 2) > rbuf->len) ? rbuf->len : (rl - 3 - 2);
memcpy(rbuf->buf, &b[3], rbuf->len);
} else {
rbuf->len = rl;
}
}
if (rcode)
*rcode = b[2];
return ret;
}
int it930x_write_regs(struct it930x_bridge *it930x, struct it930x_regbuf *regbuf, int num)
{
int ret = 0;
int i;
if (!regbuf || !num) {
pr_debug("it930x_write_regs: Invaild parameter.\n");
return -EINVAL;
}
for (i = 0; i < num; i++) {
u8 b[249], len;
u32 reg = regbuf[i].reg;
struct ctrl_buf sb;
if (regbuf[i].buf) {
len = regbuf[i].u.len;
if (!len || len > (249 - 6)) {
pr_debug("it930x_write_regs: Buffer too large. (num: %d, i: %d, reg: %x)\n", num, i, reg);
continue;
}
memcpy(&b[6], regbuf[i].buf, len);
} else {
len = 1;
b[6] = regbuf[i].u.val;
}
b[0] = len;
b[1] = reg_addr_len(reg);
b[2] = (reg >> 24) & 0xff;
b[3] = (reg >> 16) & 0xff;
b[4] = (reg >> 8) & 0xff;
b[5] = reg & 0xff;
sb.buf = b;
sb.len = 6 + len;
ret = it930x_control(it930x, IT930X_CMD_REG_WRITE, &sb, NULL, NULL, false);
if (ret) {
pr_debug("it930x_write_regs: it930x_control() failed. (num: %d, i: %d, reg: %x, len: %u)\n", num, i, reg, len);
break;
}
}
return ret;
}
int it930x_write_reg(struct it930x_bridge *it930x, u32 reg, u8 val)
{
struct it930x_regbuf regbuf;
regbuf.reg = reg;
regbuf.buf = NULL;
regbuf.u.val = val;
return it930x_write_regs(it930x, &regbuf, 1);
}
int it930x_write_reg_bits(struct it930x_bridge *it930x, u32 reg, u8 val, u8 pos, u8 len)
{
int ret = 0;
u8 tmp;
struct it930x_regbuf regbuf;
if (len > 8) {
pr_debug("it930x_write_reg_bits: Invalid parameter.\n");
return -EINVAL;
}
regbuf.reg = reg;
regbuf.buf = &tmp;
regbuf.u.len = 1;
if (len < 8) {
ret = it930x_read_regs(it930x, &regbuf, 1);
if (ret) {
pr_debug("it930x_write_reg_bits: it930x_read_regs() failed. (reg: %x, val: %u, pos: %u, len: %u, ret: %d)\n", reg, val, pos, len, ret);
return ret;
}
tmp = (val << pos) | (tmp & (~((0xff) >> (8 - len) << pos)));
} else {
tmp = val;
}
ret = it930x_write_regs(it930x, &regbuf, 1);
if (ret)
pr_debug("it930x_write_reg_bits: it930x_write_regs() failed. (reg: %x, val: %u, pos: %u, len: %u, t: %u, ret: %d)\n", reg, val, pos, len, tmp, ret);
return ret;
}
int it930x_read_regs(struct it930x_bridge *it930x, struct it930x_regbuf *regbuf, int num)
{
int ret = 0;
int i;
if (!regbuf || !num) {
pr_debug("it930x_read_regs: Invald parameter.\n");
return -EINVAL;
}
for (i = 0; i < num; i++) {
u8 b[6];
u32 reg = regbuf[i].reg;
struct ctrl_buf sb, rb;
if (!regbuf[i].buf || !regbuf[i].u.len) {
pr_debug("it930x_read_regs: Invalid buffer. (num: %d, i: %d, reg: %x)\n", num, i, reg);
continue;
}
b[0] = regbuf[i].u.len;
b[1] = reg_addr_len(reg);
b[2] = (reg >> 24) & 0xff;
b[3] = (reg >> 16) & 0xff;
b[4] = (reg >> 8) & 0xff;
b[5] = reg & 0xff;
sb.buf = b;
sb.len = 6;
rb.buf = regbuf[i].buf;
rb.len = regbuf[i].u.len;
ret = it930x_control(it930x, IT930X_CMD_REG_READ, &sb, &rb, NULL, false);
if (ret) {
pr_debug("it930x_read_regs: it930x_control() failed. (num: %d, i: %d, reg: %x, len: %u, rlen: %u, ret: %d)\n", num, i, reg, regbuf[i].u.len, rb.len, ret);
break;
}
if (rb.len != regbuf[i].u.len)
pr_debug("it930x_read_regs: Incorrect size! (num: %d, i: %d, reg: %x, len: %u, rlen: %u)\n", num, i, reg, regbuf[i].u.len, rb.len);
}
return ret;
}
int it930x_read_reg(struct it930x_bridge *it930x, u32 reg, u8 *val)
{
struct it930x_regbuf regbuf = { reg, val, { 1 } };
return it930x_read_regs(it930x, &regbuf, 1);
}
static int it930x_i2c_master_write(struct it930x_i2c_master_info *i2c, u8 addr, const u8 *data, int len)
{
u8 b[249];
struct ctrl_buf sb;
if (!data || !len) {
pr_debug("it930x_i2c_master_write: Invalid parameter.\n");
return -EINVAL;
}
if (len > (249 - 3)) {
pr_debug("it930x_i2c_master_write: Buffer too large.\n");
return -EINVAL;
}
b[0] = len;
b[1] = i2c->bus;
b[2] = addr;
memcpy(&b[3], data, len);
sb.buf = b;
sb.len = 3 + len;
return it930x_control(i2c->it930x, IT930X_CMD_I2C_WRITE, &sb, NULL, NULL, false);
}
static int it930x_i2c_master_read(struct it930x_i2c_master_info *i2c, u8 addr, u8 *data, int len)
{
u8 b[3];
struct ctrl_buf sb, rb;
if (!data || !len) {
pr_debug("it930x_i2c_master_read: Invalid parameter.\n");
return -EINVAL;
}
b[0] = len;
b[1] = i2c->bus;
b[2] = addr;
sb.buf = b;
sb.len = 3;
rb.buf = data;
rb.len = len;
return it930x_control(i2c->it930x, IT930X_CMD_I2C_READ, &sb, &rb, NULL, false);
}
static int it930x_get_firmware_version(struct it930x_bridge *it930x)
{
int ret = 0;
u8 b[4];
struct ctrl_buf sb, rb;
b[0] = 1;
sb.buf = b;
sb.len = 1;
rb.buf = b;
rb.len = 4;
ret = it930x_control(it930x, IT930X_CMD_QUERYINFO, &sb, &rb, NULL, false);
if (!ret)
it930x->fw_version = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
return ret;
}
static int it930x_enable_dvbt_mode(struct it930x_bridge *it930x, bool enable)
{
int ret = 0;
if (enable) {
ret = it930x_write_reg_bits(it930x, 0xf41f, 1, 2, 1);
if (ret)
return ret;
ret = it930x_write_reg_bits(it930x, 0xda10, 0, 0, 1);
if (ret)
return ret;
ret = it930x_write_reg_bits(it930x, 0xf41a, 1, 0, 1);
if (ret)
return ret;
} else {
ret = it930x_write_reg_bits(it930x, 0xf41f, 0, 2, 1);
if (ret)
return ret;
ret = it930x_write_reg_bits(it930x, 0xda10, 1, 0, 1);
if (ret)
return ret;
ret = it930x_write_reg_bits(it930x, 0xf41a, 0, 0, 1);
if (ret)
return ret;
}
return 0;
}
static int it930x_enable_stream_output(struct it930x_bridge *it930x, bool enable)
{
int ret = 0, ret2 = 0;
ret = it930x_write_reg_bits(it930x, 0xda1d, 1, 0, 1);
if (ret)
return ret;
switch(it930x->bus.type) {
case IT930X_BUS_USB:
// nak
ret = it930x_write_reg_bits(it930x, 0xdd13, (enable) ? 0 : 1, 5, 1);
if (ret)
goto end_rst_off;
// ep
ret = it930x_write_reg_bits(it930x, 0xdd11, (enable) ? 1 : 0, 5, 1);
if (ret)
goto end_rst_off;
if (enable) {
struct it930x_regbuf regbuf[2];
u16 x;
u8 buf[2];
x = (it930x->bus.usb.stream_xfer_size / 4) & 0xffff;
buf[0] = x & 0xff;
buf[1] = (x >> 8) & 0xff;
it930x_regbuf_set_buf(&regbuf[0], 0xdd88, buf, 2);
it930x_regbuf_set_val(&regbuf[1], 0xdd0c, 512 / 4/* USB2.0 */);
ret = it930x_write_regs(it930x, regbuf, 2);
if (ret)
goto end_rst_off;
ret = it930x_write_reg_bits(it930x, 0xda05, 0, 0, 0);
if (ret)
goto end_rst_off;
ret = it930x_write_reg_bits(it930x, 0xda06, 0, 0, 0);
if (ret)
goto end_rst_off;
}
break;
default:
break;
}
end_rst_off:
ret2 = it930x_write_reg_bits(it930x, 0xda1d, 0, 0, 1);
// reverse: no
ret2 = it930x_write_reg(it930x, 0xd920, 0);
return (ret) ? ret : ret2;
}
static int it930x_config_i2c(struct it930x_bridge *it930x)
{
int ret = 0, i, j;
u32 i2c_regs[5][2] = {
{ 0x4975, 0x4971 },
{ 0x4974, 0x4970 },
{ 0x4973, 0x496f },
{ 0x4972, 0x496e },
{ 0x4964, 0x4963 }
};
struct it930x_regbuf regbuf[10];
// set i2c speed
it930x_regbuf_set_val(&regbuf[0], 0xf6a7, IT930X_I2C_SPEED);
it930x_regbuf_set_val(&regbuf[1], 0xf103, IT930X_I2C_SPEED);
ret = it930x_write_regs(it930x, regbuf, 2);
if (ret)
return ret;
// set i2c address and bus
for(i = 0, j = 0; i < 5; i++) {
struct it930x_stream_port *port = &it930x->port[i];
if (port->enable) {
it930x_regbuf_set_val(&regbuf[j], i2c_regs[port->slave_number][0], port->i2c_addr);
j++;
it930x_regbuf_set_val(&regbuf[j], i2c_regs[port->slave_number][1], port->i2c_bus);
j++;
}
}
if (j) {
ret = it930x_write_regs(it930x, regbuf, j);
if (ret)
return ret;
}
return 0;
}
static int it930x_config_stream_port(struct it930x_bridge *it930x)
{
int ret = 0, i;
for (i = 0; i < 5; i++) {
struct it930x_stream_port *port = &it930x->port[i];
struct it930x_regbuf regbuf[3];
if (!port->enable)
continue;
if (port->number < 2) {
ret = it930x_write_reg(it930x, 0xda58 + port->number, (port->is_parallel) ? 1 : 0);
if (ret) {
pr_debug("it930x_config_stream_port: it930x_write_reg(): failed. (idx: %d, ret: %d)\n", i, ret);
break;
}
}
// mode: sync byte
it930x_regbuf_set_val(&regbuf[0], 0xda73 + port->number, 1);
it930x_regbuf_set_val(&regbuf[1], 0xda78 + port->number, port->sync_byte);
// enable port
it930x_regbuf_set_val(&regbuf[2], 0xda4c + port->number, 1);
ret = it930x_write_regs(it930x, regbuf, 3);
if (ret) {
pr_debug("it930x_config_stream_port: it930x_write_regs() failed. (idx: %d, ret: %d)\n", i, ret);
break;
}
}
return ret;
}
int it930x_init(struct it930x_bridge *it930x)
{
int i;
// set i2c operator
for (i = 0; i < 2; i++) {
it930x->i2c[i].it930x = it930x;
it930x->i2c[i].bus = i + 2;
it930x->i2c_master[i].wr = (int (*)(void *, u8, const u8 *, int))it930x_i2c_master_write;
it930x->i2c_master[i].rd = (int (*)(void *, u8, u8 *, int))it930x_i2c_master_read;
it930x->i2c_master[i].priv = &it930x->i2c[i];
}
return 0;
}
int it930x_load_firmware(struct it930x_bridge *it930x, const char *filename)
{
int ret = 0;
const struct firmware *fw;
size_t i, n, len = 0;
struct ctrl_buf sb;
if (!filename)
return -EINVAL;
ret = it930x_get_firmware_version(it930x);
if (ret) {
pr_debug("it930x_load_firmware: it930x_get_firmware_version() failed. 1 (ret: %d)\n", ret);
goto end;
}
if (it930x->fw_version)
return 0;
ret = it930x_write_reg(it930x, 0xf103, IT930X_I2C_SPEED);
if (ret) {
pr_debug("it930x_load_firmware: it930x_write_reg(0xf103) failed. (ret: %d)\n", ret);
return ret;
}
ret = request_firmware(&fw, filename, &it930x->bus.usb.dev->dev);
if (ret) {
pr_debug("it930x_load_firmware: request_firmware() failed. (ret: %d)\n", ret);
pr_err("Could't load firmware from the file.\n");
return ret;
}
n = fw->size;
for(i = 0; i < n; i += len) {
const u8 *p = &fw->data[i];
unsigned j, m = p[3];
len = 0;
if (p[0] != 0x03) {
pr_debug("it930x_load_firmware: Invalid firmware block was found. Abort. (ofs: %zx)\n", i);
ret = -ECANCELED;
goto end;
}
for(j = 0; j < m; j++)
len += p[6 + (j * 3)];
if (!len) {
pr_debug("it930x_load_firmware: No data in the block. (ofs: %zx)\n", i);
continue;
}
len += 4 + (m * 3);
// send firmware block
sb.buf = (u8 *)p;
sb.len = len;
ret = it930x_control(it930x, IT930X_CMD_FW_SCATTER_WRITE, &sb, NULL, NULL, false);
if (ret) {
pr_debug("it930x_load_firmware: it930x_control(IT930X_CMD_FW_SCATTER_WRITE) failed. (ofs: %zx, ret: %d)\n", i, ret);
goto end;
}
}
sb.buf = NULL;
sb.len = 0;
ret = it930x_control(it930x, IT930X_CMD_BOOT, &sb, NULL, NULL, false);
if (ret) {
pr_debug("it930x_load_firmware: it930x_control(IT930X_CMD_BOOT) failed. (ret: %d)\n", ret);
goto end;
}
ret = it930x_get_firmware_version(it930x);
if (ret) {
pr_debug("it930x_load_firmware: it930x_get_firmware_version() failed. 2 (ret: %d)\n", ret);
goto end;
}
if (!it930x->fw_version) {
ret = -EREMOTEIO;
goto end;
}
pr_info("Firmware loaded. version: %08x\n", it930x->fw_version);
end:
release_firmware(fw);
return ret;
}
int it930x_init_device(struct it930x_bridge *it930x)
{
int ret = 0;
struct it930x_regbuf regbuf[4];
if (it930x->bus.type != IT930X_BUS_USB) {
pr_debug("it930x_init_device: This driver only supports usb.\n");
return -EINVAL;
}
it930x_regbuf_set_val(&regbuf[0], 0x4976, 0x00);
it930x_regbuf_set_val(&regbuf[1], 0x4bfb, 0x00);
it930x_regbuf_set_val(&regbuf[2], 0x4978, 0x00);
it930x_regbuf_set_val(&regbuf[3], 0x4977, 0x00);
ret = it930x_write_regs(it930x, regbuf, 4);
if (ret)
return ret;
// ignore sync byte: no
ret = it930x_write_reg(it930x, 0xda1a, 0);
if (ret)
return ret;
ret = it930x_enable_dvbt_mode(it930x, true);
if (ret) {
pr_debug("it930x_init_device: it930x_enable_dvbt_mode() failed.\n");
return ret;
}
ret = it930x_enable_stream_output(it930x, true);
if (ret) {
pr_debug("it930x_init_device: it930x_enable_stream_output() failed.\n");
return ret;
}
// power config
it930x_regbuf_set_val(&regbuf[0], 0xd833, 1);
it930x_regbuf_set_val(&regbuf[1], 0xd830, 0);
it930x_regbuf_set_val(&regbuf[2], 0xd831, 1);
it930x_regbuf_set_val(&regbuf[3], 0xd832, 0);
ret = it930x_write_regs(it930x, regbuf, 4);
if (ret)
return ret;
ret = it930x_config_i2c(it930x);
if (ret) {
pr_debug("it930x_init_device: it930x_config_i2c() failed. (ret: %d)\n", ret);
return ret;
}
ret = it930x_config_stream_port(it930x);
if (ret) {
pr_debug("it930x_init_device: it930x_config_stream_port() failed. (ret: %d)\n", ret);
return ret;
}
return 0;
}
int it930x_set_gpio(struct it930x_bridge *it930x, int gpio, bool h)
{
u32 gpio_en_regs[] = {
0xd8b0, // gpioh1
0xd8b8, // gpioh2
0xd8b4, // gpioh3
0xd8c0, // gpioh4
0xd8bc, // gpioh5
0xd8c8, // gpioh6
0xd8c4, // gpioh7
0xd8d0, // gpioh8
0xd8cc, // gpioh9
0xd8d8, // gpioh10
0xd8d4, // gpioh11
0xd8e0, // gpioh12
0xd8dc, // gpioh13
0xd8e4, // gpioh14
0xd8e8, // gpioh15
0xd8ec, // gpioh16
};
struct it930x_regbuf regbuf[3];
if (gpio <= 0 || gpio > (sizeof(gpio_en_regs) / sizeof(gpio_en_regs[0])))
return -EINVAL;
it930x_regbuf_set_val(&regbuf[0], gpio_en_regs[gpio - 1], 1);
it930x_regbuf_set_val(&regbuf[1], gpio_en_regs[gpio - 1] + 0x01, 1);
it930x_regbuf_set_val(&regbuf[2], gpio_en_regs[gpio - 1] - 0x01, (h) ? 1 : 0);
return it930x_write_regs(it930x, regbuf, 3);
}

82
driver/it930x.h Normal file
View File

@@ -0,0 +1,82 @@
// it930x.h
#ifndef __IT930X_H__
#define __IT930X_H__
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include "it930x-config.h"
#include "it930x-bus.h"
#include "i2c_comm.h"
#define IT930X_CMD_REG_READ 0x00
#define IT930X_CMD_REG_WRITE 0x01
#define IT930X_CMD_QUERYINFO 0x22
#define IT930X_CMD_BOOT 0x23
#define IT930X_CMD_FW_SCATTER_WRITE 0x29
#define IT930X_CMD_I2C_READ 0x2a
#define IT930X_CMD_I2C_WRITE 0x2b
struct it930x_i2c_master_info {
struct it930x_bridge *it930x;
u8 bus;
};
struct it930x_stream_port {
bool enable;
bool is_parallel;
u8 number; // internal port number
u8 slave_number;
u8 i2c_bus;
u8 i2c_addr;
u8 packet_len;
u8 sync_byte;
};
struct it930x_bridge {
struct it930x_bus bus;
u32 fw_version;
struct it930x_stream_port port[5];
struct it930x_i2c_master_info i2c[2];
struct i2c_comm_master i2c_master[2];
u8 buf[255];
u8 sequence;
};
struct it930x_regbuf {
u32 reg;
u8 *buf;
union {
u8 val; // buf == NULL (write only)
u8 len; // buf != NULL
} u;
};
static inline void it930x_regbuf_set_val(struct it930x_regbuf *regbuf, u32 reg, u8 val)
{
regbuf->reg = reg;
regbuf->buf = NULL;
regbuf->u.val = val;
}
static inline void it930x_regbuf_set_buf(struct it930x_regbuf *regbuf, u32 reg, u8 *buf, u8 len)
{
regbuf->reg = reg;
regbuf->buf = buf;
regbuf->u.len = len;
}
int it930x_write_regs(struct it930x_bridge *it930x, struct it930x_regbuf *regbuf, int num_regbuf);
int it930x_write_reg(struct it930x_bridge *it930x, u32 reg, u8 val);
int it930x_write_reg_bits(struct it930x_bridge *it930x, u32 reg, u8 val, u8 pos, u8 len);
int it930x_read_regs(struct it930x_bridge *it930x, struct it930x_regbuf *regbuf, int num_regbuf);
int it930x_read_reg(struct it930x_bridge *it930x, u32 reg, u8 *val);
int it930x_init(struct it930x_bridge *it930x);
int it930x_load_firmware(struct it930x_bridge *it930x, const char *filename);
int it930x_init_device(struct it930x_bridge *it930x);
int it930x_set_gpio(struct it930x_bridge *it930x, int gpio, bool h);
#endif

8
driver/print_format.h Normal file
View File

@@ -0,0 +1,8 @@
// print_format.h
#ifndef __PRINT_FORMAT_H__
#define __PRINT_FORMAT_H__
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#endif

1134
driver/px4.c Normal file

File diff suppressed because it is too large Load Diff

8
driver/px4.h Normal file
View File

@@ -0,0 +1,8 @@
// px4.h
#ifndef __PX4_H__
#define __PX4_H__
#define PX4_DRIVER_VERSION "0.1.0"
#endif

141
driver/ringbuffer.c Normal file
View File

@@ -0,0 +1,141 @@
// ringbuffer.c
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/atomic.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include "ringbuffer.h"
int ringbuffer_init(struct ringbuffer *ringbuffer, size_t size)
{
ringbuffer_term(ringbuffer);
ringbuffer->buf = (u8 *)__get_free_pages(GFP_KERNEL, get_order(size));
if (!ringbuffer->buf) {
return -ENOMEM;
}
ringbuffer->buf_size = size;
ringbuffer->data_size = 0;
spin_lock_init(&ringbuffer->lock);
init_waitqueue_head(&ringbuffer->wait);
atomic_set(&ringbuffer->empty, 0);
ringbuffer->tail_pos = ringbuffer->head_pos = 0;
return 0;
}
int ringbuffer_term(struct ringbuffer *ringbuffer)
{
if (ringbuffer->buf) {
unsigned long p = (unsigned long)ringbuffer->buf;
ringbuffer->buf = NULL;
free_pages(p, get_order(ringbuffer->buf_size));
}
return 0;
}
int ringbuffer_flush(struct ringbuffer *ringbuffer)
{
ringbuffer->tail_pos = ringbuffer->head_pos = 0;
ringbuffer->data_size = 0;
atomic_set(&ringbuffer->empty, 1);
wake_up(&ringbuffer->wait);
return 0;
}
int ringbuffer_write(struct ringbuffer *ringbuffer, const void *data, size_t len)
{
unsigned long flags;
const u8 *p = data;
size_t buf_size, data_size, tail_pos, write_size;
if (!ringbuffer->buf)
return -EINVAL;
buf_size = ringbuffer->buf_size;
tail_pos = ringbuffer->tail_pos;
spin_lock_irqsave(&ringbuffer->lock, flags);
data_size = ringbuffer->data_size;
spin_unlock_irqrestore(&ringbuffer->lock, flags);
write_size = (data_size + len <= buf_size) ? (len) : (buf_size - data_size);
if (write_size) {
size_t t = (tail_pos + write_size <= buf_size) ? (write_size) : (buf_size - tail_pos);
memcpy(ringbuffer->buf + tail_pos, p, t);
if (t < write_size) {
memcpy(ringbuffer->buf, p + t, write_size - t);
tail_pos = write_size - t;
} else {
tail_pos = (tail_pos + write_size == buf_size) ? 0 : (tail_pos + write_size);
}
ringbuffer->tail_pos = tail_pos;
spin_lock_irqsave(&ringbuffer->lock, flags);
ringbuffer->data_size += write_size;
wake_up(&ringbuffer->wait);
spin_unlock_irqrestore(&ringbuffer->lock, flags);
}
return 0;
}
int ringbuffer_read_to_user(struct ringbuffer *ringbuffer, void __user *buf, size_t *len)
{
u8 *p = buf;
size_t buf_size, l = *len, buf_pos = 0;
buf_size = ringbuffer->buf_size;
while (l > buf_pos) {
size_t data_size, head_pos, read_size, t;
if (!ringbuffer->buf)
break;
wait_event(ringbuffer->wait, (ringbuffer->data_size || atomic_read(&ringbuffer->empty)));
spin_lock(&ringbuffer->lock);
data_size = ringbuffer->data_size;
spin_unlock(&ringbuffer->lock);
if (!data_size)
break;
head_pos = ringbuffer->head_pos;
read_size = (l - buf_pos > data_size) ? (data_size) : (l - buf_pos);
t = (head_pos + read_size <= buf_size) ? (read_size) : (buf_size - head_pos);
memcpy(p + buf_pos, ringbuffer->buf + head_pos, t);
if (t < read_size) {
memcpy(p + buf_pos + t, ringbuffer->buf, read_size - t);
head_pos = read_size - t;
} else {
head_pos = (head_pos + read_size == buf_size) ? 0 : (head_pos + read_size);
}
ringbuffer->head_pos = head_pos;
buf_pos += read_size;
spin_lock(&ringbuffer->lock);
ringbuffer->data_size -= read_size;
spin_unlock(&ringbuffer->lock);
}
*len = buf_pos;
return 0;
}

28
driver/ringbuffer.h Normal file
View File

@@ -0,0 +1,28 @@
// ringbuffer.h
#ifndef __RINGBUFFER_H__
#define __RINGBUFFER_H__
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
struct ringbuffer {
spinlock_t lock; // for data_size
atomic_t empty;
wait_queue_head_t wait;
u8 *buf;
size_t buf_size;
size_t data_size;
size_t tail_pos; // write
size_t head_pos; // read
};
int ringbuffer_init(struct ringbuffer *ringbuffer, size_t size);
int ringbuffer_term(struct ringbuffer *ringbuffer);
int ringbuffer_flush(struct ringbuffer *ringbuffer);
int ringbuffer_write(struct ringbuffer *ringbuffer, const void *data, size_t len);
int ringbuffer_read_to_user(struct ringbuffer *ringbuffer, void __user *buf, size_t *len);
#endif

352
driver/rt710.c Normal file
View File

@@ -0,0 +1,352 @@
// rt710.c
// RafaelMicro RT710 driver
#include "print_format.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/delay.h>
#include "i2c_comm.h"
#include "rt710.h"
#define NUM_REGS 0x10
static const u8 init_regs[NUM_REGS] = {
0x40, 0x1d, 0x20, 0x10, 0x41, 0x50, 0xed, 0x25,
0x07, 0x58, 0x39, 0x64, 0x38, 0xf7/*0xe7*/, 0x90, 0x35
};
static const u8 sleep_regs[NUM_REGS] = {
0xff, 0x5c, 0x88, 0x30, 0x41, 0xc8, 0xed, 0x25,
0x47, 0xfc, 0x48, 0xa2, 0x08, 0x0f, 0xf3, 0x59
};
static u8 reverse_bit(u8 val)
{
u8 t = val;
t = (t & 0x55) << 1 | (t & 0xaa) >> 1;
t = (t & 0x33) << 2 | (t & 0xcc) >> 2;
t = (t & 0x0f) << 4 | (t & 0xf0) >> 4;
return t;
}
static int rt710_write_regs(struct rt710_tuner *t, u8 reg, const u8 *buf, int len)
{
u8 b[1 + NUM_REGS];
if (!t || !buf || !len)
return -EINVAL;
if (len > (NUM_REGS - reg))
return -EINVAL;
b[0] = reg;
memcpy(&b[1], buf, len);
return i2c_comm_master_write(t->i2c, t->i2c_addr, b, len + 1);
}
static int rt710_read_regs(struct rt710_tuner *t, u8 reg, u8 *buf, int len)
{
int ret = 0, i;
u8 b[1 + NUM_REGS];
if (!t || !buf || !len)
return -EINVAL;
if (len > (NUM_REGS - reg))
return -EINVAL;
b[0] = 0x00;
ret = i2c_comm_master_write(t->i2c, t->i2c_addr, b, 1);
if (ret)
return ret;
ret = i2c_comm_master_read(t->i2c, t->i2c_addr, &b[0], len + reg);
if (ret)
return ret;
for (i = reg; i < (reg + len); i++)
buf[i - reg] = reverse_bit(b[i]);
return 0;
}
int rt710_init(struct rt710_tuner *t)
{
int ret = 0;
u8 tmp;
ret = rt710_read_regs(t, 0x03, &tmp, 1);
if (ret) {
pr_debug("rt710_init: rt710_read_regs() failed.\n");
return ret;
}
if ((tmp & 0xf0) != 0x70) {
pr_debug("rt710_init: Unknown chip.\n");
return -ENOSYS;
}
return 0;
}
int rt710_sleep(struct rt710_tuner *t, bool sleep)
{
u8 regs[NUM_REGS];
memcpy(regs, sleep_regs, sizeof(regs));
if (sleep)
regs[0x03] = 0x20;
return rt710_write_regs(t, 0x00, regs, NUM_REGS);
}
static int rt710_set_pll_regs(struct rt710_tuner *t, u8 *regs, u32 freq)
{
int ret = 0;
u32 min, max, b, c;
u16 e, g, h;
u8 div, a, d, f;
min = 2350000;
max = min * 2;
div = 2;
a = 0;
g = 2;
h = 0;
do {
u32 q;
q = freq * div;
if (q >= min && q <= max) {
switch(div) {
case 2:
a = 1;
break;
case 4:
a = 0;
break;
case 8:
a = 2;
break;
case 16:
a = 3;
break;
default:
return -ECANCELED;
}
break;
}
div *= 2;
} while(div <= 16);
regs[4] &= 0xfe;
regs[4] |= (a & 1);
ret = rt710_write_regs(t, 0x04, &regs[0x04], 1);
if (ret)
return ret;
b = freq * div;
c = (b / 2) / 24000;
d = (c & 0xff);
e = (d * 17536) + (b & 0xffff);
if (e < 375) {
e = 0;
} else if (e > 47625) {
e = 0;
d++;
} else if (e > 23812 && e < 24000) {
e = 23812;
} else if (e > 24000 && e < 24187) {
e = 24187;
}
f = (d - 13) / 4;
regs[0x05] = f + ((d - (f * 4) - 13) << 6);
ret = rt710_write_regs(t, 0x05, &regs[0x05], 1);
if (ret)
return ret;
if (!e)
regs[0x04] |= 0x02;
ret = rt710_write_regs(t, 0x04, &regs[0x04], 1);
if (ret)
return ret;
while (e > 1) {
u32 s;
s = (24000 * 2) / g;
if (e > s) {
h += (32768 / (g / 2));
e -= s;
if (g >= 32768) {
break;
}
}
g *= 2;
}
regs[0x07] = ((h >> 8) & 0xff);
regs[0x06] = (h & 0xff);
ret = rt710_write_regs(t, 0x07, &regs[0x07], 1);
if (ret)
return ret;
ret = rt710_write_regs(t, 0x06, &regs[0x06], 1);
if (ret)
return ret;
return 0;
}
int rt710_set_params(struct rt710_tuner *t, u32 freq, u32 symbol_rate, u32 rolloff)
{
int ret = 0;
u8 regs[NUM_REGS];
u32 a;
u8 b = 0, f = 0;
struct {
u32 a;
u8 b;
u8 f;
} c[] = {
{ 50000, 0, 0 },
{ 73000, 0, 1 },
{ 96000, 1, 0 },
{ 104000, 1, 1 },
{ 116000, 2, 0 },
{ 126000, 2, 1 },
{ 134000, 3, 0 },
{ 146000, 3, 1 },
{ 158000, 4, 0 },
{ 170000, 4, 1 },
{ 178000, 5, 0 },
{ 190000, 5, 1 },
{ 202000, 6, 0 },
{ 212000, 6, 1 },
{ 218000, 7, 0 },
{ 234000, 7, 1 },
{ 244000, 9, 1 },
{ 246000, 10, 0 },
{ 262000, 10, 1 },
{ 266000, 11, 0 },
{ 282000, 11, 1 },
{ 298000, 12, 1 },
{ 318000, 13, 1 },
{ 340000, 14, 1 },
{ 358000, 15, 1 },
{ 379999, 16, 1 }
};
if (rolloff > 5)
return -EINVAL;
memcpy(regs, init_regs, sizeof(regs));
ret = rt710_write_regs(t, 0x00, regs, NUM_REGS);
if (ret) {
pr_debug("rt710_set_params: rt710_write_regs(0x00, NUM_REGS) failed. (ret: %d)", ret);
return ret;
}
ret = rt710_set_pll_regs(t, regs, freq);
if (ret) {
pr_debug("rt710_set_params: rt710_set_pll_regs() failed. (ret: %d)\n", ret);
return ret;
}
msleep(10);
if ((freq - 1600000) >= 350000) {
regs[0x02] &= 0xbf;
regs[0x08] &= 0x7f;
if (freq >= 1950000)
regs[0x0a] = 0x38;
} else {
regs[0x02] |= 0x40;
regs[0x08] |= 0x80;
}
ret = rt710_write_regs(t, 0x0a, &regs[0x0a], 1);
if (ret)
return ret;
ret = rt710_write_regs(t, 0x02, &regs[0x02], 1);
if (ret)
return ret;
ret = rt710_write_regs(t, 0x08, &regs[0x08], 1);
if (ret)
return ret;
regs[0x0e] &= 0xf3;
if (freq >= 2000000)
regs[0x0e] |= 0x08;
ret = rt710_write_regs(t, 0x0e, &regs[0x0e], 1);
if (ret)
return ret;
a = (symbol_rate * (0x73 + (rolloff * 5))) / 10;
if (!a)
return -ECANCELED;
if (a >= 380000) {
a -= 380000;
if (a % 17400)
b++;
a /= 17400;
b += (a & 0xff) + 0x10;
f = 1;
} else {
int i;
for (i = 0; i < (sizeof(c) / sizeof(c[0])); i++) {
if (a <= c[i].a) {
b = c[i].b;
f = c[i].f;
break;
}
}
}
regs[0x0f] = (b << 2) | f;
ret = rt710_write_regs(t, 0x0f, &regs[0x0f], 1);
if (ret)
return ret;
return ret;
}
int rt710_get_pll_locked(struct rt710_tuner *t, bool *locked)
{
int ret = 0;
u8 tmp;
ret = rt710_read_regs(t, 0x02, &tmp, 1);
if (ret) {
pr_debug("rt710_get_pll_locked: rt710_read_regs() failed. (ret: %d)\n", ret);
return ret;
}
*locked = (tmp & 0x80) ? true : false;
return ret;
}

20
driver/rt710.h Normal file
View File

@@ -0,0 +1,20 @@
// rt710.h
#ifndef __RT710_H__
#define __RT710_H__
#include <linux/types.h>
#include "i2c_comm.h"
struct rt710_tuner {
struct i2c_comm_master *i2c;
u8 i2c_addr;
};
int rt710_init(struct rt710_tuner *t);
int rt710_sleep(struct rt710_tuner *t, bool sleep);
int rt710_set_params(struct rt710_tuner *t, u32 freq, u32 symbol_rate, u32 rolloff);
int rt710_get_pll_locked(struct rt710_tuner *t, bool *locked);
#endif

280
driver/tc90522.c Normal file
View File

@@ -0,0 +1,280 @@
// tc90522.c
// Toshiba TC90522 driver
#include "print_format.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include "i2c_comm.h"
#include "tc90522.h"
int tc90522_write_regs(struct tc90522_demod *demod, struct tc90522_regbuf *regbuf, int num)
{
int ret = 0, i;
if (!regbuf || !num)
return -EINVAL;
for (i = 0; i < num; i++) {
u8 b[255], len;
b[0] = regbuf[i].reg;
if (regbuf[i].buf) {
len = regbuf[i].u.len;
if (!len || len > 254) {
pr_debug("tc90522_write_regs: Buffer too large. (num: %d, i: %d, reg: %x)\n", num, i, regbuf[i].reg);
continue;
}
memcpy(&b[1], regbuf[i].buf, len);
} else {
b[1] = regbuf[i].u.val;
len = 1;
}
ret = i2c_comm_master_write(demod->i2c, demod->i2c_addr, b, len + 1);
if (ret) {
pr_debug("tc90522_write_regs: i2c_comm_master_write() failed. (num: %d, i: %d, reg: %x, ret: %d)\n", num, i, regbuf[i].reg, ret);
break;
}
}
return ret;
}
int tc90522_write_reg(struct tc90522_demod *demod, u8 reg, u8 val)
{
struct tc90522_regbuf regbuf[1];
tc90522_regbuf_set_val(&regbuf[0], reg, val);
return tc90522_write_regs(demod, regbuf, 1);
}
int tc90522_read_regs(struct tc90522_demod *demod, struct tc90522_regbuf *regbuf, int num)
{
int ret = 0, i;
if (!regbuf || !num)
return -EINVAL;
for (i = 0; i < num; i++) {
u8 b[1];
if (!regbuf[i].buf || !regbuf[i].u.len) {
pr_debug("tc90522_read_regs: Invalid buffer. (num: %d, i: %d, reg: %x)\n", num, i, regbuf[i].reg);
continue;
}
b[0] = regbuf[i].reg;
ret = i2c_comm_master_write(demod->i2c, demod->i2c_addr, b, 1);
if (ret) {
pr_debug("tc90522_read_regs: i2c_comm_master_write() failed. (num: %d, i: %d, reg: %x, ret: %d)\n", num, i, regbuf[i].reg, ret);
break;
}
ret = i2c_comm_master_read(demod->i2c, demod->i2c_addr, regbuf[i].buf, regbuf[i].u.len);
if (ret) {
pr_debug("tc90522_read_regs: i2c_comm_master_read() failed. (num: %d, i: %d, reg: %x, ret: %d)\n", num, i, regbuf[i].reg, ret);
break;
}
}
return ret;
}
int tc90522_read_reg(struct tc90522_demod *demod, u8 reg, u8 *val)
{
struct tc90522_regbuf regbuf[1];
tc90522_regbuf_set_buf(&regbuf[0], reg, val, 1);
return tc90522_read_regs(demod, regbuf, 1);
}
static int tc90522_i2c_master_write(struct tc90522_demod *demod, u8 addr, const u8 *data, int len)
{
u8 b[255];
if (!data || !len || len > 253)
return -EINVAL;
b[0] = 0xfe;
b[1] = (addr << 1);
memcpy(&b[2], data, len);
return i2c_comm_master_write(demod->i2c, demod->i2c_addr, b, len + 2);
}
static int tc90522_i2c_master_read(struct tc90522_demod *demod, u8 addr, u8 *data, int len)
{
int ret = 0;
u8 b[2];
if (!data || !len)
return -EINVAL;
b[0] = 0xfe;
b[1] = (addr << 1) | 0x01;
ret = i2c_comm_master_write(demod->i2c, demod->i2c_addr, b, 2);
if (ret)
return ret;
return i2c_comm_master_read(demod->i2c, demod->i2c_addr, data, len);
}
int tc90522_init(struct tc90522_demod *demod)
{
demod->i2c_master.wr = (int (*)(void *, u8, const u8 *, int))tc90522_i2c_master_write;
demod->i2c_master.rd = (int (*)(void *, u8, u8 *, int))tc90522_i2c_master_read;
demod->i2c_master.priv = demod;
return 0;
}
int tc90522_sleep_s(struct tc90522_demod *demod, bool sleep)
{
struct tc90522_regbuf regbuf[2] = {
{ 0x13, NULL, { 0x00 } },
{ 0x17, NULL, { 0x00 } }
};
if (sleep) {
// sleep
regbuf[0].u.val = 0x80;
regbuf[1].u.val = 0xff;
}
return tc90522_write_regs(demod, regbuf, 2);
}
int tc90522_set_agc_s(struct tc90522_demod *demod, bool on)
{
struct tc90522_regbuf regbuf[] = {
{ 0x0a, NULL, { 0x00 } },
{ 0x10, NULL, { 0xb0 } },
{ 0x11, NULL, { 0x02 } },
{ 0x03, NULL, { 0x01 } }
};
if (on) {
// on
regbuf[0].u.val = 0xff;
regbuf[1].u.val |= 0x02;
regbuf[2].u.val = 0x00;
}
return tc90522_write_regs(demod, regbuf, 4);
}
int tc90522_tmcc_get_tsid_s(struct tc90522_demod *demod, u8 idx, u16 *tsid)
{
int ret = 0;
u8 b[2];
struct tc90522_regbuf regbuf[1];
if (idx >= 12)
return -EINVAL;
tc90522_regbuf_set_buf(&regbuf[0], 0xc3, &b[0], 1);
ret = tc90522_read_regs(demod, regbuf, 1);
if (ret)
return ret;
if (b[0] & 0x10)
return -EAGAIN;
tc90522_regbuf_set_buf(&regbuf[0], 0xce + (idx * 2), &b[0], 2);
ret = tc90522_read_regs(demod, regbuf, 1);
if (!ret)
*tsid = (b[0] << 8 | b[1]);
return ret;
}
int tc90522_set_tsid_s(struct tc90522_demod *demod, u16 tsid)
{
u8 b[2];
struct tc90522_regbuf regbuf[2];
b[0] = ((tsid >> 8) & 0xff);
b[1] = (tsid & 0xff);
tc90522_regbuf_set_buf(&regbuf[0], 0x8f, &b[0], 1);
tc90522_regbuf_set_buf(&regbuf[1], 0x90, &b[1], 1);
return tc90522_write_regs(demod, regbuf, 2);
}
int tc90522_get_tsid_s(struct tc90522_demod *demod, u16 *tsid)
{
int ret = 0;
u8 b[2];
struct tc90522_regbuf regbuf[1];
tc90522_regbuf_set_buf(&regbuf[0], 0xe6, &b[0], 2);
ret = tc90522_read_regs(demod, regbuf, 1);
if (!ret)
*tsid = (b[0] << 8 | b[1]);
return ret;
}
int tc90522_get_cn_s(struct tc90522_demod *demod, u16 *cn)
{
int ret = 0;
u8 b[2];
struct tc90522_regbuf regbuf[1];
tc90522_regbuf_set_buf(&regbuf[0], 0xbc, &b[0], 2);
ret = tc90522_read_regs(demod, regbuf, 1);
if (!ret)
*cn = (b[0] << 8) | b[1];
return ret;
}
int tc90522_enable_ts_pins_s(struct tc90522_demod *demod, bool e)
{
struct tc90522_regbuf regbuf[] = {
{ 0x1c, NULL, { 0x00 } },
{ 0x1f, NULL, { 0x00 } }
};
if (!e) {
regbuf[0].u.val = 0x80;
regbuf[1].u.val = 0x22;
}
return tc90522_write_regs(demod, regbuf, 2);
}
int tc90522_sleep_t(struct tc90522_demod *demod, bool sleep)
{
return tc90522_write_reg(demod, 0x03, (sleep) ? 0xf0 : 0x00);
}
int tc90522_get_cndat_t(struct tc90522_demod *demod, u32 *cndat)
{
int ret = 0;
u8 b[3];
struct tc90522_regbuf regbuf[1];
tc90522_regbuf_set_buf(&regbuf[0], 0x8b, &b[0], 3);
ret = tc90522_read_regs(demod, regbuf, 1);
if (!ret)
*cndat = (b[0] << 16) | (b[1] << 8) | b[2];
return ret;
}
int tc90522_enable_ts_pins_t(struct tc90522_demod *demod, bool e)
{
return tc90522_write_reg(demod, 0x1d, (e) ? 0x00 : 0xa8);
}

60
driver/tc90522.h Normal file
View File

@@ -0,0 +1,60 @@
// tc90522.h
#ifndef __TC90522_H__
#define __TC90522_H__
#include <linux/types.h>
#include "i2c_comm.h"
struct tc90522_demod {
struct i2c_comm_master *i2c;
u8 i2c_addr;
struct i2c_comm_master i2c_master;
};
struct tc90522_regbuf {
u8 reg;
u8 *buf;
union {
u8 val;
u8 len;
} u;
};
#define TC90522_REGBUF_ARRAY_NUM(regbuf) ((sizeof((regbuf)) / sizeof((regbuf)[0])))
static inline void tc90522_regbuf_set_val(struct tc90522_regbuf *regbuf, u8 reg, u8 val)
{
regbuf->reg = reg;
regbuf->buf = NULL;
regbuf->u.val = val;
}
static inline void tc90522_regbuf_set_buf(struct tc90522_regbuf *regbuf, u8 reg, u8 *buf, u8 len)
{
regbuf->reg = reg;
regbuf->buf = buf;
regbuf->u.len = len;
}
int tc90522_write_regs(struct tc90522_demod *demod, struct tc90522_regbuf *regbuf, int num);
int tc90522_write_reg(struct tc90522_demod *demod, u8 reg, u8 val);
int tc90522_read_regs(struct tc90522_demod *demod, struct tc90522_regbuf *regbuf, int num);
int tc90522_read_reg(struct tc90522_demod *demod, u8 reg, u8 *val);
int tc90522_init(struct tc90522_demod *demod);
int tc90522_sleep_s(struct tc90522_demod *demod, bool sleep);
int tc90522_set_agc_s(struct tc90522_demod *demod, bool on);
int tc90522_tmcc_get_tsid_s(struct tc90522_demod *demod, u8 idx, u16 *tsid);
int tc90522_set_tsid_s(struct tc90522_demod *demod, u16 tsid);
int tc90522_get_tsid_s(struct tc90522_demod *demod, u16 *tsid);
int tc90522_get_cn_s(struct tc90522_demod *demod, u16 *cn);
int tc90522_enable_ts_pins_s(struct tc90522_demod *demod, bool e);
int tc90522_sleep_t(struct tc90522_demod *demod, bool sleep);
int tc90522_get_cndat_t(struct tc90522_demod *demod, u32 *cndat);
int tc90522_enable_ts_pins_t(struct tc90522_demod *demod, bool e);
#endif