oleavr-rgl-a500-mini-linux-.../drivers/media/platform/sunxi-vin/vin-cci/bsp_cci.c

580 lines
13 KiB
C
Raw Normal View History

2022-05-07 01:01:45 +02:00
/*
* linux-3.10/drivers/media/platform/sunxi-vin/vin-cci/bsp_cci.c
*
* Copyright (c) 2007-2017 Allwinnertech Co., Ltd.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
/*
******************************************************************************
*
* bsp_cci.c
*
* Hawkview ISP - bsp_cci.c module
*
* Copyright (c) 2014 by Allwinnertech Co., Ltd. http://www.allwinnertech.com
*
* Version Author Date Description
*
* 2.0 Yang Feng 2014/04/23 Second Version
*
******************************************************************************
*/
#include "bsp_cci.h"
#include "../platform/platform_cfg.h"
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/semaphore.h>
wait_queue_head_t wait[MAX_CSIC_CCI_NUM];
static int status_err_flag[MAX_CSIC_CCI_NUM] = { 0 };
struct mutex cci_mutex[MAX_CSIC_CCI_NUM];
struct semaphore cci_sema[MAX_CSIC_CCI_NUM];
int bsp_csi_cci_set_base_addr(unsigned int sel, unsigned long addr)
{
init_waitqueue_head(&wait[sel]);
mutex_init(&cci_mutex[sel]);
sema_init(&cci_sema[sel], 1);
return csi_cci_set_base_addr(sel, addr);
}
static void cci_cal_div(unsigned int clk, unsigned char *div_coef)
{
unsigned char clk_m = 0;
unsigned char clk_n = 0;
unsigned int src_clk = CCI_HCLK / 10;
unsigned int sclk_req = clk;
unsigned char _2_pow_clk_n = 1;
unsigned int sclk_real = 0;
unsigned int divider = src_clk / sclk_req;
if (divider == 0) {
clk_m = 1;
goto out;
}
while (clk_n < 8) {
clk_m = (divider / _2_pow_clk_n) - 1;
while (clk_m < 16) {
sclk_real = src_clk / (clk_m + 1) / _2_pow_clk_n;
if (sclk_real <= sclk_req)
goto out;
else
clk_m++;
}
clk_n++;
_2_pow_clk_n *= 2;
}
out:
div_coef[0] = clk_m;
div_coef[1] = clk_n;
}
void bsp_csi_cci_init(unsigned int sel)
{
unsigned char div_coef[2];
csi_cci_reset(sel);
csi_cci_enable(sel);
cci_cal_div(400 * 1000, div_coef);
csi_cci_set_clk_div(sel, div_coef);
csi_cci_set_pkt_interval(sel, 16);
csi_cci_set_ack_timeout(sel, 16);
csi_cci_set_trig_dly(sel, 0);
cci_pad_en(sel);
}
void bsp_csi_cci_exit(unsigned int sel)
{
bsp_cci_int_disable(sel, CCI_INT_ALL);
csi_cci_reset(sel);
csi_cci_disable(sel);
}
void bsp_cci_set_tx_mode(unsigned int sel, struct cci_tx_mode *tx_mode)
{
csi_cci_set_tx_buf_mode(sel, &tx_mode->tx_buf_mode);
csi_cci_set_trig_mode(sel, &tx_mode->tx_trig_mode);
csi_cci_set_trig_line_cnt(sel, tx_mode->tx_trig_line_cnt);
}
void bsp_cci_tx_start(unsigned int sel, struct cci_msg *msg)
{
unsigned int i, j, pkt_len, max_pkt_num, pkt_num;
unsigned char *buf;
csi_cci_set_bus_fmt(sel, &msg->bus_fmt);
buf = msg->pkt_buf;
pkt_num = msg->pkt_num;
pkt_len = msg->bus_fmt.addr_len + msg->bus_fmt.data_len;
max_pkt_num = FIFO_DEPTH / pkt_len;
while (pkt_num != 0) {
if (pkt_num < max_pkt_num) {
j = pkt_num;
pkt_num = 0;
} else {
j = max_pkt_num;
pkt_num = pkt_num - max_pkt_num;
}
for (i = 0; i < j; i++) {
csi_cci_wr_tx_buf(sel, buf, pkt_len);
buf += pkt_len;
}
csi_cci_trans_start(sel, SINGLE);
}
}
void bsp_cci_tx_data_rb(unsigned int sel, struct cci_msg *msg)
{
unsigned int i, j, pkt_len, max_pkt_num, pkt_num;
unsigned char *buf;
csi_cci_set_bus_fmt(sel, &msg->bus_fmt);
buf = msg->pkt_buf;
pkt_num = msg->pkt_num;
pkt_len = msg->bus_fmt.addr_len + msg->bus_fmt.data_len;
max_pkt_num = FIFO_DEPTH / pkt_len;
while (pkt_num != 0) {
if (pkt_num < max_pkt_num) {
j = pkt_num;
pkt_num = 0;
} else {
j = max_pkt_num;
pkt_num = pkt_num - max_pkt_num;
}
for (i = 0; i < j; i++) {
csi_cci_fifo_pt_add(sel, msg->bus_fmt.addr_len);
csi_cci_rd_tx_buf(sel, buf + msg->bus_fmt.addr_len,
msg->bus_fmt.data_len);
buf += pkt_len;
}
}
csi_cci_fifo_pt_reset(sel);
}
static void bsp_cci_error_process(unsigned int sel)
{
cci_int_clear_status(sel, CCI_INT_ERROR);
bsp_cci_bus_error_process(sel);
bsp_csi_cci_exit(sel);
bsp_csi_cci_init_helper(sel);
}
static int bsp_cci_tx_start_wait_done_unlocked(unsigned int sel,
struct cci_msg *msg)
{
#ifdef CCI_IRQ
int ret = 0;
unsigned long timeout = 0;
bsp_cci_tx_start(sel, msg);
#ifdef FPGA_VER
timeout =
wait_event_timeout(wait[sel], csi_cci_get_trans_done(sel) == 0,
HZ / 50);
#else
timeout =
wait_event_timeout(wait[sel], csi_cci_get_trans_done(sel) == 0, HZ);
if (timeout == 0) {
printk("[VIN CCI_%d ERR] timeout error at "
"addr_8bit = %x, wr_flag = %d, val = %x\n",
sel, msg->bus_fmt.saddr_7bit << 1, msg->bus_fmt.wr_flag,
*(int *)msg->pkt_buf);
ret = -1;
}
#endif
if (1 == status_err_flag[sel]) {
printk("[VIN CCI_%d ERR] Status error at "
"addr_8bit = %x, wr_flag = %d, val = %x\n",
sel, msg->bus_fmt.saddr_7bit << 1, msg->bus_fmt.wr_flag,
*(int *)msg->pkt_buf);
ret = -1;
}
if (msg->bus_fmt.wr_flag == 1)
bsp_cci_tx_data_rb(sel, msg);
return ret;
#else
struct cci_int_status status;
int ret = 0;
bsp_cci_tx_start(sel, msg);
usleep_range(100, 120);
while (1) {
if (csi_cci_get_trans_done(sel) == 0) {
break;
} else {
usleep_range(80, 100);
}
};
cci_int_get_status(sel, &status);
if (status.error) {
cci_int_clear_status(sel, CCI_INT_ERROR);
printk("[VIN CCI_%d ERR] Status error at "
"addr_8bit = %x, wr_flag = %d\n",
sel, msg->bus_fmt.saddr_7bit << 1, msg->bus_fmt.wr_flag);
bsp_cci_bus_error_process(sel);
bsp_csi_cci_exit(sel);
bsp_csi_cci_init_helper(sel);
ret = -1;
}
if (status.complete)
cci_int_clear_status(sel, CCI_INT_FINISH);
if (msg->bus_fmt.wr_flag == 1)
bsp_cci_tx_data_rb(sel, msg);
return ret;
#endif
}
int bsp_cci_tx_start_wait_done(unsigned int sel, struct cci_msg *msg)
{
int ret = -1;
mutex_lock(&cci_mutex[sel]);
if (down_trylock(&cci_sema[sel])) {
printk("down_trylock fail!\n");
return -1;
}
ret = bsp_cci_tx_start_wait_done_unlocked(sel, msg);
up(&cci_sema[sel]);
mutex_unlock(&cci_mutex[sel]);
return ret;
}
void bsp_cci_int_enable(unsigned int sel, enum cci_int_sel interrupt)
{
cci_int_enable(sel, interrupt);
}
void bsp_cci_int_disable(unsigned int sel, enum cci_int_sel interrupt)
{
cci_int_disable(sel, interrupt);
}
void CCI_INLINE bsp_cci_int_get_status(unsigned int sel,
struct cci_int_status *status)
{
cci_int_get_status(sel, status);
}
void CCI_INLINE bsp_cci_int_clear_status(unsigned int sel,
enum cci_int_sel interrupt)
{
cci_int_clear_status(sel, interrupt);
}
enum cci_bus_status CCI_INLINE bsp_cci_get_bus_status(unsigned int sel)
{
return cci_get_bus_status(sel);
}
int bsp_cci_irq_process(unsigned int sel)
{
struct cci_int_status status;
unsigned int ret = 0;
cci_int_get_status(sel, &status);
if (status.error) {
bsp_cci_error_process(sel);
status_err_flag[sel] = 1;
ret = -1;
}
if (status.complete) {
status_err_flag[sel] = 0;
cci_int_clear_status(sel, CCI_INT_FINISH);
}
wake_up(&wait[sel]);
return ret;
}
void bsp_cci_bus_error_process(unsigned int sel)
{
cci_stop(sel);
cci_sck_cycles(sel, 16);
}
void bsp_csi_cci_init_helper(unsigned int sel)
{
struct cci_tx_mode tx_mode;
bsp_csi_cci_init(sel);
tx_mode.tx_buf_mode.buf_src = FIFO;
tx_mode.tx_buf_mode.pkt_mode = COMPACT;
tx_mode.tx_buf_mode.pkt_cnt = 1;
tx_mode.tx_trig_mode.trig_src = NO_TRIG;
tx_mode.tx_trig_mode.trig_con = TRIG_DEFAULT;
tx_mode.tx_trig_line_cnt = 0;
bsp_cci_set_tx_mode(sel, &tx_mode);
bsp_cci_int_clear_status(sel, CCI_INT_ALL);
bsp_cci_int_enable(sel, CCI_INT_ALL);
}
int cci_wr_8_8(unsigned int sel, unsigned char reg, unsigned char data,
unsigned char slv)
{
struct cci_msg msg;
unsigned char buf[2];
buf[0] = reg;
buf[1] = data;
msg.bus_fmt.saddr_7bit = slv;
msg.bus_fmt.wr_flag = 0;
msg.bus_fmt.rs_start = START_WITH_ID_W;
msg.bus_fmt.rs_mode = STOP_START;
msg.bus_fmt.addr_len = 1;
msg.bus_fmt.data_len = 1;
msg.pkt_buf = buf;
msg.pkt_num = 1;
return bsp_cci_tx_start_wait_done(sel, &msg);
}
int cci_wr_16_8(unsigned int sel, unsigned short reg, unsigned char data,
unsigned char slv)
{
struct cci_msg msg;
unsigned char buf[3];
buf[0] = (reg & 0xff00) >> 8;
buf[1] = (reg & 0x00ff);
buf[2] = data;
msg.bus_fmt.saddr_7bit = slv;
msg.bus_fmt.wr_flag = 0;
msg.bus_fmt.rs_start = START_WITH_ID_W;
msg.bus_fmt.rs_mode = STOP_START;
msg.bus_fmt.addr_len = 2;
msg.bus_fmt.data_len = 1;
msg.pkt_buf = buf;
msg.pkt_num = 1;
return bsp_cci_tx_start_wait_done(sel, &msg);
}
int cci_wr_16_16(unsigned int sel, unsigned short reg, unsigned short data,
unsigned char slv)
{
struct cci_msg msg;
unsigned char buf[4];
buf[0] = (reg & 0xff00) >> 8;
buf[1] = (reg & 0x00ff);
buf[2] = (data & 0xff00) >> 8;
buf[3] = (data & 0x00ff);
msg.bus_fmt.saddr_7bit = slv;
msg.bus_fmt.wr_flag = 0;
msg.bus_fmt.rs_start = START_WITH_ID_W;
msg.bus_fmt.rs_mode = STOP_START;
msg.bus_fmt.addr_len = 2;
msg.bus_fmt.data_len = 2;
msg.pkt_buf = buf;
msg.pkt_num = 1;
return bsp_cci_tx_start_wait_done(sel, &msg);
}
int cci_wr_8_16(unsigned int sel, unsigned char reg, unsigned short data,
unsigned char slv)
{
struct cci_msg msg;
unsigned char buf[3];
buf[0] = reg;
buf[1] = (data & 0xff00) >> 8;
buf[2] = (data & 0x00ff);
msg.bus_fmt.saddr_7bit = slv;
msg.bus_fmt.wr_flag = 0;
msg.bus_fmt.rs_start = START_WITH_ID_W;
msg.bus_fmt.rs_mode = STOP_START;
msg.bus_fmt.addr_len = 1;
msg.bus_fmt.data_len = 2;
msg.pkt_buf = buf;
msg.pkt_num = 1;
return bsp_cci_tx_start_wait_done(sel, &msg);
}
int cci_wr_0_16(unsigned int sel, unsigned short data, unsigned char slv)
{
struct cci_msg msg;
unsigned char buf[2];
buf[0] = (data & 0xff00) >> 8;
buf[1] = (data & 0x00ff);
msg.bus_fmt.saddr_7bit = slv;
msg.bus_fmt.wr_flag = 0;
msg.bus_fmt.rs_start = START_WITHOUT_ID_W;
msg.bus_fmt.rs_mode = STOP_START;
msg.bus_fmt.addr_len = 0;
msg.bus_fmt.data_len = 2;
msg.pkt_buf = buf;
msg.pkt_num = 1;
return bsp_cci_tx_start_wait_done(sel, &msg);
}
int cci_rd_8_8(unsigned int sel, unsigned char reg, unsigned char *data,
unsigned char slv)
{
struct cci_msg msg;
unsigned char buf[2];
int ret;
buf[0] = reg;
msg.bus_fmt.saddr_7bit = slv;
msg.bus_fmt.wr_flag = 1;
msg.bus_fmt.rs_start = START_WITH_ID_W;
msg.bus_fmt.rs_mode = STOP_START;
msg.bus_fmt.addr_len = 1;
msg.bus_fmt.data_len = 1;
msg.pkt_buf = buf;
msg.pkt_num = 1;
ret = bsp_cci_tx_start_wait_done(sel, &msg);
*data = buf[1];
return ret;
}
int cci_rd_16_8(unsigned int sel, unsigned short reg, unsigned char *data,
unsigned char slv)
{
struct cci_msg msg;
unsigned char buf[3];
int ret;
buf[0] = (reg & 0xff00) >> 8;
buf[1] = (reg & 0x00ff);
msg.bus_fmt.saddr_7bit = slv;
msg.bus_fmt.wr_flag = 1;
msg.bus_fmt.rs_start = START_WITH_ID_W;
msg.bus_fmt.rs_mode = STOP_START;
msg.bus_fmt.addr_len = 2;
msg.bus_fmt.data_len = 1;
msg.pkt_buf = buf;
msg.pkt_num = 1;
ret = bsp_cci_tx_start_wait_done(sel, &msg);
*data = buf[2];
return ret;
}
int cci_rd_16_16(unsigned int sel, unsigned short reg, unsigned short *data,
unsigned char slv)
{
struct cci_msg msg;
unsigned char buf[4];
int ret;
buf[0] = (reg & 0xff00) >> 8;
buf[1] = (reg & 0x00ff);
msg.bus_fmt.saddr_7bit = slv;
msg.bus_fmt.wr_flag = 1;
msg.bus_fmt.rs_start = START_WITH_ID_W;
msg.bus_fmt.rs_mode = STOP_START;
msg.bus_fmt.addr_len = 2;
msg.bus_fmt.data_len = 2;
msg.pkt_buf = buf;
msg.pkt_num = 1;
ret = bsp_cci_tx_start_wait_done(sel, &msg);
*data = buf[2] * 256 + buf[3];
return ret;
}
int cci_rd_8_16(unsigned int sel, unsigned char reg, unsigned short *data,
unsigned char slv)
{
struct cci_msg msg;
unsigned char buf[3];
int ret;
buf[0] = reg;
msg.bus_fmt.saddr_7bit = slv;
msg.bus_fmt.wr_flag = 1;
msg.bus_fmt.rs_start = START_WITH_ID_W;
msg.bus_fmt.rs_mode = STOP_START;
msg.bus_fmt.addr_len = 1;
msg.bus_fmt.data_len = 2;
msg.pkt_buf = buf;
msg.pkt_num = 1;
ret = bsp_cci_tx_start_wait_done(sel, &msg);
*data = buf[1] * 256 + buf[2];
return ret;
}
int cci_rd_0_16(unsigned int sel, unsigned short *data, unsigned char slv)
{
struct cci_msg msg;
unsigned char buf[2];
int ret;
msg.bus_fmt.saddr_7bit = slv;
msg.bus_fmt.wr_flag = 1;
msg.bus_fmt.rs_start = START_WITHOUT_ID_W;
msg.bus_fmt.rs_mode = STOP_START;
msg.bus_fmt.addr_len = 0;
msg.bus_fmt.data_len = 2;
msg.pkt_buf = buf;
msg.pkt_num = 1;
ret = bsp_cci_tx_start_wait_done(sel, &msg);
*data = buf[0] * 256 + buf[1];
return ret;
}
#define CCI_MAX_MSG_LEN 12
int cci_wr_a16_d8_continuous(unsigned int sel, unsigned short reg,
unsigned char *data, unsigned char slv, int size)
{
struct cci_msg msg;
unsigned char buf[2 + CCI_MAX_MSG_LEN];
int ret = 0, i;
while (size > 0) {
int len = size > CCI_MAX_MSG_LEN ? CCI_MAX_MSG_LEN : size;
buf[0] = (reg & 0xff00) >> 8;
buf[1] = (reg & 0x00ff);
for (i = 2; i < 2 + len; i++)
buf[i] = *data++;
msg.bus_fmt.saddr_7bit = slv;
msg.bus_fmt.wr_flag = 0;
msg.bus_fmt.rs_start = START_WITH_ID_W;
msg.bus_fmt.rs_mode = STOP_START;
msg.bus_fmt.addr_len = 2;
msg.bus_fmt.data_len = len;
msg.pkt_buf = buf;
msg.pkt_num = 1;
ret = bsp_cci_tx_start_wait_done(sel, &msg);
if (ret >= 0) {
ret = 0;
} else if (ret < 0) {
printk("[CCI] sensor_write error!\n");
break;
}
reg += len;
size -= len;
}
return ret;
}