增加所有文件

This commit is contained in:
2025-11-07 09:57:14 +08:00
parent b598c1d479
commit 97bc808489
9392 changed files with 3483224 additions and 21 deletions

View File

@@ -0,0 +1,889 @@
/*
* csi_drv.c
*
* Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*
* Description: csi controller driver (system adaptive)
*
* Revision History:
* -----------------
*/
#include "camera/sdrv-cam-baremetal-def.h"
#include "camera/vdev_defs.h"
#include "csi_drv.h"
#include "csi_hw.h"
#define MAX_CSI_INST CONFIG_CSI_NUM
#define MAX_CSI_CHANNEL CONFIG_CSI_CH_NUM
#define MAX_BUF_CNT 16
#define MAX_QLEN 1
#define SKIP_FRAME_NUM (1 << 0)
enum channel_status {
STOPPED,
RUNNING,
IDLE,
};
#define CHECK_CSI_ID_CH(id, ch) \
do { \
if ((id >= MAX_CSI_INST) || (ch >= MAX_CSI_CHANNEL)) { \
cam_err("%s: error id %d\n", __func__, id); \
return -1; \
} \
} while (0)
struct camera_device;
struct camera_buffer {
struct list_node list;
uint32_t buffer_idx;
uint32_t frm_cnt;
uint32_t timestamp;
dma_addr_t paddr[3];
};
struct cam_queue {
int qlen;
struct list_node qhead;
};
static struct camera_buffer s_buffers[MAX_CSI_INST][MAX_CSI_CHANNEL]
[MAX_BUF_CNT];
struct camera_channel {
struct camera_device *host;
uint32_t ch_id;
uint32_t enable;
uint32_t status;
uint32_t frm_cnt;
uint32_t update_cnt;
struct camera_buffer *bufs;
struct list_node empty;
struct list_node in_bufq;
struct list_node out_bufq;
struct cam_queue active_q;
struct camera_buffer *next_buf;
completion_t completion;
spinlock_t bufq_lock;
struct mem_range cam_mem[3];
struct csi_ch_params cfg;
};
struct camera_device {
uint32_t host_id; /* host ip id */
struct csi_device ex_dev;
struct csi_hw_dev *csi_hw;
uint32_t csi_irq;
reg_addr_t csi_base;
uint32_t bt; /* bus type */
uint32_t sync;
struct camera_channel cam_ch[MAX_CSI_CHANNEL];
};
struct csi_channel_ops ch_ops;
static struct camera_device s_cam_devices[MAX_CSI_INST];
#ifdef _CONFIG_OS_SAFETY_
static enum handler_return camera_int_handler(void *data)
{
enum handler_return ret = INT_NO_RESCHEDULE;
struct camera_device *cam_dev = (struct camera_device *)data;
cam_dev->csi_hw->ops.csi_irq_handle(cam_dev->csi_hw);
return ret;
}
static uint32_t get_irq(uint32_t id)
{
if (id == CSI_HOST_0)
return CSI1_INTERRUPT_NUM;
else if (id == CSI_HOST_1)
return CSI2_INTERRUPT_NUM;
else
return CSI3_INTERRUPT_NUM;
}
static reg_addr_t get_reg_base(uint32_t id)
{
uint32_t res;
int32_t ret, res_index = 0;
addr_t phy_addr = 0;
if (id == CSI_HOST_0)
res = RES_CSI_CSI1;
else if (id == CSI_HOST_1)
res = RES_CSI_CSI2;
else
res = RES_CSI_CSI3;
ret = res_get_info_by_id(res, &phy_addr, &res_index);
if (ret < 0) {
cam_err("%s: get resource 0x%x fail, csi_id=%d, ret=%d\n", __func__,
res, id, ret);
return NULL;
}
cam_info("%s: csi%d, base 0x%lx\n", __func__, id, (unsigned long)phy_addr);
return (reg_addr_t)phy_addr;
}
#else /* _CONFIG_OS_SSDK_ */
#define get_irq(id) CSI_INTR_NUM
#define get_reg_base(id) APB_CSI_BASE
static int camera_int_handler(uint32_t irq, void *data)
{
struct camera_device *cam_dev = (struct camera_device *)data;
cam_debug("camera_int_handler: enter irq: %d\n", irq);
cam_dev->csi_hw->ops.csi_irq_handle(cam_dev->csi_hw);
return 0;
}
static void register_int_handler(uint32_t irq, irq_handler handler, void *data)
{
irq_attach(irq, handler, data);
irq_enable(irq);
}
#endif
static uint32_t to_vdev_pixfmt(uint32_t csi_pix_fmt)
{
switch (csi_pix_fmt) {
case CSI_FMT_YUYV:
return VDEV_PIX_FMT_YUYV;
case CSI_FMT_UYVY:
return VDEV_PIX_FMT_UYVY;
case CSI_FMT_NV16:
return VDEV_PIX_FMT_NV16;
case CSI_FMT_NV61:
return VDEV_PIX_FMT_NV61;
case CSI_FMT_YUV422P:
return VDEV_PIX_FMT_YUV422P;
case CSI_FMT_RGB24:
return VDEV_PIX_FMT_RGB24;
case CSI_FMT_BGR24:
return VDEV_PIX_FMT_BGR24;
case CSI_FMT_RGB16:
return VDEV_PIX_FMT_RGB16;
default:
; // return VDEV_PIX_FMT_UYVY;
}
return VDEV_PIX_FMT_UYVY;
}
static uint32_t to_csi_pixfmt(uint32_t vdev_pix_fmt)
{
switch (vdev_pix_fmt) {
case VDEV_PIX_FMT_YUYV:
return CSI_FMT_YUYV;
case VDEV_PIX_FMT_UYVY:
return CSI_FMT_UYVY;
case VDEV_PIX_FMT_NV16:
return CSI_FMT_NV16;
case VDEV_PIX_FMT_NV61:
return CSI_FMT_NV61;
case VDEV_PIX_FMT_YUV422P:
return CSI_FMT_YUV422P;
case VDEV_PIX_FMT_RGB24:
return CSI_FMT_RGB24;
case VDEV_PIX_FMT_BGR24:
return CSI_FMT_BGR24;
case VDEV_PIX_FMT_RGB16:
return CSI_FMT_RGB16;
default:
; // return CSI_FMT_UYVY;
}
return CSI_FMT_UYVY;
}
static int camera_frame_done(void *caller, uint32_t ch)
{
struct camera_channel *pch = (struct camera_channel *)caller;
struct camera_device *cam_dev = pch->host;
struct list_node *node;
struct camera_buffer *pbuf;
unsigned long flags = 0;
#ifdef TEST_CAM_FPS
static uint64_t time_last, time_cur;
time_cur = current_time_hires();
if (pch->frm_cnt == 0)
time_last = time_cur;
if ((pch->frm_cnt & 0x1f) == 0x1f) {
uint64_t fps_1_us = 32000000000000ul / (time_cur - time_last);
cam_info("frm_cnt %d, cam_fps: %llu.%06llu\n", pch->frm_cnt,
fps_1_us / 1000000, fps_1_us % 1000000);
time_last = time_cur;
}
#endif
if (pch->status == STOPPED) {
pch->frm_cnt++;
return 0;
}
if (pch->active_q.qlen == 0) {
if (pch->status == RUNNING)
cam_err("csi%d img%d, active_q empty\n", cam_dev->host_id, ch);
pch->frm_cnt++;
return 0;
}
if (pch->active_q.qlen > MAX_QLEN)
cam_err("csi%d img%d, active_q full\n", cam_dev->host_id, ch);
spin_lock_irqsave(&pch->bufq_lock, flags);
node = list_peek_head(&pch->active_q.qhead);
list_delete(node);
pch->active_q.qlen--;
if (pch->frm_cnt < SKIP_FRAME_NUM) {
list_add_tail(&pch->in_bufq, node);
spin_unlock_irqrestore(&pch->bufq_lock, flags);
pch->frm_cnt++;
return 0;
}
pbuf = containerof(node, struct camera_buffer, list);
pbuf->timestamp = pch->frm_cnt * 10; /* TODO: get_systime(); */
pbuf->frm_cnt = pch->frm_cnt;
list_add_tail(&pch->out_bufq, &pbuf->list);
spin_unlock_irqrestore(&pch->bufq_lock, flags);
completion_done(&pch->completion);
cam_debug("%s, csi%d img%d, frm_cnt %d, buf_idx %d\n", __func__,
cam_dev->host_id, ch, pch->frm_cnt, pbuf->buffer_idx);
pch->frm_cnt++;
return 0;
}
static int camera_update_buf(void *caller, uint32_t ch)
{
struct camera_channel *pch = (struct camera_channel *)caller;
struct camera_device *cam_dev = pch->host;
struct list_node *node;
struct camera_buffer *pbuf;
struct csi_img_buf buf;
unsigned long flags = 0;
pch->update_cnt++;
if (pch->status == STOPPED)
return 0;
spin_lock_irqsave(&pch->bufq_lock, flags);
node = list_peek_head(&pch->in_bufq);
if (node == NULL) {
spin_unlock_irqrestore(&pch->bufq_lock, flags);
if (pch->status == RUNNING) {
pch->status = IDLE;
cam_debug("ch %d vbuf_list is empty\n", ch);
}
return 0;
}
pch->status = RUNNING;
if (pch->active_q.qlen >= MAX_QLEN) {
spin_unlock_irqrestore(&pch->bufq_lock, flags);
cam_err("csi%d img%d, active_q overflow\n", cam_dev->host_id, ch);
return 0;
}
if (pch->next_buf) {
pbuf = pch->next_buf;
list_add_tail(&pch->active_q.qhead, &pbuf->list);
pch->active_q.qlen++;
pch->next_buf = NULL;
}
list_delete(node);
pbuf = containerof(node, struct camera_buffer, list);
pch->next_buf = pbuf;
spin_unlock_irqrestore(&pch->bufq_lock, flags);
buf.paddr[0] = pbuf->paddr[0];
buf.paddr[1] = pbuf->paddr[1];
buf.paddr[2] = pbuf->paddr[2];
cam_dev->csi_hw->ops.csi_cfg_img_buf(cam_dev->csi_hw, ch, &buf);
cam_debug("%s, csi%d img%d, done\n", __func__, cam_dev->host_id, ch);
return 0;
}
static int camera_error_handle(void *caller, uint32_t ch)
{
struct camera_channel *pch = (struct camera_channel *)caller;
struct camera_device *cam_dev = pch->host;
cam_err("%s, csi%d img%d, error\n", __func__, cam_dev->host_id, ch);
return 0;
}
struct csi_device *camera_dev_init(uint32_t id)
{
int i;
struct camera_device *cam_dev;
if (id >= MAX_CSI_INST) {
cam_err("%s: error id %d\n", __func__, id);
return NULL;
}
cam_dev = &s_cam_devices[id];
cam_dev->host_id = id;
cam_dev->csi_irq = get_irq(id);
cam_dev->csi_base = (reg_addr_t)get_reg_base(id);
cam_dev->bt = 1;
cam_dev->sync = 0;
cam_err("csi%d init: irq %d, reg_base 0x%08x\n", id, cam_dev->csi_irq,
(uint32_t)cam_dev->csi_base);
cam_dev->csi_hw = (void *)csi_hw_init(cam_dev->csi_base, id);
if (cam_dev->csi_hw == NULL) {
cam_err("%s: faile to init csi_hw\n", __func__);
return NULL;
}
cam_dev->csi_hw->ops.csi_cfg_sync(cam_dev->csi_hw, cam_dev->sync);
cam_info("enalbe irq: %d\n", cam_dev->csi_irq);
register_int_handler(cam_dev->csi_irq, camera_int_handler, cam_dev);
for (i = 0; i < MAX_CSI_CHANNEL; i++) {
cam_dev->cam_ch[i].ch_id = i;
cam_dev->cam_ch[i].host = cam_dev;
spin_lock_init(&cam_dev->cam_ch[i].bufq_lock);
}
cam_dev->ex_dev.ops = ch_ops;
cam_info("%s: done\n", __func__);
return &cam_dev->ex_dev;
}
static int channel_open(uint32_t id, uint32_t ch)
{
int i;
struct camera_device *cam_dev;
struct camera_channel *pch;
struct camera_buffer *pbuf;
struct csi_callback_t cb;
CHECK_CSI_ID_CH(id, ch);
cam_dev = &s_cam_devices[id];
pch = &cam_dev->cam_ch[ch];
cam_dev->csi_hw->ops.csi_reset_img(cam_dev->csi_hw, ch,
CSI_RESET_IMG_CTX | CSI_RESET_IMG_HW);
list_initialize(&pch->empty);
pch->bufs = &s_buffers[id][ch][0];
memset(pch->bufs, 0, sizeof(struct camera_buffer) * MAX_BUF_CNT);
for (i = 0, pbuf = pch->bufs; i < MAX_BUF_CNT; i++, pbuf++) {
list_add_tail(&pch->empty, &pbuf->list);
}
list_initialize(&pch->out_bufq);
list_initialize(&pch->in_bufq);
list_initialize(&pch->active_q.qhead);
pch->active_q.qlen = 0;
pch->next_buf = NULL;
completion_init(&pch->completion);
pch->cfg.bus_type = CSI_BUS_PARALLEL2;
pch->cfg.bus_fmt = CSI_MBUS_UYVY8_2X8;
pch->cfg.width = 1920;
pch->cfg.height = 1080;
pch->cfg.crop_en = 0;
pch->cfg.set_pix_fmt = CSI_FMT_UYVY;
pch->cfg.pix_fmt_num = 1;
pch->cfg.pix_fmt[0] = CSI_FMT_UYVY;
memset(&cb, 0, sizeof(struct csi_callback_t));
cb.caller = (void *)pch;
cb.csi_frame_done = camera_frame_done;
cb.csi_update_buf = camera_update_buf;
cb.csi_error_handle = camera_error_handle;
cam_info("csi_register_callback: caller %p, done %p\n", cb.caller,
cb.csi_frame_done);
cam_dev->csi_hw->ops.csi_register_callback(cam_dev->csi_hw, ch, &cb);
pch->enable = 1;
pch->status = STOPPED;
cam_info("%s: ch %d done\n", __func__, ch);
return 0;
}
static int channel_close(uint32_t id, uint32_t ch)
{
struct camera_device *cam_dev;
struct camera_channel *pch;
CHECK_CSI_ID_CH(id, ch);
cam_dev = &s_cam_devices[id];
pch = &cam_dev->cam_ch[ch];
pch->enable = 0;
pch->status = STOPPED;
completion_destroy(&pch->completion);
cam_info("%s: ch %d done\n", __func__, ch);
return 0;
}
static int channel_cfg_bus_type(uint32_t id, uint32_t ch,
struct csi_ch_params *param)
{
struct camera_device *cam_dev;
struct camera_channel *pch;
CHECK_CSI_ID_CH(id, ch);
cam_dev = &s_cam_devices[id];
pch = &cam_dev->cam_ch[ch];
switch (param->bus_type) {
case VDEV_MBUS_PARALLEL:
case VDEV_MBUS_CSI1:
pch->cfg.bus_type = CSI_BUS_PARALLEL4;
break;
case VDEV_MBUS_PARALLEL2:
pch->cfg.bus_type = CSI_BUS_PARALLEL2;
break;
case VDEV_MBUS_BT656:
pch->cfg.bus_type = CSI_BUS_BT656;
break;
case VDEV_MBUS_BT1120SDR:
pch->cfg.bus_type = CSI_BUS_BT1120_SDR;
break;
default:
cam_err("%s: error vdev bus type %d\n", __func__, param->bus_type);
break;
}
cam_info("%s: ch %d ,bus %d ,done\n", __func__, ch, pch->cfg.bus_type);
return 0;
}
static int channel_cfg_bus_fmt(uint32_t id, uint32_t ch,
struct csi_ch_params *param)
{
struct camera_device *cam_dev;
struct camera_channel *pch;
CHECK_CSI_ID_CH(id, ch);
cam_dev = &s_cam_devices[id];
pch = &cam_dev->cam_ch[ch];
switch (param->bus_fmt) {
case VDEV_MBUS_FMT_UYVY:
pch->cfg.bus_fmt = CSI_MBUS_UYVY8_2X8;
break;
case VDEV_MBUS_FMT_YUYV:
pch->cfg.bus_fmt = CSI_MBUS_YUYV8_2X8;
break;
case VDEV_MBUS_FMT_UYVY16:
pch->cfg.bus_fmt = CSI_MBUS_UYVY8_1X16;
break;
case VDEV_MBUS_FMT_YUYV16:
pch->cfg.bus_fmt = CSI_MBUS_YUYV8_1X16;
break;
case VDEV_MBUS_FMT_RGB888:
pch->cfg.bus_fmt = CSI_MBUS_RGB888_1X24;
break;
case VDEV_MBUS_FMT_RGB24:
pch->cfg.bus_fmt = CSI_MBUS_RGB24_1X24;
break;
case VDEV_MBUS_FMT_BGR24:
pch->cfg.bus_fmt = CSI_MBUS_BGR24_1X24;
break;
case VDEV_MBUS_FMT_RGB565:
pch->cfg.bus_fmt = CSI_MBUS_RGB565_1X16;
break;
default:
cam_err("%s: error vdev bus fmt %d\n", __func__, param->bus_fmt);
break;
}
cam_info("%s: ch %d %d done\n", __func__, ch, pch->cfg.bus_fmt);
return 0;
}
static int channel_enum_pixfmt(uint32_t id, uint32_t ch,
struct csi_ch_params *param)
{
int ret;
uint32_t i;
struct camera_device *cam_dev;
struct camera_channel *pch;
struct csi_bus_info bus_info;
struct csi_outfmt_info res;
CHECK_CSI_ID_CH(id, ch);
cam_dev = &s_cam_devices[id];
pch = &cam_dev->cam_ch[ch];
bus_info.bus_type = pch->cfg.bus_type;
bus_info.mbus_fmt = pch->cfg.bus_fmt;
bus_info.bus_flag = 0;
ret = cam_dev->csi_hw->ops.csi_cfg_bus(cam_dev->csi_hw, ch, &bus_info);
ret |= cam_dev->csi_hw->ops.csi_query_outfmts(cam_dev->csi_hw, ch, &res);
if (ret == 0 && res.count > 0) {
param->pix_fmt_num = res.count;
for (i = 0; i < res.count; i++) {
param->pix_fmt[i] = to_vdev_pixfmt(res.fmts[i]);
if (i == 0)
pch->cfg.set_pix_fmt = res.fmts[i];
}
} else {
param->pix_fmt_num = 1;
param->pix_fmt[0] = VDEV_PIX_FMT_UYVY;
}
cam_info("%s: ch %d done\n", __func__, ch);
return 0;
}
static int channel_cfg_pixfmt(uint32_t id, uint32_t ch,
struct csi_ch_params *param)
{
struct camera_device *cam_dev;
struct camera_channel *pch;
CHECK_CSI_ID_CH(id, ch);
cam_dev = &s_cam_devices[id];
pch = &cam_dev->cam_ch[ch];
pch->cfg.set_pix_fmt = to_csi_pixfmt(param->set_pix_fmt);
cam_info("%s: ch %d done,\n", __func__, ch);
return 0;
}
static int channel_cfg_size(uint32_t id, uint32_t ch,
struct csi_ch_params *param)
{
struct camera_device *cam_dev;
struct camera_channel *pch;
CHECK_CSI_ID_CH(id, ch);
cam_dev = &s_cam_devices[id];
pch = &cam_dev->cam_ch[ch];
if (param->field_type == VDEV_FIELD_INTERLACED) {
pch->cfg.field_type = CSI_FIELD_INTERLACED;
pch->cfg.width_even = param->width_even;
pch->cfg.height_even = param->height_even;
} else {
pch->cfg.field_type = CSI_FIELD_NONE;
}
pch->cfg.width = param->width;
pch->cfg.height = param->height;
cam_info("%s: ch %d done\n", __func__, ch);
return 0;
}
static int channel_cfg_crop(uint32_t id, uint32_t ch,
struct csi_ch_params *param)
{
struct camera_device *cam_dev;
struct camera_channel *pch;
struct csi_img_crop crop[2];
CHECK_CSI_ID_CH(id, ch);
cam_dev = &s_cam_devices[id];
pch = &cam_dev->cam_ch[ch];
pch->cfg.crop_en = 1;
pch->cfg.crop_x = param->crop_x;
pch->cfg.crop_y = param->crop_y;
pch->cfg.crop_w = param->crop_w;
pch->cfg.crop_h = param->crop_h;
if (pch->status != STOPPED) {
memset(&crop[0], 0, sizeof(crop));
crop[0].x = pch->cfg.crop_x;
crop[0].y = pch->cfg.crop_y;
crop[0].w = pch->cfg.crop_w;
crop[0].h = pch->cfg.crop_h;
cam_dev->csi_hw->ops.csi_cfg_crop(cam_dev->csi_hw, ch, &crop[0]);
}
cam_info("%s: ch %d done\n", __func__, ch);
return 0;
}
static int channel_stream(uint32_t id, uint32_t ch, int on)
{
int ret = 0;
uint32_t outfmt = CSI_FMT_YUYV;
struct csi_bus_info bus_info;
struct csi_field_info field;
struct csi_img_size size[2];
struct csi_img_crop crop[2];
struct camera_device *cam_dev;
struct camera_channel *pch;
struct list_node *node;
unsigned long flags = 0;
CHECK_CSI_ID_CH(id, ch);
cam_dev = &s_cam_devices[id];
pch = &cam_dev->cam_ch[ch];
if ((on && (pch->status != STOPPED)) || (!on && (pch->status == STOPPED))) {
cam_err("%s: csi %d img %d already stream %s\n", __func__, id, ch,
(on ? "on" : "off"));
return 0;
}
if (on == 0) {
goto stream_off;
}
pch->status = IDLE;
bus_info.bus_type = pch->cfg.bus_type;
bus_info.mbus_fmt = pch->cfg.bus_fmt;
bus_info.bus_flag = 0;
ret = cam_dev->csi_hw->ops.csi_cfg_bus(cam_dev->csi_hw, ch, &bus_info);
outfmt = pch->cfg.set_pix_fmt;
ret |= cam_dev->csi_hw->ops.csi_cfg_outfmt(cam_dev->csi_hw, ch, outfmt);
field.field_type = pch->cfg.field_type;
ret |= cam_dev->csi_hw->ops.csi_cfg_field(cam_dev->csi_hw, ch, &field);
memset(&size[0], 0, sizeof(size));
size[0].w = pch->cfg.width;
size[0].h = pch->cfg.height;
size[1].w = pch->cfg.width_even;
size[1].h = pch->cfg.height_even;
ret |= cam_dev->csi_hw->ops.csi_cfg_size(cam_dev->csi_hw, ch, &size[0]);
if (pch->cfg.crop_en == 1) {
memset(&crop[0], 0, sizeof(crop));
crop[0].x = pch->cfg.crop_x;
crop[0].y = pch->cfg.crop_y;
crop[0].w = pch->cfg.crop_w;
crop[0].h = pch->cfg.crop_h;
ret |= cam_dev->csi_hw->ops.csi_cfg_crop(cam_dev->csi_hw, ch, &crop[0]);
}
ret |= camera_update_buf(pch, ch);
ret |= cam_dev->csi_hw->ops.csi_stream_on(cam_dev->csi_hw, ch);
if (ret) {
cam_info("%s: ch %d error\n", __func__, ch);
return -1;
}
pch->frm_cnt = 0;
pch->update_cnt = 0;
pch->status = RUNNING;
cam_info("%s: ch %d stream on done\n", __func__, ch);
return 0;
stream_off:
pch->status = STOPPED;
cam_dev->csi_hw->ops.csi_stream_off(cam_dev->csi_hw, ch);
/* clear all buffer queue here */
spin_lock_irqsave(&pch->bufq_lock, flags);
do {
node = list_peek_head(&pch->out_bufq);
if (node == NULL)
break;
list_delete(node);
list_add_tail(&pch->empty, node);
} while (1);
do {
node = list_peek_head(&pch->in_bufq);
if (node == NULL)
break;
list_delete(node);
list_add_tail(&pch->empty, node);
} while (1);
do {
node = list_peek_head(&pch->active_q.qhead);
if (node == NULL)
break;
list_delete(node);
list_add_tail(&pch->empty, node);
} while (1);
if (pch->next_buf) {
list_add_tail(&pch->empty, &pch->next_buf->list);
pch->next_buf = NULL;
}
spin_unlock_irqrestore(&pch->bufq_lock, flags);
cam_info("%s: ch %d stream off done\n", __func__, ch);
return 0;
}
static int channel_cfg_memrange(uint32_t id, uint32_t ch, uint32_t low[3],
uint32_t up[3])
{
struct camera_device *cam_dev;
struct camera_channel *pch;
CHECK_CSI_ID_CH(id, ch);
cam_dev = &s_cam_devices[id];
pch = &cam_dev->cam_ch[ch];
pch->cam_mem[0].low = low[0];
pch->cam_mem[0].up = up[0];
pch->cam_mem[1].low = low[1];
pch->cam_mem[1].up = up[1];
pch->cam_mem[2].low = low[2];
pch->cam_mem[2].up = up[2];
cam_dev->csi_hw->ops.csi_cfg_mem_range(cam_dev->csi_hw, ch,
&pch->cam_mem[0]);
cam_info("%s: ch %d done\n", __func__, ch);
return 0;
}
static int channel_qbuf(uint32_t id, uint32_t ch, void *buf)
{
struct camera_device *cam_dev;
struct camera_channel *pch;
struct camera_buffer *pbuf;
struct list_node *node;
struct vdev_buffer *in_buf = (struct vdev_buffer *)buf;
unsigned long flags = 0;
CHECK_CSI_ID_CH(id, ch);
cam_dev = &s_cam_devices[id];
pch = &cam_dev->cam_ch[ch];
spin_lock_irqsave(&pch->bufq_lock, flags);
node = list_peek_head(&pch->empty);
if (node == NULL) {
spin_unlock_irqrestore(&pch->bufq_lock, flags);
cam_err("%s: ch %d failed\n", __func__, ch);
return -1;
}
list_delete(node);
pbuf = containerof(node, struct camera_buffer, list);
pbuf->buffer_idx = in_buf->buffer_idx;
pbuf->paddr[0] = (dma_addr_t)in_buf->paddr[0];
pbuf->paddr[1] = (dma_addr_t)in_buf->paddr[1];
pbuf->paddr[2] = (dma_addr_t)in_buf->paddr[2];
list_add_tail(&pch->in_bufq, &pbuf->list);
spin_unlock_irqrestore(&pch->bufq_lock, flags);
// cam_info("%s: ch %d done, buf idx %d\n", __func__, ch,
// in_buf->buffer_idx);
return 0;
}
static int channel_dqbuf(uint32_t id, uint32_t ch, void *buf)
{
struct camera_device *cam_dev;
struct camera_channel *pch;
struct camera_buffer *pbuf;
struct list_node *node;
struct vdev_buffer *out_buf = (struct vdev_buffer *)buf;
uint32_t timeout_ms;
unsigned long flags = 0;
CHECK_CSI_ID_CH(id, ch);
cam_dev = &s_cam_devices[id];
pch = &cam_dev->cam_ch[ch];
spin_lock_irqsave(&pch->bufq_lock, flags);
node = list_peek_head(&pch->out_bufq);
if (node != NULL) {
cam_debug("%s: ch %d get node\n", __func__, ch);
goto get_node;
}
spin_unlock_irqrestore(&pch->bufq_lock, flags);
timeout_ms = (out_buf->timeout_ms > 0) ? out_buf->timeout_ms : (10 * 1000);
cam_debug("%s: ch %d wait timeout %d ms\n", __func__, ch, timeout_ms);
if (wait_completion_timeout(&pch->completion, timeout_ms) < 0) {
cam_err("%s: ch %d failed for timeout %d ms\n", __func__, ch,
timeout_ms);
return -1;
}
spin_lock_irqsave(&pch->bufq_lock, flags);
node = list_peek_head(&pch->out_bufq);
if (node == NULL) {
spin_unlock_irqrestore(&pch->bufq_lock, flags);
cam_debug("%s: ch %d node null\n", __func__, ch);
return -1;
}
get_node:
list_delete(node);
pbuf = containerof(node, struct camera_buffer, list);
out_buf->buffer_idx = pbuf->buffer_idx;
out_buf->frm_cnt = pbuf->frm_cnt;
out_buf->timestamp = pbuf->timestamp;
out_buf->paddr[0] = (void *)pbuf->paddr[0];
out_buf->paddr[1] = (void *)pbuf->paddr[1];
out_buf->paddr[2] = (void *)pbuf->paddr[2];
memset(pbuf, 0, sizeof(struct camera_buffer));
list_add_tail(&pch->empty, &pbuf->list);
spin_unlock_irqrestore(&pch->bufq_lock, flags);
cam_debug("%s: ch %d done\n", __func__, ch);
return 0;
}
struct csi_channel_ops ch_ops = {
.channel_open = channel_open,
.channel_close = channel_close,
.channel_stream = channel_stream,
.channel_cfg_bus_type = channel_cfg_bus_type,
.channel_cfg_bus_fmt = channel_cfg_bus_fmt,
.channel_cfg_size = channel_cfg_size,
.channel_cfg_crop = channel_cfg_crop,
.channel_enum_pixfmt = channel_enum_pixfmt,
.channel_cfg_pixfmt = channel_cfg_pixfmt,
.channel_cfg_memrange = channel_cfg_memrange,
.channel_qbuf = channel_qbuf,
.channel_dqbuf = channel_dqbuf,
};

View File

@@ -0,0 +1,61 @@
/**
* @file csi_drv.h
*
* Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*
* Description: csi driver interface
*
* Revision History:
* -----------------
*/
#ifndef __CSI_DRV_H__
#define __CSI_DRV_H__
struct csi_ch_params {
uint32_t bus_type;
uint32_t bus_fmt;
uint32_t field_type;
uint32_t width;
uint32_t height;
uint32_t width_even;
uint32_t height_even;
uint32_t crop_en;
uint32_t crop_x;
uint32_t crop_y;
uint32_t crop_w;
uint32_t crop_h;
uint32_t set_pix_fmt;
uint32_t pix_fmt_num;
uint32_t pix_fmt[8];
};
struct csi_channel_ops {
int (*channel_open)(uint32_t id, uint32_t ch);
int (*channel_close)(uint32_t id, uint32_t ch);
int (*channel_stream)(uint32_t id, uint32_t ch, int enable);
int (*channel_qbuf)(uint32_t id, uint32_t ch, void *buf);
int (*channel_dqbuf)(uint32_t id, uint32_t ch, void *buf);
int (*channel_cfg_bus_type)(uint32_t id, uint32_t ch,
struct csi_ch_params *param);
int (*channel_cfg_bus_fmt)(uint32_t id, uint32_t ch,
struct csi_ch_params *param);
int (*channel_cfg_size)(uint32_t id, uint32_t ch,
struct csi_ch_params *param);
int (*channel_cfg_crop)(uint32_t id, uint32_t ch,
struct csi_ch_params *param);
int (*channel_enum_pixfmt)(uint32_t id, uint32_t ch,
struct csi_ch_params *param);
int (*channel_cfg_pixfmt)(uint32_t id, uint32_t ch,
struct csi_ch_params *param);
int (*channel_cfg_memrange)(uint32_t id, uint32_t ch, uint32_t low[3],
uint32_t up[3]);
};
struct csi_device {
struct csi_channel_ops ops;
};
struct csi_device *camera_dev_init(uint32_t id);
#endif /* __CSI_DRV_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,195 @@
/*
* csi_hw.h
*
* Semidrive platform csi controller ip core header file
*
* Copyright (C) 2022, Semidrive Communications Inc.
*
* This file is licensed under a dual GPLv2 or X11 license.
*/
#ifndef __CSI_HW_H__
#define __CSI_HW_H__
#include <stdint.h>
/* max output format for one specified input bus fmt */
#define CSI_OUTFMT_MAX 8
/* csi hardware id */
enum csi_host_id {
CSI_HOST_0 = 0,
CSI_HOST_1,
CSI_HOST_2,
CSI_HOST_NUM,
};
/* image channel id in one csi hardware device */
enum csi_img_id { CSI_IMG0 = 0, CSI_IMG1, CSI_IMG2, CSI_IMG3, CSI_IMG_NUM };
/*
* for cmd of: int csi_reset_img(struct csi_hw_dev *csi_dev, uint32_t img_id,
* uint32_t cmd); CSI_RESET_IMG_HW: reset hardware (csi virtual channel)
* CSI_RESET_IMG_CTX: reset image software context.
* (CSI_RESET_IMG_HW | CSI_RESET_IMG_CTX) should be applied every time video
* open ”CSI_RESET_IMG_HW only“ can be applied for error recovering
*/
enum csi_img_reset_type {
CSI_RESET_IMG_HW = (1 << 0),
CSI_RESET_IMG_CTX = (1 << 1),
};
/* input camera signal interface type */
enum csi_bus_type {
CSI_BUS_MIPICSI2 = 0, /* mipi-csi2 */
CSI_BUS_PARALLEL1, /* continuous clk, VSYNC & HSYNC, gate mode1 -- NOT
SUPPORTED */
CSI_BUS_PARALLEL2, /* continuous clk, VSYNC * HSYNC, gate mode2 */
CSI_BUS_PARALLEL3, /* non-continuous clk, VSYNC */
CSI_BUS_PARALLEL4, /* continuous clk, VSYNC & HSYNC, DE(data-enable), same
as MIPICSI2 */
CSI_BUS_BT656, /* clk, data */
CSI_BUS_BT1120_SDR, /* clk, single data one cycle */
CSI_BUS_BT1120_DDR, /* clk, double data one cycle -- NOT SUPPORTED */
CSI_BUS_TYPE_MAX
};
/* input media format */
enum csi_mbus_fmt {
CSI_MBUS_UYVY8_2X8 =
1, /* for mipi-csi YUV422-8bit, for parallel UYVY-8bit */
CSI_MBUS_YUYV8_2X8, /* for parallel YUYV-8bit*/
CSI_MBUS_YUYV8_1X16, /* for parallel YUYV-16bit*/
CSI_MBUS_UYVY8_1X16, /* for parallel UYVY-16bit*/
CSI_MBUS_UYVY10_2X10, /* for mipi-csi YUV422-10bit */
CSI_MBUS_UYVY8_1_5X8, /* for mipi-csi YUV420-legacy, for parallel
UYY-VYY-8bit */
CSI_MBUS_VYUY8_1_5X8, /* for mipi-csi YUV420, for parallel VYY-UYY-8bit
*/
CSI_MBUS_RGB888_1X24, /* for mipi-csi RGB-8bit, for parallel RGB-8bit */
CSI_MBUS_RGB565_1X16, /* for mipi-csi RGB-6bit, for parallel RGB-6bit */
CSI_MBUS_RGB24_1X24, /* for parallel RGB-24bit*/
CSI_MBUS_BGR24_1X24, /* for parallel RGB-24bit*/
/* More types to be added ....*/
CSI_MBUS_FMT_MAX
};
enum csi_bus_flag {
MBUS_DE_ACTIVE_LOW = (1 << 0),
MBUS_VSYNC_ACTIVE_LOW = (1 << 1),
MBUS_HSYNC_ACTIVE_HIGH = (1 << 2),
MBUS_PCLK_SAMPLE_RISING = (1 << 3),
};
/* csi output format (stored in memory) */
enum csi_out_fmt {
CSI_FMT_YUYV = 1, /* Y-U-Y-V, 8-8-8-8 */
CSI_FMT_UYVY, /* U-Y-V-Y, 8-8-8-8 */
CSI_FMT_NV16, /* Y-Y-Y-Y, U-V-U-V, two planes*/
CSI_FMT_NV61, /* Y-Y-Y-Y, V-U-V-U, two planes*/
CSI_FMT_YUV422P, /* Y, U, V, three planes*/
CSI_FMT_RGB24, /* R-G-B, 8-8-8*/
CSI_FMT_BGR24, /* B-G-R, 8-8-8*/
CSI_FMT_RGB16, /* R-G-B, 5-6-5*/
/* More types to be added ....*/
CSI_FMT_MAX,
};
/* input image type. For BT656, interlaced field is supportted. Other cases
* should be CSI_FIELD_NONE */
enum csi_field_type {
CSI_FIELD_NONE, /* no field, progressive */
CSI_FIELD_TOP,
CSI_FIELD_BOTTOM,
CSI_FIELD_INTERLACED, /* both top and bottom field and interlaced into one
frame */
CSI_FIELD_MAX,
};
struct csi_img_crop {
uint32_t x;
uint32_t y;
uint32_t w;
uint32_t h;
};
struct csi_img_size {
uint32_t w;
uint32_t h;
};
struct csi_img_buf {
dma_addr_t paddr[3];
};
struct mem_range {
uint32_t up;
uint32_t low;
};
struct csi_bus_info {
uint32_t bus_type; /* should be enum csi_bus_type */
uint32_t mbus_fmt; /* should be enum csi_mbus_fmt */
uint32_t bus_flag;
};
/* supported output formats for one specified bus_types and mbus_fmt */
struct csi_outfmt_info {
uint32_t count;
uint32_t fmts[CSI_OUTFMT_MAX];
};
struct csi_field_info {
uint32_t field_type; /* should be enum csi_field_type */
};
struct csi_callback_t {
void *caller;
int (*csi_frame_done)(void *caller, uint32_t img_id);
int (*csi_update_buf)(void *caller, uint32_t img_id);
int (*csi_error_handle)(void *caller, uint32_t img_id);
};
struct csi_hw_dev;
struct csi_hw_operations {
/* ==== following interface for csi_core ==== */
int (*csi_irq_handle)(struct csi_hw_dev *csi_dev);
int (*csi_cfg_sync)(struct csi_hw_dev *csi_dev, uint32_t sync);
/* ==== following interface for csi_image (one of virtual channel) ==== */
int (*csi_reset_img)(struct csi_hw_dev *csi_dev, uint32_t img_id,
uint32_t cmd);
int (*csi_register_callback)(struct csi_hw_dev *csi_dev, uint32_t img_id,
struct csi_callback_t *cb);
int (*csi_cfg_bus)(struct csi_hw_dev *csi_dev, uint32_t img_id,
struct csi_bus_info *bus);
int (*csi_query_outfmts)(struct csi_hw_dev *csi_dev, uint32_t img_id,
struct csi_outfmt_info *fmt);
int (*csi_cfg_outfmt)(struct csi_hw_dev *csi_dev, uint32_t img_id,
uint32_t outfmt);
int (*csi_cfg_field)(struct csi_hw_dev *csi_dev, uint32_t img_id,
struct csi_field_info *field);
int (*csi_cfg_size)(struct csi_hw_dev *csi_dev, uint32_t img_id,
struct csi_img_size *size);
int (*csi_cfg_crop)(struct csi_hw_dev *csi_dev, uint32_t img_id,
struct csi_img_crop *crop);
int (*csi_cfg_img_buf)(struct csi_hw_dev *csi_dev, uint32_t img_id,
struct csi_img_buf *buf);
int (*csi_cfg_mem_range)(struct csi_hw_dev *csi_dev, uint32_t img_id,
struct mem_range *mem);
int (*csi_stream_on)(struct csi_hw_dev *csi_dev, uint32_t img_id);
int (*csi_stream_off)(struct csi_hw_dev *csi_dev, uint32_t img_id);
};
struct csi_hw_dev {
void *priv_data;
uint32_t host_id;
struct csi_hw_operations ops;
};
/* ==== following interface for csi_core ==== */
struct csi_hw_dev *csi_hw_init(reg_addr_t base, uint32_t host_id);
int csi_hw_deinit(struct csi_hw_dev *csi_dev);
#endif

View File

@@ -0,0 +1,131 @@
/*
* csi_reg.h
*
* Semidrive platform mipi csi2 header file
*
* Copyright (C) 2021, Semidrive Communications Inc.
*
* This file is licensed under a dual GPLv2 or X11 license.
*/
#ifndef __CSI_REG_H__
#define __CSI_REG_H__
/* all registers in csi ip */
#define CSI_REG_ENABLE 0x00
#define CSI_REG_INT_STAT0 0x04
#define CSI_REG_INT_STAT1 0x08
#define CSI_REG_INT_MASK0 0x0c
#define CSI_REG_INT_MASK1 0x10
#define CSI_REG_REG_LOAD 0x14
/* n: 0 ~ 3, indicates image channel. */
#define CSI_REG_IMG_RGBY_BADDR_H(n) (0x100 + n * 0x80)
#define CSI_REG_IMG_RGBY_BADDR_L(n) (0x104 + n * 0x80)
#define CSI_REG_IMG_U_BADDR_H(n) (0x108 + n * 0x80)
#define CSI_REG_IMG_U_BADDR_L(n) (0x10c + n * 0x80)
#define CSI_REG_IMG_V_BADDR_H(n) (0x110 + n * 0x80)
#define CSI_REG_IMG_V_BADDR_L(n) (0x114 + n * 0x80)
#define CSI_REG_IMG_RGBY_STRIDE(n) (0x118 + n * 0x80)
#define CSI_REG_IMG_U_STRIDE(n) (0x11c + n * 0x80)
#define CSI_REG_IMG_V_STRIDE(n) (0x120 + n * 0x80)
#define CSI_REG_IMG_SIZE(n) (0x124 + n * 0x80)
#define CSI_REG_IMG_IPI_CTRL(n) (0x128 + n * 0x80)
#define CSI_REG_IMG_IF_PIXEL_MASK0(n) (0x12c + n * 0x80)
#define CSI_REG_IMG_IF_PIXEL_MASK1(n) (0x130 + n * 0x80)
#define CSI_REG_IMG_CHN_CTRL(n) (0x134 + n * 0x80)
#define CSI_REG_IMG_CHN_SPLIT0(n) (0x138 + n * 0x80)
#define CSI_REG_IMG_CHN_SPLIT1(n) (0x13c + n * 0x80)
#define CSI_REG_IMG_CHN_SPLIT2(n) (0x140 + n * 0x80)
#define CSI_REG_IMG_CHN_CROP0(n) (0x144 + n * 0x80)
#define CSI_REG_IMG_CHN_CROP1(n) (0x148 + n * 0x80)
#define CSI_REG_IMG_CHN_CROP2(n) (0x14c + n * 0x80)
#define CSI_REG_IMG_CHN_PACK0(n) (0x150 + n * 0x80)
#define CSI_REG_IMG_CHN_PACK1(n) (0x154 + n * 0x80)
#define CSI_REG_IMG_CHN_PACK2(n) (0x158 + n * 0x80)
#define CSI_REG_IMG_CHN_VCROP0(n) (0x15c + n * 0x80)
/* n: 0 ~ 7, indicates wdma output channel, max 2 channels for each image */
#define CSI_REG_WDMA_CHN_DFIFO(n) (0x300 + n * 0x20)
#define CSI_REG_WDMA_CHN_CFIFO(n) (0x304 + n * 0x20)
#define CSI_REG_WDMA_CHN_AXI_CTRL0(n) (0x308 + n * 0x20)
#define CSI_REG_WDMA_CHN_AXI_CTRL1(n) (0x30c + n * 0x20)
#define CSI_REG_WDMA_CHN_AXI_CTRL2(n) (0x310 + n * 0x20)
#define CSI_REG_WDMA_CHN_STATE(n) (0x314 + n * 0x20)
/* n: 0 ~ 3, indicates image channel.
* Actually 1~3 will never be used because only image0 can get input from
* outside of SOC
*/
#define CSI_REG_PARA_BT_CTRL0(n) (0x500 + n * 0x40)
#define CSI_REG_PARA_BT_CTRL1(n) (0x504 + n * 0x40)
#define CSI_REG_PARA_BT_CTRL2(n) (0x508 + n * 0x40)
#define CSI_REG_PIXEL_MAP(n) (0x50C + n * 0x40)
/* n: 0 ~ 1, indicates image channel. */
#define CSI_REG_IMG_FB_CTRL(n) (0x800 + n * 0x80)
#define CSI_REG_IMG_UP_RGBY_ADDR(n) (0x804 + n * 0x80)
#define CSI_REG_IMG_LOW_RGBY_ADDR(n) (0x808 + n * 0x80)
#define CSI_REG_IMG_UP_U_ADDR(n) (0x810 + n * 0x80)
#define CSI_REG_IMG_LOW_U_ADDR(n) (0x814 + n * 0x80)
#define CSI_REG_IMG_UP_V_ADDR(n) (0x818 + n * 0x80)
#define CSI_REG_IMG_LOW_V_ADDR(n) (0x81c + n * 0x80)
enum csi_int0_status {
CSI_INT0_STORE_DONE0 = (1 << 0),
CSI_INT0_STORE_DONE1 = (1 << 1),
CSI_INT0_STORE_DONE2 = (1 << 2),
CSI_INT0_STORE_DONE3 = (1 << 3),
CSI_INT0_SHADOW_SET0 = (1 << 4),
CSI_INT0_SHADOW_SET1 = (1 << 5),
CSI_INT0_SHADOW_SET2 = (1 << 6),
CSI_INT0_SHADOW_SET3 = (1 << 7),
CSI_INT0_BT_ERR = (1 << 8),
CSI_INT0_BT_FATAL = (1 << 9),
CSI_INT0_BT_FIELD_CHANGE = (1 << 10),
};
enum csi_int1_status {
CSI_INT1_CROP_ERR0 = (1 << 0),
CSI_INT1_CROP_ERR1 = (1 << 1),
CSI_INT1_CROP_ERR2 = (1 << 2),
CSI_INT1_CROP_ERR3 = (1 << 3),
CSI_INT1_PIXEL_ERR0 = (1 << 4),
CSI_INT1_PIXEL_ERR1 = (1 << 5),
CSI_INT1_PIXEL_ERR2 = (1 << 6),
CSI_INT1_PIXEL_ERR3 = (1 << 7),
CSI_INT1_OVERFLOW0 = (1 << 8),
CSI_INT1_OVERFLOW1 = (1 << 9),
CSI_INT1_OVERFLOW2 = (1 << 10),
CSI_INT1_OVERFLOW3 = (1 << 11),
CSI_INT1_IMG0_BUSERR0 = (1 << 12),
CSI_INT1_IMG0_BUSERR1 = (1 << 13),
CSI_INT1_IMG0_BUSERR2 = (1 << 14),
CSI_INT1_IMG1_BUSERR0 = (1 << 15),
CSI_INT1_IMG1_BUSERR1 = (1 << 16),
CSI_INT1_IMG1_BUSERR2 = (1 << 17),
CSI_INT1_IMG2_BUSERR0 = (1 << 18),
CSI_INT1_IMG2_BUSERR1 = (1 << 19),
CSI_INT1_IMG2_BUSERR2 = (1 << 20),
CSI_INT1_IMG3_BUSERR0 = (1 << 21),
CSI_INT1_IMG3_BUSERR1 = (1 << 22),
CSI_INT1_IMG3_BUSERR2 = (1 << 23),
};
#define INT0_ERR_MASK(n) ((CSI_INT0_BT_ERR << n) | (CSI_INT0_BT_FATAL << 0))
#define INT0_MASK(n) (CSI_INT0_SHADOW_SET0 << n)
#define INT0_BT_MASK(n) (INT0_ERR_MASK(n) | (CSI_INT0_SHADOW_SET0 << n))
#define INT0_ERR_MASK_ALL \
(INT0_ERR_MASK(0) | INT0_ERR_MASK(1) | INT0_ERR_MASK(2) | INT0_ERR_MASK(3))
#define INT1_ERR_MASK(n) \
(((CSI_INT1_CROP_ERR0 | CSI_INT1_PIXEL_ERR0 | CSI_INT1_OVERFLOW0) << n) | \
(CSI_INT1_IMG0_BUSERR0 << (n * 3)))
#define INT1_ERR_MASK_ALL \
(INT1_ERR_MASK(0) | INT1_ERR_MASK(1) | INT1_ERR_MASK(2) | INT1_ERR_MASK(3))
#endif /* __CSI_REG_H__ */