mirror of
https://github.com/tsukumijima/px4_drv.git
synced 2025-07-23 04:03:01 +02:00
ドライバ本体を追加
とりあえずS側のみ
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
Module.symvers
|
||||
modules.order
|
||||
*.o
|
||||
*.rc
|
||||
*.ko
|
||||
*.o.cmd
|
||||
*.ko.cmd
|
||||
|
29
driver/Makefile
Normal file
29
driver/Makefile
Normal 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
22
driver/i2c_comm.h
Normal 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
374
driver/it930x-bus.c
Normal 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
49
driver/it930x-bus.h
Normal 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
12
driver/it930x-config.h
Normal 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
700
driver/it930x.c
Normal 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, ®buf, 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, ®buf, 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, ®buf, 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, ®buf, 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(®buf[0], 0xdd88, buf, 2);
|
||||
it930x_regbuf_set_val(®buf[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(®buf[0], 0xf6a7, IT930X_I2C_SPEED);
|
||||
it930x_regbuf_set_val(®buf[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(®buf[j], i2c_regs[port->slave_number][0], port->i2c_addr);
|
||||
j++;
|
||||
it930x_regbuf_set_val(®buf[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(®buf[0], 0xda73 + port->number, 1);
|
||||
it930x_regbuf_set_val(®buf[1], 0xda78 + port->number, port->sync_byte);
|
||||
// enable port
|
||||
it930x_regbuf_set_val(®buf[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(®buf[0], 0x4976, 0x00);
|
||||
it930x_regbuf_set_val(®buf[1], 0x4bfb, 0x00);
|
||||
it930x_regbuf_set_val(®buf[2], 0x4978, 0x00);
|
||||
it930x_regbuf_set_val(®buf[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(®buf[0], 0xd833, 1);
|
||||
it930x_regbuf_set_val(®buf[1], 0xd830, 0);
|
||||
it930x_regbuf_set_val(®buf[2], 0xd831, 1);
|
||||
it930x_regbuf_set_val(®buf[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(®buf[0], gpio_en_regs[gpio - 1], 1);
|
||||
it930x_regbuf_set_val(®buf[1], gpio_en_regs[gpio - 1] + 0x01, 1);
|
||||
it930x_regbuf_set_val(®buf[2], gpio_en_regs[gpio - 1] - 0x01, (h) ? 1 : 0);
|
||||
|
||||
return it930x_write_regs(it930x, regbuf, 3);
|
||||
}
|
82
driver/it930x.h
Normal file
82
driver/it930x.h
Normal 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
8
driver/print_format.h
Normal 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
1134
driver/px4.c
Normal file
File diff suppressed because it is too large
Load Diff
8
driver/px4.h
Normal file
8
driver/px4.h
Normal 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
141
driver/ringbuffer.c
Normal 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
28
driver/ringbuffer.h
Normal 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
352
driver/rt710.c
Normal 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, ®s[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, ®s[0x05], 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!e)
|
||||
regs[0x04] |= 0x02;
|
||||
|
||||
ret = rt710_write_regs(t, 0x04, ®s[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, ®s[0x07], 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rt710_write_regs(t, 0x06, ®s[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, ®s[0x0a], 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rt710_write_regs(t, 0x02, ®s[0x02], 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rt710_write_regs(t, 0x08, ®s[0x08], 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regs[0x0e] &= 0xf3;
|
||||
|
||||
if (freq >= 2000000)
|
||||
regs[0x0e] |= 0x08;
|
||||
|
||||
ret = rt710_write_regs(t, 0x0e, ®s[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, ®s[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
20
driver/rt710.h
Normal 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
280
driver/tc90522.c
Normal 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(®buf[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(®buf[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(®buf[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(®buf[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(®buf[0], 0x8f, &b[0], 1);
|
||||
tc90522_regbuf_set_buf(®buf[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(®buf[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(®buf[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(®buf[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
60
driver/tc90522.h
Normal 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
|
Reference in New Issue
Block a user