移动了文件位置

This commit is contained in:
2026-04-18 09:16:58 +08:00
parent 2b49081589
commit 1575212f13
10236 changed files with 72 additions and 1436438 deletions

View File

@@ -0,0 +1,10 @@
For FreeRTOS
cam_hal: as android HAL
csi_drv: csi driver
excam: sensor,serdes,..., driver
20220908
demo path:
boards\e3_display\app_demo\FreeRTOS-cam_pre\IAR

View File

@@ -0,0 +1,409 @@
/*
* cam_hal.c
*
* Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*
* Description: implement the camera interface for application user
*
* Revision History:
* -----------------
*/
#include "camera/cam_hal.h"
#include "../csi_drv/csi_drv.h"
#include "../excam/exvdev.h"
/* board.c should call board_camera_init() to reconfig this */
struct camera_res_cfg g_cam_cfg[CAMERA_NUM] = {
{{0, 0, 0, 0}, 0, 0, NULL, NULL},
{{0, 0, 0, 0}, 0, 0, NULL, NULL},
{{0, 0, 0, 0}, 0, 0, NULL, NULL},
};
static camera_handle camera_handles[CAMERA_NUM] = {
[CAMERA_0] =
{
.cam_id = CAMERA_0,
.csi_id = 0,
.ch_id = 0,
.name = "camera0",
.res_cfg = (void *)&g_cam_cfg[CAMERA_0],
},
[CAMERA_1] =
{
.cam_id = CAMERA_1,
.csi_id = 0,
.ch_id = 0,
.name = "camera1",
.res_cfg = (void *)&g_cam_cfg[CAMERA_1],
},
[CAMERA_2] =
{
.cam_id = CAMERA_2,
.csi_id = 0,
.ch_id = 1,
.name = "camera2",
.res_cfg = (void *)&g_cam_cfg[CAMERA_2],
},
};
/**
* @brief Get a camera instance by cam id.
*/
camera_handle *hal_cam_get_instance(enum camera_id cam_id)
{
camera_handle *cur;
if (cam_id >= CAMERA_NUM) {
HAL_LOGE("cam_hal: error cam id %d\n", cam_id);
return NULL;
}
cur = &camera_handles[cam_id];
HAL_LOGD("cam_hal: get cam%d, ptr %p, before usrs %d\n", cam_id, cur,
cur->usr_cnt);
if (atomic_add(&cur->usr_cnt, 1) > 1) {
atomic_add(&cur->usr_cnt, -1);
HAL_LOGE("cam_hal: fail to get cam id %d, already in use\n", cam_id);
return NULL;
}
HAL_LOGI("cam_hal: get cam%d, ptr %p, usrs %d\n", cam_id, cur,
cur->usr_cnt);
return cur;
}
int hal_cam_put_instance(camera_handle *cam_handle)
{
if (cam_handle == NULL || cam_handle->cam_id >= CAMERA_NUM) {
HAL_LOGE("cam_hal: put invalid camera %p\n", cam_handle);
return 0;
}
HAL_LOGD("cam_hal: release usrs %d\n", cam_handle->usr_cnt);
atomic_add(&cam_handle->usr_cnt, -1);
HAL_LOGI("cam_hal: release cam%d, ptr %p, usrs %d\n", cam_handle->cam_id,
cam_handle, cam_handle->usr_cnt);
return 0;
}
int hal_cam_init(camera_handle *cam_handle)
{
int ret = 0;
const struct external_camera_api *excam_api = NULL;
struct vdev_device *vdev = NULL;
struct csi_device *csi_dev = NULL;
struct csi_ch_params param;
struct camera_res_cfg *cfg;
if (cam_handle == NULL || cam_handle->cam_id >= CAMERA_NUM) {
HAL_LOGE("cam_hal: fatal, null cam_handle\n");
return -1;
}
/* camera_dev_init is global for csi dev. DO NOT need deinit */
csi_dev = camera_dev_init(cam_handle->csi_id);
if (csi_dev == NULL) {
HAL_LOGE("cam_hal: cam%d fail to init csi %d\n", cam_handle->cam_id,
cam_handle->csi_id);
return ret;
}
cfg = (struct camera_res_cfg *)cam_handle->res_cfg;
HAL_LOGI("cam_hal: cam%d i2c_res 0x%08x handle %p, name %p\n",
cam_handle->cam_id, cfg->i2c_res, cfg->i2c_handle, cfg->name);
if (cfg->name == NULL) {
HAL_LOGE("cam_hal: cam%d fail to get cam name\n", cam_handle->cam_id);
return -1;
}
excam_api = get_excam_api(cfg->name);
if (excam_api == NULL) {
HAL_LOGE("cam_hal: cam%d fail to get external camera api for %s\n",
cam_handle->cam_id, cfg->name);
return -1;
}
vdev = excam_api->init(cfg);
if (vdev == NULL) {
HAL_LOGE("cam_hal: fail to init vdev, name %s\n", cfg->name);
return -1;
}
ret = vdev->ops.s_power(vdev, 1);
if (ret) {
HAL_LOGE("cam_hal: fail to power on vdev %s\n", cfg->name);
goto pw_err;
}
ret = csi_dev->ops.channel_open(cam_handle->csi_id, cam_handle->ch_id);
if (ret < 0) {
HAL_LOGE("cam_hal: fail to open csi %d ch %d\n", cam_handle->csi_id,
cam_handle->ch_id);
goto open_err;
}
param.bus_type = vdev->ep.bus_type;
ret = csi_dev->ops.channel_cfg_bus_type(cam_handle->csi_id,
cam_handle->ch_id, &param);
if (ret < 0) {
HAL_LOGE("cam_hal: fail to cfg bus_type, csi %d ch %d\n",
cam_handle->csi_id, cam_handle->ch_id);
goto cfg_err;
}
cam_handle->vdev = (void *)vdev;
cam_handle->csi_dev = (void *)csi_dev;
#if CONFIG_CSI_IOMUX
/* for E3_BGA324 display board only */
if ((cam_handle->cam_id == CAMERA_0) || (cam_handle->cam_id == CAMERA_1))
csi_iomux_cfg(cam_handle->cam_id);
#endif
HAL_LOGI("cam_hal: cam%d init done. vdev %s\n", cam_handle->cam_id,
cfg->name);
return 0;
cfg_err:
csi_dev->ops.channel_close(cam_handle->csi_id, cam_handle->ch_id);
open_err:
vdev->ops.s_power(vdev, 0);
pw_err:
vdev->ops.close(vdev);
cam_handle->vdev = NULL;
HAL_LOGE("cam_hal: cam%d init failed. vdev %s\n", cam_handle->cam_id,
cfg->name);
return -1;
}
int hal_cam_deinit(camera_handle *cam_handle)
{
struct vdev_device *vdev = (struct vdev_device *)cam_handle->vdev;
struct csi_device *csi_dev = (struct csi_device *)cam_handle->csi_dev;
vdev->ops.s_power(vdev, 0);
vdev->ops.close(vdev);
cam_handle->vdev = NULL;
csi_dev->ops.channel_close(cam_handle->csi_id, cam_handle->ch_id);
cam_handle->csi_dev = NULL;
HAL_LOGI("cam_hal: cam%d deinit done\n", cam_handle->cam_id);
return 0;
}
int hal_cam_get_size(camera_handle *cam_handle, uint32_t *width,
uint32_t *height)
{
int ret = 0;
struct vdev_device *vdev = (struct vdev_device *)cam_handle->vdev;
struct csi_device *csi_dev = (struct csi_device *)cam_handle->csi_dev;
struct csi_ch_params param;
struct vdev_mbus_framefmt fmt;
memset(&fmt, 0, sizeof(struct vdev_mbus_framefmt));
ret = vdev->ops.get_fmt(vdev, &fmt);
param.field_type = fmt.field;
if (fmt.field != VDEV_FIELD_INTERLACED) {
param.width = fmt.width;
param.height = fmt.height;
} else {
param.width = fmt.field_w[0];
param.height = fmt.field_h[0];
param.width_even = fmt.field_w[1];
param.height_even = fmt.field_h[1];
}
*width = fmt.width;
*height = fmt.height;
HAL_LOGI("cam_hal: cam %d get size %d %d\n", cam_handle->cam_id, fmt.width,
fmt.height);
ret = csi_dev->ops.channel_cfg_size(cam_handle->csi_id, cam_handle->ch_id,
&param);
return ret;
}
int hal_cam_enum_format(camera_handle *cam_handle, uint32_t *format)
{
int ret = 0;
struct vdev_device *vdev = (struct vdev_device *)cam_handle->vdev;
struct csi_device *csi_dev = (struct csi_device *)cam_handle->csi_dev;
struct csi_ch_params param;
struct vdev_mbus_framefmt fmt;
ret = vdev->ops.get_fmt(vdev, &fmt);
param.bus_fmt = fmt.code;
ret = csi_dev->ops.channel_cfg_bus_fmt(cam_handle->csi_id,
cam_handle->ch_id, &param);
ret = csi_dev->ops.channel_enum_pixfmt(cam_handle->csi_id,
cam_handle->ch_id, &param);
if (param.pix_fmt_num == 0) {
HAL_LOGE("cam_hal: cam %d fail to get fmt\n", cam_handle->cam_id);
return -1;
} else if (param.pix_fmt_num > MAX_PIXFMT_COUNT) {
param.pix_fmt_num = MAX_PIXFMT_COUNT;
}
param.set_pix_fmt = param.pix_fmt[0];
for (uint32_t i = 0; i < param.pix_fmt_num; i++) {
format[i] = param.pix_fmt[i];
}
ret = csi_dev->ops.channel_cfg_pixfmt(cam_handle->csi_id, cam_handle->ch_id,
&param);
HAL_LOGI("%s: set fmt %d\n", __func__, param.set_pix_fmt);
*format = param.set_pix_fmt;
return ret;
}
int hal_cam_set_format(camera_handle *cam_handle, uint32_t format)
{
int ret = 0, support = 0;
struct vdev_device *vdev = (struct vdev_device *)cam_handle->vdev;
struct csi_device *csi_dev = (struct csi_device *)cam_handle->csi_dev;
struct csi_ch_params param;
struct vdev_mbus_framefmt fmt;
ret = vdev->ops.get_fmt(vdev, &fmt);
param.bus_fmt = fmt.code;
ret = csi_dev->ops.channel_cfg_bus_fmt(cam_handle->csi_id,
cam_handle->ch_id, &param);
ret = csi_dev->ops.channel_enum_pixfmt(cam_handle->csi_id,
cam_handle->ch_id, &param);
if (param.pix_fmt_num == 0) {
HAL_LOGE("cam_hal: cam %d fail to get fmt\n", cam_handle->cam_id);
return -1;
}
for (uint32_t i = 0; i < param.pix_fmt_num; i++) {
if (format == param.pix_fmt[i]) {
support = 1;
break;
}
}
if (support == 0) {
HAL_LOGE("cam_hal: cam %d does not support fmt %d\n",
cam_handle->cam_id, format);
return -1;
}
param.set_pix_fmt = format;
ret = csi_dev->ops.channel_cfg_pixfmt(cam_handle->csi_id, cam_handle->ch_id,
&param);
HAL_LOGI("%s: set fmt %d done\n", __func__, param.set_pix_fmt);
return ret;
}
int hal_cam_get_fps(camera_handle *cam_handle, uint32_t *fps)
{
int ret = 0;
struct vdev_device *vdev = (struct vdev_device *)cam_handle->vdev;
struct vdev_fract fract;
*fps = 0;
if (vdev->ops.g_frame_interval) {
ret = vdev->ops.g_frame_interval(vdev, &fract);
*fps = fract.denominator;
}
return ret;
}
int hal_cam_set_crop(camera_handle *cam_handle, uint32_t x[2], uint32_t y[2],
uint32_t w[2], uint32_t h[2])
{
int ret = 0;
struct csi_device *csi_dev = (struct csi_device *)cam_handle->csi_dev;
struct csi_ch_params param;
param.crop_x = x[0];
param.crop_y = y[0];
param.crop_w = w[0];
param.crop_h = h[0];
ret = csi_dev->ops.channel_cfg_crop(cam_handle->csi_id, cam_handle->ch_id,
&param);
if (ret) {
HAL_LOGE("cam_hal: cam %d fail to cfg crop\n", cam_handle->cam_id);
}
return ret;
}
int hal_cam_set_memrange(camera_handle *cam_handle, uint32_t low[3],
uint32_t up[3])
{
int ret = 0;
struct csi_device *csi_dev = (struct csi_device *)cam_handle->csi_dev;
ret = csi_dev->ops.channel_cfg_memrange(cam_handle->csi_id,
cam_handle->ch_id, low, up);
if (ret) {
HAL_LOGE("cam_hal: cam %d fail to cfg memrange\n", cam_handle->cam_id);
}
return ret;
}
int hal_cam_qbuf(camera_handle *cam_handle, struct vdev_buffer *buf)
{
int ret = 0;
struct csi_device *csi_dev = (struct csi_device *)cam_handle->csi_dev;
ret = csi_dev->ops.channel_qbuf(cam_handle->csi_id, cam_handle->ch_id, buf);
if (ret) {
HAL_LOGE("cam_hal: cam %d fail to push buffer\n", cam_handle->cam_id);
}
return ret;
}
int hal_cam_dqbuf(camera_handle *cam_handle, struct vdev_buffer *buf)
{
int ret = 0;
struct csi_device *csi_dev = (struct csi_device *)cam_handle->csi_dev;
ret =
csi_dev->ops.channel_dqbuf(cam_handle->csi_id, cam_handle->ch_id, buf);
if (ret) {
HAL_LOGE("cam_hal: cam %d fail to pop buffer\n", cam_handle->cam_id);
}
return ret;
}
int hal_cam_start(camera_handle *cam_handle)
{
int ret = 0;
struct vdev_device *vdev = (struct vdev_device *)cam_handle->vdev;
struct csi_device *csi_dev = (struct csi_device *)cam_handle->csi_dev;
ret = csi_dev->ops.channel_stream(cam_handle->csi_id, cam_handle->ch_id, 1);
if (ret) {
HAL_LOGE("cam_hal: cam %d fail to stream on csi\n", cam_handle->cam_id);
return -1;
}
ret = vdev->ops.s_stream(vdev, 1);
if (ret) {
HAL_LOGE("cam_hal: cam %d fail to stream on vdev\n",
cam_handle->cam_id);
csi_dev->ops.channel_stream(cam_handle->csi_id, cam_handle->ch_id, 0);
return -1;
}
HAL_LOGI("cam_hal: cam %d start done\n", cam_handle->cam_id);
return ret;
}
int hal_cam_stop(camera_handle *cam_handle)
{
struct vdev_device *vdev = (struct vdev_device *)cam_handle->vdev;
struct csi_device *csi_dev = (struct csi_device *)cam_handle->csi_dev;
vdev->ops.s_stream(vdev, 0);
csi_dev->ops.channel_stream(cam_handle->csi_id, cam_handle->ch_id, 0);
HAL_LOGI("cam_hal: cam %d stop done\n", cam_handle->cam_id);
return 0;
}

View File

@@ -0,0 +1,890 @@
/*
* csi_drv.c
*
* Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*
* Description: csi controller driver (system adaptive)
*
* Revision History:
* -----------------
*/
#include "camera/sdrv-cam-os-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,196 @@
/*
* 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 "camera/sdrv-cam-os-def.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__ */

View File

@@ -0,0 +1,349 @@
/*
* Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#include "ex_max96706.h"
/* ========================================================================================================================= */
#define max9276_DEVICE_ID (0x90U)
#define max9276_DESER_ADDR (0x48U)
enum max9276_status {
DESER_IDLE,
DESER_STREAM_ON,
};
struct max9276_dev {
struct vdev_device vdev;
void *i2c_handle;
uint8_t deser_addr;
uint32_t gpio_pwn;
uint32_t gpio_gpi;
uint32_t gpio_poc;
void *port_handle;
void *dio_handle;
uint32_t status;
};
/* ========================================================================================================================= */
static int max9276_write_reg(struct max9276_dev *deser, uint8_t reg,
uint8_t val)
{
uint8_t buf[2];
int ret = 0;
deser_info("%s: addr=0x%x, reg=0x%x, val=0x%x\r\n", __func__,
deser->deser_addr, reg, val);
buf[0] = reg;
buf[1] = val;
ret = i2c_write(deser->i2c_handle, deser->deser_addr, buf, 2);
if (ret < 0) {
deser_err("%s: error: reg=0x%x, val=%x\r\n", __func__, reg, val);
return ret;
}
return ret;
}
/* ========================================================================================================================= */
static int max9276_read_reg(struct max9276_dev *deser, uint8_t reg,
uint8_t *val)
{
uint8_t buf[1];
int ret = 0;
memset(buf, 0, sizeof(buf));
ret = i2c_write_read(deser->i2c_handle, deser->deser_addr, &reg, 1, buf, 1);
if (ret < 0) {
deser_err("%s: error: read reg=0x%x\r\n", __func__, reg);
return ret;
}
deser_info("%s: addr=0x%x, reg=0x%x, val=0x%x\r\n", __func__,
deser->deser_addr, reg, buf[0]);
*val = buf[0];
return 0;
}
static int max9276_check_chip_id(struct max9276_dev *deser)
{
int ret;
int i = 0;
uint8_t chip_id = 0;
deser_info("%s\r\n", __func__);
while (i++ < 32) {
deser_err("%s: slave addr %x\r\n", __func__, deser->deser_addr);
ret = max9276_read_reg(deser, 0x01, &chip_id);
if (ret) {
deser_err("%s: failed to read chip identifier\r\n", __func__);
continue;
}
udelay(10000); // 10ms
if (chip_id != max9276_DEVICE_ID) {
deser_err("%s: wrong chip identifier, expected 0x%x(max9276), got "
"0x%x\r\n",
__func__, max9276_DEVICE_ID, chip_id);
continue;
} else {
deser_info(
"%s: chip identifier, expected 0x%x(max9276), got 0x%x\r\n",
__func__, max9276_DEVICE_ID, chip_id);
break;
}
}
return 0;
}
static int max9276_init_setup(struct max9276_dev *deser)
{
int i = 0;
uint8_t reg;
/* Add Init Setting Here */
max9276_write_reg(deser, 0x04, 0x00);
udelay(1000); // 1ms
max9276_write_reg(deser, 0x02, 0x0F);
udelay(1000); // 1ms
max9276_write_reg(deser, 0x05, 0x05);
udelay(1000); // 1ms
max9276_write_reg(deser, 0x0C, 0xFF);
udelay(1000); // 1ms
max9276_write_reg(deser, 0x11, 0xA2);
udelay(1000); // 1ms
max9276_write_reg(deser, 0x14, 0x05);
udelay(1000); // 1ms
max9276_write_reg(deser, 0x15, 0xF1);
udelay(1000); // 1ms
max9276_write_reg(deser, 0x16, 0xDA);
udelay(1000); // 1ms
max9276_write_reg(deser, 0x04, 0x03);
udelay(1000); // 1ms
/*
for (reg = 0x00; reg < 0x20; reg++) {
uint8_t val = 0;
max9276_read_reg(deser, reg, &val);
ssdk_printf(SSDK_INFO, "reg 0x%02x: 0x%02x", reg, val);
}
*/
for (i = 0; i < 500; i++) {
uint8_t val;
max9276_read_reg(deser, 0x04, &val);
if (val >> 7) {
ssdk_printf(SSDK_INFO, "max9276 locked done: %d\r\n", i);
break;
}
ssdk_printf(SSDK_INFO, "max9276 try lock for %d\r\n", i+1);
udelay(2000); // 2ms
}
return 0;
}
static int max9276_s_power(struct vdev_device *vdev, int enable)
{
return 0;
}
static int max9276_enum_mbus_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fme)
{
struct max9276_dev *deser;
if ((vdev == NULL) || (fme == NULL)) {
return -1;
}
deser = (struct max9276_dev *)vdev->priv_data;
if (fme->index > 0) {
fme->index = 0;
return -1;
}
fme->code = deser->vdev.fmt.code;
fme->field = deser->vdev.fmt.field;
fme->colorspace = deser->vdev.fmt.colorspace;
return 0;
}
static int max9276_enum_frame_size(struct vdev_device *vdev,
struct vdev_frame_size_enum *fse)
{
struct max9276_dev *deser;
if (!vdev || !fse) {
return -1;
}
deser = (struct max9276_dev *)vdev->priv_data;
if (fse->index > 0) {
fse->index = 0;
return -1;
}
fse->min_width = deser->vdev.fmt.width;
fse->max_width = deser->vdev.fmt.width;
fse->min_height = deser->vdev.fmt.height;
fse->max_height = deser->vdev.fmt.height;
return 0;
}
static int max9276_enum_frame_interval(struct vdev_device *vdev,
struct vdev_frame_interval_enum *fie)
{
return 0;
}
static int max9276_get_frame_interval(struct vdev_device *vdev,
struct vdev_fract *fi)
{
if (!vdev || !fi) {
return -1;
}
*fi = vdev->frame_interval;
return 0;
}
static int max9276_set_frame_interval(struct vdev_device *vdev,
struct vdev_fract fi)
{
return 0;
}
static int max9276_get_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fmt)
{
if (!vdev) {
return -1;
}
*fmt = vdev->fmt;
return 0;
}
static int max9276_set_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt fmt)
{
return 0;
}
static int max9276_s_stream(struct vdev_device *vdev, int enable)
{
int ret = 0;
uint8_t val;
struct max9276_dev *deser;
if (!vdev) {
return -1;
}
deser = (struct max9276_dev *)vdev->priv_data;
if (enable == 1) {
if (deser->status == DESER_STREAM_ON) {
deser_info("%s: already stream on\r\n", __func__);
goto exit;
}
ret = max9276_init_setup(deser);
if (ret < 0)
goto exit;
deser->status = DESER_STREAM_ON;
} else {
max9276_read_reg(deser, 0x04, &val);
val |= 0x40;
max9276_write_reg(deser, 0x04, val);
usleep_range(1000, 1100);
deser->status = DESER_IDLE;
}
exit:
return ret;
}
/* ========================================================================================================================= */
static int max9276_deinit(struct vdev_device *vdev)
{
struct max9276_dev *deser;
if (!vdev) {
return -1;
}
deser = (struct max9276_dev *)vdev->priv_data;
return 0;
}
/* ========================================================================================================================= */
static const struct vdev_dev_ops max9276_vdev_ops = {
.s_power = max9276_s_power,
.enum_mbus_fmt = max9276_enum_mbus_fmt,
.enum_frame_size = max9276_enum_frame_size,
.enum_frame_interval = max9276_enum_frame_interval,
.g_frame_interval = max9276_get_frame_interval,
.s_frame_interval = max9276_set_frame_interval,
.get_fmt = max9276_get_fmt,
.set_fmt = max9276_set_fmt,
.s_stream = max9276_s_stream,
.close = max9276_deinit,
};
/* ========================================================================================================================= */
struct vdev_device *max9276_init(void *init_data)
{
int ret;
struct max9276_dev *deser;
struct camera_res_cfg *cfg = (struct camera_res_cfg *)init_data;
deser = pvPortMalloc(sizeof(struct max9276_dev));
if (!deser || !cfg || !cfg->i2c_handle)
return NULL;
memset(deser, 0, sizeof(struct max9276_dev));
deser->i2c_handle = cfg->i2c_handle;
deser->deser_addr = max9276_DESER_ADDR;
deser->gpio_pwn = cfg->gpio[0];
deser->gpio_gpi = cfg->gpio[1];
deser->gpio_poc = cfg->gpio[2];
deser->vdev.priv_data = (void *)deser;
max9276_s_power(&deser->vdev, 1);
ret = max9276_check_chip_id(deser);
max9276_s_power(&deser->vdev, 0);
deser->vdev.ep.bus_type = VDEV_MBUS_PARALLEL;
deser->vdev.frame_interval.numerator = 1;
deser->vdev.frame_interval.denominator = 60;
deser->vdev.fmt.width = 1920;
deser->vdev.fmt.height = 720;
deser->vdev.fmt.code = VDEV_MBUS_FMT_BGR24;
deser->vdev.fmt.field = VDEV_FIELD_NONE;
deser->vdev.fmt.colorspace = VDEV_COLORSPACE_SRGB;
deser->vdev.ops = max9276_vdev_ops;
return &deser->vdev;
}

View File

@@ -0,0 +1,15 @@
/* ============================================================================================================================
** (C) Copyright 2022 Kotei
** Author: Luo Yajun
** Date: 2022/9/29
============================================================================================================================ */
#ifndef EX_MAX9276_H
#define EX_MAX9276_H
/* ========================================================================================================================= */
#include "exvdev.h"
/* ========================================================================================================================= */
struct vdev_device *max9276_init(void *init_data);
#endif /* EX_MAX9276_H */

View File

@@ -0,0 +1,604 @@
/*
* Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#include "camera/sdrv-cam-os-def.h"
#include "ex_max96706.h"
#define MAX96706_DEVICE_ID 0x4A
#define MAX96705_DEVICE_ID 0x41
#define MAX96706_DESER_ADDR 0x78
#define MAX96705_SERER_ADDR 0x40
#define MAX96705_ISP_ADDR 0xA8
#define TCA9539_ADDR 0x74
enum max96706_status {
DESER_IDLE,
DESER_STREAM_ON,
};
struct max96706_dev {
struct vdev_device vdev;
void *i2c_handle;
uint8_t deser_addr;
uint8_t serer_addr;
uint8_t isp_addr;
uint8_t gpioext_addr;
uint32_t gpio_pwn;
uint32_t gpio_gpi;
uint32_t gpio_poc;
void *port_handle;
void *dio_handle;
osMutexId_t lock;
uint32_t status;
};
static int max96706_poc_power(struct max96706_dev *deser, int enable);
static int max96706_write_reg(struct max96706_dev *deser, uint8_t reg,
uint8_t val)
{
uint8_t buf[2];
int ret = 0;
deser_info("%s: addr=0x%x, reg=0x%x, val=0x%x\n", __func__,
deser->deser_addr, reg, val);
buf[0] = reg;
buf[1] = val;
ret = i2c_write(deser->i2c_handle, deser->deser_addr, buf, 2);
if (ret < 0) {
deser_err("%s: error: reg=0x%x, val=%x\n", __func__, reg, val);
return ret;
}
return ret;
}
static int max96706_read_reg(struct max96706_dev *deser, uint8_t reg,
uint8_t *val)
{
uint8_t buf[1];
int ret = 0;
memset(buf, 0, sizeof(buf));
ret = i2c_write_read(deser->i2c_handle, deser->deser_addr, &reg, 1, buf, 1);
if (ret < 0) {
deser_err("%s: error: read reg=0x%x\n", __func__, reg);
return ret;
}
deser_info("%s: addr=0x%x, reg=0x%x, val=0x%x\n", __func__,
deser->deser_addr, reg, buf[0]);
*val = buf[0];
return 0;
}
static int max96705_write_reg(struct max96706_dev *deser, uint8_t reg,
uint8_t val)
{
uint8_t buf[2];
int ret = 0;
deser_info("%s: addr=0x%x, reg=0x%x, val=0x%x\n", __func__,
deser->serer_addr, reg, val);
buf[0] = reg;
buf[1] = val;
ret = i2c_write(deser->i2c_handle, deser->serer_addr, buf, 2);
if (ret < 0) {
deser_err("%s: error: reg=0x%x, val=%x\n", __func__, reg, val);
return ret;
}
return ret;
}
static int max96705_read_reg(struct max96706_dev *deser, uint8_t reg,
uint8_t *val)
{
uint8_t buf[1];
int ret = 0;
memset(buf, 0, sizeof(buf));
ret = i2c_write_read(deser->i2c_handle, deser->serer_addr, &reg, 1, buf, 1);
if (ret < 0) {
deser_err("%s: error: read reg=0x%x\n", __func__, reg);
return ret;
}
deser_info("%s: addr=0x%x, reg=0x%x, val=0x%x\n", __func__,
deser->serer_addr, reg, buf[0]);
*val = buf[0];
return 0;
}
static int max96706_check_chip_id(struct max96706_dev *deser)
{
int ret;
int i = 0;
uint8_t chip_id = 0;
deser_info("%s\n", __func__);
while (i++ < 30) {
ret = max96706_read_reg(deser, 0x1E, &chip_id);
if (ret) {
deser_err("%s: failed to read chip identifier\n", __func__);
continue;
}
if (chip_id != MAX96706_DEVICE_ID) {
deser_err("%s: wrong chip identifier, expected 0x%x(max96706), got "
"0x%x\n",
__func__, MAX96706_DEVICE_ID, chip_id);
continue;
} else {
deser_info(
"%s: chip identifier, expected 0x%x(max96706), got 0x%x\n",
__func__, MAX96706_DEVICE_ID, chip_id);
break;
}
}
return 0;
}
static int max96705_check_chip_id(struct max96706_dev *deser)
{
int ret;
int i = 0;
uint8_t chip_id = 0;
deser_info("%s\n", __func__);
while (i++ < 30) {
ret = max96705_read_reg(deser, 0x1E, &chip_id);
if (ret) {
deser_err("%s: failed to read chip identifier\n", __func__);
continue;
}
if (chip_id != MAX96705_DEVICE_ID) {
deser_err("%s: wrong chip identifier, expected 0x%x(max96705), got "
"0x%x\n",
__func__, MAX96705_DEVICE_ID, chip_id);
continue;
} else {
deser_info(
"%s: chip identifier, expected 0x%x(max96705), got 0x%x\n",
__func__, MAX96705_DEVICE_ID, chip_id);
return 0;
}
}
return -1;
}
static int max96706_init_setup(struct max96706_dev *deser)
{
uint8_t val;
int i, ret;
val = 0;
ret = max96706_read_reg(deser, 0x06, &val);
if (ret < 0) {
deser_err("max96706 fail to read 0x06=%d\n", ret);
return ret;
}
val |= 0x80;
max96706_write_reg(deser, 0x06, val);
usleep_range(1000, 1100);
//[7]dbl, [2]hven, [1]cxtp
val = 0;
max96706_read_reg(deser, 0x07, &val);
deser_info("%s, reg[0x07]=0x%x\n", __func__, val);
if (val != 0x86) {
max96706_write_reg(deser, 0x07, 0x86);
usleep_range(10000, 11000);
}
// invert hsync
val = 0;
max96706_read_reg(deser, 0x02, &val);
val |= 0x80;
max96706_write_reg(deser, 0x02, val);
usleep_range(3000, 3100);
// disable output
val = 0;
max96706_read_reg(deser, 0x04, &val);
deser_info("%s, reg[0x04]=0x%x\n", __func__, val);
val = 0x47;
max96706_write_reg(deser, 0x04, val);
usleep_range(10000, 11000);
max96706_poc_power(deser, 1);
usleep_range(100000, 110000);
// dcs
val = 0;
max96706_read_reg(deser, 0x5, &val);
deser_info("%s, reg[0x05]=0x%x\n", __func__, val);
val |= 0x40;
max96706_write_reg(deser, 0x5, val);
val = 0;
max96706_read_reg(deser, 0x5, &val);
// Retry for i2c address comflict in defaul 7b i2c address 0x40.
for (i = 0; i < 3; i++) {
ret = max96705_check_chip_id(deser);
if (ret < 0) {
deser_crit("%s: times %d not found 96705, ret=%d\n", __func__, i,
ret);
usleep_range(20000, 20002);
} else {
deser_err("%s: found 96705, ret=%d\n", __func__, ret);
break;
}
}
deser_info("%s: check 96705, ret=%d\n", __func__, ret);
if (ret < 0)
return ret;
#ifdef MAX96706_MULTI_SERER
// Change I2C: not required for only one serer.
usleep_range(1000, 2000);
max96705_write_reg(deser, 0x00, deser->serer_addr << 1);
usleep_range(5000, 6000);
max96705_write_reg(deser, 0x01, deser->deser_addr << 1);
usleep_range(10000, 11000);
#endif
if (deser->gpio_gpi) {
deser_err("%s: error: gpi is not supported\n", __func__);
#ifdef MAX96706_GPI_EN
max96706_gpi_power(deser, 9, 1);
usleep_range(100, 200);
max96706_gpi_power(deser, 9, 0);
usleep_range(100, 200);
max96706_gpi_power(deser, 9, 1);
#endif
} else {
val = 0;
max96705_read_reg(deser, 0x0f, &val);
val |= 0x81;
max96705_write_reg(deser, 0x0f, val);
usleep_range(100, 200);
}
// enable dbl, hven
max96705_write_reg(deser, 0x07, 0x84);
usleep_range(200000, 210000);
return 0;
}
#ifdef _CONFIG_OS_SAFETY_
#include "tca9539.h"
// poc-gpios = <&cam_gpios 6 0>;
// Power for camera module
static int max96706_poc_power(struct max96706_dev *deser, int enable)
{
struct tca9539_device *pd;
int port = 7;
deser_info("%s: en %d, port %d\n", __func__, enable, port);
pd = tca9539_init(10, TCA9539_ADDR);
if (pd == NULL) {
deser_err("%s: init tca9359 error!\n", __func__);
return -1;
}
pd->ops.output_enable(pd, port);
thread_sleep(50); // 50 ms
pd->ops.output_val(pd, port, 0);
thread_sleep(50);
if (enable) {
pd->ops.output_val(pd, port, 1);
thread_sleep(100);
}
tca9539_deinit(pd);
return 0;
}
extern const domain_res_t g_gpio_res;
extern const domain_res_t g_iomuxc_res;
// pwdn-gpios = <&port4c 19 0>;
// Power for Rx max96706/712
static int max96706_s_power(struct vdev_device *vdev, int enable)
{
struct max96706_dev *deser;
bool ioret;
void *port_handle;
void *dio_handle = NULL;
int pin_index = PortConf_PIN_OSPI2_DATA1;
const Port_PinModeType MODE_GPIO_OSPI2_DATA1 = {
((uint32_t)PORT_PAD_POE__DISABLE | PORT_PAD_IS__OUT |
PORT_PAD_SR__FAST | PORT_PAD_DS__MID1 | PORT_PIN_IN_NO_PULL),
((uint32_t)PORT_PIN_MUX_FV__MIN | PORT_PIN_MUX_FIN__MIN |
PORT_PIN_OUT_PUSHPULL | PORT_PIN_MODE_GPIO),
};
deser = (struct max96706_dev *)vdev->priv_data;
deser_info("%s: enable=%d.\n", __func__, enable);
/* Port setup */
if (deser->port_handle != NULL)
return 0;
ioret = hal_port_creat_handle(&port_handle, g_iomuxc_res.res_id[0]);
if (ioret == false) {
deser_err("%s: port creat failed.\n", __func__);
return -1;
}
deser_info("%s: port_handle=%p, pin=%d\n", __func__, port_handle,
pin_index);
ioret =
hal_port_set_pin_mode(port_handle, pin_index, MODE_GPIO_OSPI2_DATA1);
hal_port_release_handle(&port_handle);
ioret = hal_dio_creat_handle(&dio_handle, g_gpio_res.res_id[0]);
if (ioret == false) {
deser_err("%s: dio creat failed.\n", __func__);
return -1;
}
deser_info("%s: dio_handle=%p.\n", __func__, dio_handle);
hal_dio_set_channel_direction(dio_handle, pin_index, DIO_CHANNEL_OUT);
hal_dio_write_channel(dio_handle, pin_index, 0);
thread_sleep(10);
if (enable == 1) {
hal_dio_write_channel(dio_handle, pin_index, 1);
thread_sleep(10);
}
hal_dio_release_handle(&dio_handle);
thread_sleep(10);
deser_info("%s: enable=%d. done\n", __func__, enable);
return 0;
}
#else
//extern gpio_dev_t g_gpio_ap;
static int max96706_poc_power(struct max96706_dev *deser, int enable)
{
uint32_t pin = deser->gpio_poc; /* GPIO_D36 */
// gpio_dev_t *gpio_dev = &g_gpio_ap;
deser_info("%s: pin %d, en %d\n", __func__, pin, enable);
gpio_set(NULL, pin, enable ? true : false);
return 0;
}
static int max96706_s_power(struct vdev_device *vdev, int enable) { return 0; }
#endif
static int max96706_enum_mbus_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fme)
{
struct max96706_dev *deser;
if ((vdev == NULL) || (fme == NULL)) {
return -1;
}
deser = (struct max96706_dev *)vdev->priv_data;
if (fme->index > 0) {
fme->index = 0;
return -1;
}
fme->code = deser->vdev.fmt.code;
fme->field = deser->vdev.fmt.field;
fme->colorspace = deser->vdev.fmt.colorspace;
return 0;
}
static int max96706_enum_frame_size(struct vdev_device *vdev,
struct vdev_frame_size_enum *fse)
{
struct max96706_dev *deser;
if (!vdev || !fse) {
return -1;
}
deser = (struct max96706_dev *)vdev->priv_data;
if (fse->index > 0) {
fse->index = 0;
return -1;
}
fse->min_width = deser->vdev.fmt.width;
fse->max_width = deser->vdev.fmt.width;
fse->min_height = deser->vdev.fmt.height;
fse->max_height = deser->vdev.fmt.height;
return 0;
}
static int max96706_enum_frame_interval(struct vdev_device *vdev,
struct vdev_frame_interval_enum *fie)
{
return 0;
}
static int max96706_get_frame_interval(struct vdev_device *vdev,
struct vdev_fract *fi)
{
if (!vdev || !fi) {
return -1;
}
*fi = vdev->frame_interval;
return 0;
}
static int max96706_set_frame_interval(struct vdev_device *vdev,
struct vdev_fract fi)
{
return 0;
}
static int max96706_get_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fmt)
{
if (!vdev) {
return -1;
}
*fmt = vdev->fmt;
return 0;
}
static int max96706_set_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt fmt)
{
return 0;
}
static int max96706_s_stream(struct vdev_device *vdev, int enable)
{
int ret = 0;
uint8_t val;
struct max96706_dev *deser;
if (!vdev) {
return -1;
}
deser = (struct max96706_dev *)vdev->priv_data;
osMutexAcquire(deser->lock, 0);
if (enable == 1) {
if (deser->status == DESER_STREAM_ON) {
deser_info("%s: already stream on\n", __func__);
goto exit;
}
ret = max96706_init_setup(deser);
if (ret < 0)
goto exit;
usleep_range(1000, 1100);
max96706_read_reg(deser, 0x04, &val);
val &= ~(0x40);
max96706_write_reg(deser, 0x04, val);
usleep_range(1000, 1100);
deser->status = DESER_STREAM_ON;
} else {
max96706_poc_power(deser, 0);
max96706_read_reg(deser, 0x04, &val);
val |= 0x40;
max96706_write_reg(deser, 0x04, val);
usleep_range(1000, 1100);
deser->status = DESER_IDLE;
}
exit:
osMutexRelease(deser->lock);
return ret;
}
static int max96706_deinit(struct vdev_device *vdev)
{
struct max96706_dev *deser;
if (!vdev) {
return -1;
}
deser = (struct max96706_dev *)vdev->priv_data;
osMutexDelete(deser->lock);
vPortFree(deser);
return 0;
}
static const struct vdev_dev_ops max96706_vdev_ops = {
.s_power = max96706_s_power,
.enum_mbus_fmt = max96706_enum_mbus_fmt,
.enum_frame_size = max96706_enum_frame_size,
.enum_frame_interval = max96706_enum_frame_interval,
.g_frame_interval = max96706_get_frame_interval,
.s_frame_interval = max96706_set_frame_interval,
.get_fmt = max96706_get_fmt,
.set_fmt = max96706_set_fmt,
.s_stream = max96706_s_stream,
.close = max96706_deinit,
};
struct vdev_device *max96706_init(void *init_data)
{
int ret;
struct max96706_dev *deser;
struct camera_res_cfg *cfg = (struct camera_res_cfg *)init_data;
deser = pvPortMalloc(sizeof(struct max96706_dev));
if (!deser || !cfg || !cfg->i2c_handle)
return NULL;
memset(deser, 0, sizeof(struct max96706_dev));
deser->i2c_handle = cfg->i2c_handle;
deser->deser_addr = MAX96706_DESER_ADDR;
deser->serer_addr = MAX96705_SERER_ADDR;
deser->isp_addr = MAX96705_ISP_ADDR;
deser->gpioext_addr = TCA9539_ADDR;
deser->gpio_pwn = cfg->gpio[0];
deser->gpio_gpi = cfg->gpio[1];
deser->gpio_poc = cfg->gpio[2];
deser->vdev.priv_data = (void *)deser;
max96706_poc_power(deser, 0);
max96706_s_power(&deser->vdev, 1);
ret = max96706_check_chip_id(deser);
if (ret == 0)
max96706_s_stream(&deser->vdev, 0);
max96706_s_power(&deser->vdev, 0);
if (ret < 0)
goto err;
deser->lock = osMutexNew(NULL);
deser->vdev.ep.bus_type = VDEV_MBUS_PARALLEL2;
deser->vdev.frame_interval.numerator = 1;
deser->vdev.frame_interval.denominator = 25;
deser->vdev.fmt.width = 1280;
deser->vdev.fmt.height = 720;
deser->vdev.fmt.code = VDEV_MBUS_FMT_UYVY;
deser->vdev.fmt.field = VDEV_FIELD_NONE;
deser->vdev.fmt.colorspace = VDEV_COLORSPACE_SRGB;
deser->vdev.ops = max96706_vdev_ops;
return &deser->vdev;
err:
vPortFree(deser);
return NULL;
}

View File

@@ -0,0 +1,16 @@
/**
* @file ex_max96706.h
* @brief export header for max96706
*
* @copyright Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*/
#ifndef __EX_MAX96706_H__
#define __EX_MAX96706_H__
#include "exvdev.h"
struct vdev_device *max96706_init(void *init_data);
#endif /* __EX_MAX96706_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
/**
* @file ex_ov5640.h
* @brief export header for external camera sensor ov5640
*
* @copyright Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*/
#ifndef __EX_OV5640_H__
#define __EX_OV5640_H__
#include "exvdev.h"
enum ov5640_mode_id {
OV5640_MODE_QCIF_176_144 = 0,
OV5640_MODE_QVGA_320_240,
OV5640_MODE_VGA_640_480,
OV5640_MODE_NTSC_720_480,
OV5640_MODE_PAL_720_576,
OV5640_MODE_XGA_1024_768,
OV5640_MODE_720P_1280_720,
OV5640_MODE_1080P_1920_1080,
OV5640_MODE_QSXGA_2592_1944,
OV5640_NUM_MODES,
};
enum ov5640_frame_rate {
OV5640_15_FPS = 0,
OV5640_30_FPS,
OV5640_45_FPS,
OV5640_60_FPS,
OV5640_NUM_FRAMERATES,
};
/*
* size_mode: must be enum (ov5640_mode_id)
* fps_mode: must be enum (ov5640_frame_rate)
*/
int ov5640_config_mode(struct vdev_device *vdev, int size_mode, int fps_mode);
struct vdev_device *ex_ov5640_init(void *init_data);
#endif /*__EX_OV5640_H__*/

View File

@@ -0,0 +1,434 @@
/*
* Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#include "ex_tc9591.h"
#include "camera/sdrv-cam-os-def.h"
#define TC9591_DEVICE_ID 0x4401
#define TC9591_DESER_ADDR 0x0E
enum tc9591_status {
DESER_IDLE,
DESER_STREAM_ON,
};
struct tc9591_dev {
struct vdev_device vdev;
void *i2c_handle;
uint8_t deser_addr;
uint32_t gpio_rst;
uint32_t gpio_cam; //vcam 5v
uint32_t gpio_fsin;//for E3 Display 5019 Fsin pin
osMutexId_t lock;
uint32_t status;
};
int test_pattern = 0;
static int tc9591_write_reg(struct tc9591_dev *deser, uint16_t reg, uint16_t val)
{
uint8_t buf[4];
int ret = 0;
deser_debug("%s: addr=0x%x, reg=0x%4x, val=0x%4x\n", __func__,
deser->deser_addr, reg, val);
buf[0] = reg >> 8;
buf[1] = reg & 0xff;
buf[2] = val >> 8;
buf[3] = val & 0xFF;
ret = i2c_write(deser->i2c_handle, deser->deser_addr, buf, 4);
if (ret < 0) {
printf("%s: error: reg=0x%04x, val=%04xx\n", __func__, reg, val);
return ret;
}
return ret;
}
static int tc9591_read_reg(struct tc9591_dev *deser, uint16_t reg16, uint16_t *val)
{
uint8_t buf[2], reg[2];
int ret;
reg[0] = reg16 >> 8;
reg[1] = reg16 & 0xff;
memset(buf, 0, sizeof(buf));
ret = i2c_write_read(deser->i2c_handle, deser->deser_addr, reg, 2, buf, 2);
if (ret < 0) {
printf("%s: error: read reg=0x%04x\n", __func__, reg16);
return ret;
}
//printf("%s: OK: read reg=%x, val=%02x %02x\n", __func__, reg16, buf[0], buf[1]);
*val = (buf[0] << 8);
*val |= buf[1];
return 0;
}
static uint16_t tc9591_reg_list[] = {
0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000c, 0x000e,
0x0010, 0x0012, 0x0014, 0x0016, 0x0018, 0x0020, 0x0022, 0x0032,
0x0050, 0x0056, 0x0058, 0x005a, 0x005c, 0x005e, 0x0060, 0x0062,
0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, 0x0070, 0x0080,
0x0082, 0x0084, 0x0086, 0x0088, 0x008a, 0x008c, 0x008e, 0x0090,
0x00f8,
};
static int read_all_reg(struct tc9591_dev *deser)
{
int i, j;
uint16_t reg[8], val[8];
for (j = 0; j < 40; j+=8) {
for (i = 0; i < 8; i++) {
reg[i] = tc9591_reg_list[j+i];
tc9591_read_reg(deser, reg[i], &val[i]);
usleep_range(10000, 11000);
}
printf("tc9591 read [0x%04x]=0x%04x [0x%04x]=0x%04x [0x%04x]=0x%04x [0x%04x]=0x%04x [0x%04x]=0x%04x [0x%04x]=0x%04x [0x%04x]=0x%04x [0x%04x]=0x%04x\n",
reg[0], val[0], reg[1], val[1], reg[2], val[2], reg[3], val[3],
reg[4], val[4], reg[5], val[5], reg[6], val[6], reg[7], val[7]);
}
return 0;
}
static int tc9591_check_chip_id(struct tc9591_dev *deser)
{
int ret;
int i = 0;
uint16_t chip_id = 0;
printf("%s\n", __func__);
while (i++ < 3) {
ret = tc9591_read_reg(deser, 0x0000, &chip_id);
if (ret) {
printf("%s: failed to read chip identifier\n", __func__);
continue;
}
if (chip_id != TC9591_DEVICE_ID) {
printf("%s: wrong chip identifier, expected 0x%04x(tc9591), got "
"0x%04x\n",
__func__, TC9591_DEVICE_ID, chip_id);
ret = -1;
continue;
} else {
printf("%s: chip identifier, expected 0x%04x(tc9591), got 0x%04x\n",
__func__, TC9591_DEVICE_ID, chip_id);
break;
}
}
if (ret == 0)
read_all_reg(deser);
return ret;
}
static int tc9591_init_setup(struct tc9591_dev *deser)
{
uint16_t val;
int ret;
ret = tc9591_read_reg(deser, 0x0002, &val);
if (ret < 0) {
printf("tc9591 fail to read 0x02, ret=%d\n", ret);
return ret;
}
printf("tc9591 read [0x0002]=0x%04x\n", val);
ret = tc9591_read_reg(deser, 0x0004, &val);
if (ret < 0) {
printf("tc9591 fail to read 0x04, ret=%d\n", ret);
return ret;
}
printf("tc9591 read [0x0004]=0x%04x\n", val);
tc9591_write_reg(deser, 0x0002, 0x0001); // SYSctl, S/W Reset
usleep_range(10000, 11000);
tc9591_write_reg(deser, 0x0002, 0x0000); // SYSctl, S/W Reset release
tc9591_write_reg(deser, 0x0016, 0x205c); // PLL Control Register 0 (PLL_PRD,PLL_FBD)
tc9591_write_reg(deser, 0x0018, 0X0403); // PLL_FRS,PLL_LBWS, PLL oscillation enable
usleep_range(1000, 1100);
tc9591_write_reg(deser, 0x0018, 0X0413); // PLL_FRS,PLL_LBWS, PLL clock out enable
tc9591_write_reg(deser, 0x0020, 0x0011); // CLK control register: Clock divider setting
tc9591_write_reg(deser, 0x000c, 0x101); // MCLK duty setting
#if 1
tc9591_write_reg(deser, 0x0010, 0xFFF9); // GPIO Direction, GPIO2,1 output
tc9591_write_reg(deser, 0x0014, 0x0000); // GPIO output data. GPIO2="L", GPIO1="L"
tc9591_write_reg(deser, 0x000E, 0x0006); // GPIO enable. GPIO2,1 enable
tc9591_write_reg(deser, 0x0014, 0x0006); // GPIO output data. GPIO2="H", GPIO1="H"
#endif
tc9591_write_reg(deser, 0x0060, 0x800f); //PHY timing delay setting
tc9591_write_reg(deser, 0x0006, 0x0032); // FIFO control
tc9591_write_reg(deser, 0x0008, 0x0061); // DATA FORMAT: yuv422-16bit
tc9591_write_reg(deser, 0x0066, 0x00FF); // enable all error
tc9591_write_reg(deser, 0x0004, 0x141);
printf("tc9591 init setup done\n");
return 0;
}
//extern gpio_dev_t g_gpio_safety;
static int tc9591_s_power(struct vdev_device *vdev, int enable)
{
struct tc9591_dev *deser;
uint32_t pin;
if (!vdev) {
return -1;
}
printf("%s %d\r\n", __func__, enable);
deser = (struct tc9591_dev *)vdev->priv_data;
pin = deser->gpio_rst; /* GPIO_B1 */
if(enable) {
if (deser->status == DESER_STREAM_ON) {
printf("%s: already stream on\n", __func__);
goto exit;
}
printf("%s: pin %d, enable %d\n", __func__, pin, enable);
gpio_set(NULL, pin, false); // low level actice
usleep_range(1000, 1000);
gpio_set(NULL, pin, true);
} else {
gpio_set(NULL, pin, false);
}
exit:
return 0;
}
static int tc9591_enum_mbus_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fme)
{
struct tc9591_dev *deser;
if ((vdev == NULL) || (fme == NULL)) {
return -1;
}
deser = (struct tc9591_dev *)vdev->priv_data;
if (fme->index > 0) {
fme->index = 0;
return -1;
}
fme->code = deser->vdev.fmt.code;
fme->field = deser->vdev.fmt.field;
fme->colorspace = deser->vdev.fmt.colorspace;
return 0;
}
static int tc9591_enum_frame_size(struct vdev_device *vdev,
struct vdev_frame_size_enum *fse)
{
struct tc9591_dev *deser;
if (!vdev || !fse) {
return -1;
}
deser = (struct tc9591_dev *)vdev->priv_data;
if (fse->index > 0) {
fse->index = 0;
return -1;
}
fse->min_width = deser->vdev.fmt.width;
fse->max_width = deser->vdev.fmt.width;
fse->min_height = deser->vdev.fmt.height;
fse->max_height = deser->vdev.fmt.height;
return 0;
}
static int tc9591_enum_frame_interval(struct vdev_device *vdev,
struct vdev_frame_interval_enum *fie)
{
return 0;
}
static int tc9591_get_frame_interval(struct vdev_device *vdev,
struct vdev_fract *fi)
{
if (!vdev || !fi) {
return -1;
}
*fi = vdev->frame_interval;
return 0;
}
static int tc9591_set_frame_interval(struct vdev_device *vdev,
struct vdev_fract fi)
{
return 0;
}
static int tc9591_get_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fmt)
{
if (!vdev) {
return -1;
}
*fmt = vdev->fmt;
return 0;
}
static int tc9591_set_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt fmt)
{
return 0;
}
static int tc9591_s_stream(struct vdev_device *vdev, int enable)
{
int ret = 0;
uint32_t cam_pwr = 0;
struct tc9591_dev *deser;
if (!vdev) {
return -1;
}
printf("%s %d\r\n", __func__, enable);
deser = (struct tc9591_dev *)vdev->priv_data;
osMutexAcquire(deser->lock, 0);
cam_pwr = deser->gpio_cam;
if(cam_pwr <=0 ) {
sensor_info("%s get cam pwr pin err\n", __func__);
return -1;
}
if (enable == 1) {
if (deser->status == DESER_STREAM_ON) {
printf("%s: already stream on\n", __func__);
goto exit;
}
gpio_set(NULL, cam_pwr, false);
ret = tc9591_init_setup(deser);
if (ret < 0)
goto exit;
usleep_range(10000,11000);
gpio_set(NULL, cam_pwr, true);
deser->status = DESER_STREAM_ON;
} else {
deser->status = DESER_IDLE;
tc9591_write_reg(deser, 0x0004, 0x0147);
}
read_all_reg(deser);
exit:
osMutexRelease(deser->lock);
return ret;
}
static int tc9591_deinit(struct vdev_device *vdev)
{
struct tc9591_dev *deser;
if (!vdev) {
return -1;
}
deser = (struct tc9591_dev *)vdev->priv_data;
osMutexDelete(deser->lock);
vPortFree(deser);
return 0;
}
static const struct vdev_dev_ops tc9591_vdev_ops = {
.s_power = tc9591_s_power,
.enum_mbus_fmt = tc9591_enum_mbus_fmt,
.enum_frame_size = tc9591_enum_frame_size,
.enum_frame_interval = tc9591_enum_frame_interval,
.g_frame_interval = tc9591_get_frame_interval,
.s_frame_interval = tc9591_set_frame_interval,
.get_fmt = tc9591_get_fmt,
.set_fmt = tc9591_set_fmt,
.s_stream = tc9591_s_stream,
.close = tc9591_deinit,
};
struct tc9591_dev *g_deser = NULL;
struct vdev_device *tc9591_init(void *init_data)
{
int ret;
struct tc9591_dev *deser;
struct camera_res_cfg *cfg = (struct camera_res_cfg *)init_data;
deser = pvPortMalloc(sizeof(struct tc9591_dev));
if (!deser || !cfg || !cfg->i2c_handle)
return NULL;
printf("%s \r\n", __func__);
memset(deser, 0, sizeof(struct tc9591_dev));
deser->i2c_handle = cfg->i2c_handle;
deser->deser_addr = TC9591_DESER_ADDR;
deser->vdev.priv_data = (void *)deser;
deser->lock = osMutexNew(NULL);
deser->gpio_rst = cfg->gpio[0]; //rst pin
deser->gpio_cam = cfg->gpio[1]; //vcam
deser->gpio_fsin = cfg->gpio[2]; //fsin
tc9591_s_power(&deser->vdev, 1); //power on
ret = tc9591_check_chip_id(deser);
if (ret == 0)
tc9591_s_stream(&deser->vdev, 1);
if (ret < 0)
goto err;
deser->vdev.ep.bus_type = VDEV_MBUS_PARALLEL2;
deser->vdev.frame_interval.numerator = 1;
deser->vdev.frame_interval.denominator = 60;
deser->vdev.fmt.width = 1920;
deser->vdev.fmt.height = 720;
deser->vdev.fmt.code = VDEV_MBUS_FMT_UYVY16;
deser->vdev.fmt.field = VDEV_FIELD_NONE;
deser->vdev.fmt.colorspace = VDEV_COLORSPACE_SRGB;
deser->vdev.ops = tc9591_vdev_ops;
g_deser = deser;
return &deser->vdev;
err:
vPortFree(deser);
return NULL;
}
void read_tc9591_regs(void)
{
read_all_reg(g_deser);
}

View File

@@ -0,0 +1,16 @@
/**
* @file ex_tc9591.h
* @brief export header for Toshiba tc9591
*
* @copyright Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*/
#ifndef __EX_TC9591_H__
#define __EX_TC9591_H__
#include "exvdev.h"
struct vdev_device *tc9591_init(void *init_data);
#endif /* __EX_TC9591_H__ */

View File

@@ -0,0 +1,418 @@
/*
* Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#include "ex_ti926.h"
#define TI926_DEVICE_ID 0x58
#define TI926_DESER_ADDR 0x2C
enum ti926_status {
DESER_IDLE,
DESER_STREAM_ON,
};
struct ti926_dev {
struct vdev_device vdev;
void *i2c_handle;
uint8_t deser_addr;
uint32_t gpio_pwn;
uint32_t gpio_intn;
osMutexId_t lock;
uint32_t status;
};
int test_pattern = 0;
static int ti926_write_reg(struct ti926_dev *deser, uint8_t reg, uint8_t val)
{
uint8_t buf[2];
int ret = 0;
deser_info("%s: addr=0x%x, reg=0x%x, val=0x%x\n", __func__,
deser->deser_addr, reg, val);
buf[0] = reg;
buf[1] = val;
ret = i2c_write(deser->i2c_handle, deser->deser_addr, buf, 2);
if (ret < 0) {
deser_err("%s: error: reg=0x%x, val=%x\n", __func__, reg, val);
return ret;
}
return ret;
}
static int ti926_read_reg(struct ti926_dev *deser, uint8_t reg, uint8_t *val)
{
uint8_t buf[1];
int ret = 0;
memset(buf, 0, sizeof(buf));
ret = i2c_write_read(deser->i2c_handle, deser->deser_addr, &reg, 1, buf, 1);
if (ret < 0) {
deser_err("%s: error: read reg=0x%02x\n", __func__, reg);
return ret;
}
*val = buf[0];
return 0;
}
uint8_t reg_list[] = {
0x3A, 0x41, 0x44, 0x56, 0x64, 0x65, 0x66,
0x67, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
};
static int read_all_reg(struct ti926_dev *deser)
{
int cnt, j;
uint8_t reg, val;
for (reg = 0x00; reg <= 0x17; reg++) {
ti926_read_reg(deser, reg, &val);
printf("ti926 [0x%02x]=0x%02x\n", reg, val);
}
for (reg = 0x1c; reg <= 0x2c; reg++) {
ti926_read_reg(deser, reg, &val);
printf("ti926 [0x%02x]=0x%02x\n", reg, val);
}
cnt = sizeof(reg_list);
for (j = 0; j < cnt; j++) {
reg = reg_list[j];
ti926_read_reg(deser, reg, &val);
printf("ti926 [0x%02x]=0x%02x\n", reg, val);
}
return 0;
}
static int ti926_check_chip_id(struct ti926_dev *deser)
{
int ret;
int i = 0;
uint8_t chip_id = 0;
deser_info("%s\n", __func__);
while (i++ < 30) {
ret = ti926_read_reg(deser, 0x00, &chip_id);
if (ret) {
deser_err("%s: failed to read chip identifier\n", __func__);
continue;
}
if (chip_id != TI926_DEVICE_ID) {
deser_err("%s: wrong chip identifier, expected 0x%x(ti926), got "
"0x%x\n",
__func__, TI926_DEVICE_ID, chip_id);
ret = -1;
continue;
} else {
deser_info("%s: chip identifier, expected 0x%x(ti926), got 0x%x\n",
__func__, TI926_DEVICE_ID, chip_id);
break;
}
}
if (ret == 0)
read_all_reg(deser);
return ret;
}
static int ti926_init_setup(struct ti926_dev *deser)
{
uint8_t val;
int ret;
ret = ti926_read_reg(deser, 0x01, &val);
if (ret < 0) {
deser_err("ti926 fail to read 0x01, ret=%d\n", ret);
return ret;
}
deser_err("ti926 read [0x01]=0x%02x\n", val);
if (test_pattern) {
// 1280x480 test pattern
ti926_write_reg(deser, 0x66, 0x0e); // config,b'0000|[Ve|He|Vp|Hp]
ti926_write_reg(deser, 0x67, 0x02);
ti926_write_reg(deser, 0x66, 0x03); // clock divider
ti926_write_reg(deser, 0x67, 0x03);
ti926_write_reg(deser, 0x66, 0x04); // HT 8least
ti926_write_reg(deser, 0x67, 0x54);
ti926_write_reg(deser, 0x66, 0x05); // VT 4least|HT 4most
ti926_write_reg(deser, 0x67, 0xb5);
ti926_write_reg(deser, 0x66, 0x06); // VT 8most
ti926_write_reg(deser, 0x67, 0x20);
ti926_write_reg(deser, 0x66, 0x07); // H 8least
ti926_write_reg(deser, 0x67, 0x00);
ti926_write_reg(deser, 0x66, 0x08); // V 4least|H 4most
ti926_write_reg(deser, 0x67, 0x05);
ti926_write_reg(deser, 0x66, 0x09); // V 8most
ti926_write_reg(deser, 0x67, 0x1e);
ti926_write_reg(deser, 0x66, 0x0a); // HS
ti926_write_reg(deser, 0x67, 0x1c);
ti926_write_reg(deser, 0x66, 0x0b); // VS
ti926_write_reg(deser, 0x67, 0x0d);
ti926_write_reg(deser, 0x66, 0x0c); // HBP
ti926_write_reg(deser, 0x67, 0x38);
ti926_write_reg(deser, 0x66, 0x0d); // VBP
ti926_write_reg(deser, 0x67, 0x1c);
ti926_write_reg(deser, 0x65, 0x04);
ti926_write_reg(deser, 0x64, 0x11);
} else {
ti926_write_reg(deser, 0x64, 0x10);
ti926_write_reg(deser, 0x29, 0xA0);
}
deser_info("ti926 init setup done\n");
read_all_reg(deser);
return 0;
}
//extern gpio_dev_t g_gpio_safety;
static int ti926_s_power(struct vdev_device *vdev, int enable)
{
struct ti926_dev *deser;
uint32_t pin;
// gpio_dev_t *gpio_dev = &g_gpio_safety;
if (!vdev) {
return -1;
}
deser = (struct ti926_dev *)vdev->priv_data;
pin = deser->gpio_pwn; /* GPIO_C14 */
deser_info("%s: pin %d, enable %d\n", __func__, pin, enable);
gpio_set(NULL, pin, enable ? true : false);
usleep_range(10000, 11000);
return 0;
}
static int ti926_enum_mbus_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fme)
{
struct ti926_dev *deser;
if ((vdev == NULL) || (fme == NULL)) {
return -1;
}
deser = (struct ti926_dev *)vdev->priv_data;
if (fme->index > 0) {
fme->index = 0;
return -1;
}
fme->code = deser->vdev.fmt.code;
fme->field = deser->vdev.fmt.field;
fme->colorspace = deser->vdev.fmt.colorspace;
return 0;
}
static int ti926_enum_frame_size(struct vdev_device *vdev,
struct vdev_frame_size_enum *fse)
{
struct ti926_dev *deser;
if (!vdev || !fse) {
return -1;
}
deser = (struct ti926_dev *)vdev->priv_data;
if (fse->index > 0) {
fse->index = 0;
return -1;
}
fse->min_width = deser->vdev.fmt.width;
fse->max_width = deser->vdev.fmt.width;
fse->min_height = deser->vdev.fmt.height;
fse->max_height = deser->vdev.fmt.height;
return 0;
}
static int ti926_enum_frame_interval(struct vdev_device *vdev,
struct vdev_frame_interval_enum *fie)
{
return 0;
}
static int ti926_get_frame_interval(struct vdev_device *vdev,
struct vdev_fract *fi)
{
if (!vdev || !fi) {
return -1;
}
*fi = vdev->frame_interval;
return 0;
}
static int ti926_set_frame_interval(struct vdev_device *vdev,
struct vdev_fract fi)
{
return 0;
}
static int ti926_get_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fmt)
{
if (!vdev) {
return -1;
}
*fmt = vdev->fmt;
return 0;
}
static int ti926_set_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt fmt)
{
return 0;
}
static int ti926_s_stream(struct vdev_device *vdev, int enable)
{
int ret = 0;
struct ti926_dev *deser;
if (!vdev) {
return -1;
}
deser = (struct ti926_dev *)vdev->priv_data;
osMutexAcquire(deser->lock, 0);
if (enable == 1) {
if (deser->status == DESER_STREAM_ON) {
deser_info("%s: already stream on\n", __func__);
goto exit;
}
ret = ti926_init_setup(deser);
if (ret < 0)
goto exit;
deser->status = DESER_STREAM_ON;
} else {
deser->status = DESER_IDLE;
ti926_write_reg(deser, 0x64, 0x10);
ti926_write_reg(deser, 0x65, 0x00);
}
exit:
osMutexRelease(deser->lock);
return ret;
}
static int ti926_deinit(struct vdev_device *vdev)
{
struct ti926_dev *deser;
if (!vdev) {
return -1;
}
deser = (struct ti926_dev *)vdev->priv_data;
osMutexDelete(deser->lock);
vPortFree(deser);
return 0;
}
static const struct vdev_dev_ops ti926_vdev_ops = {
.s_power = ti926_s_power,
.enum_mbus_fmt = ti926_enum_mbus_fmt,
.enum_frame_size = ti926_enum_frame_size,
.enum_frame_interval = ti926_enum_frame_interval,
.g_frame_interval = ti926_get_frame_interval,
.s_frame_interval = ti926_set_frame_interval,
.get_fmt = ti926_get_fmt,
.set_fmt = ti926_set_fmt,
.s_stream = ti926_s_stream,
.close = ti926_deinit,
};
struct ti926_dev *g_deser = NULL;
struct vdev_device *ti926_init(void *init_data)
{
int ret;
struct ti926_dev *deser;
struct camera_res_cfg *cfg = (struct camera_res_cfg *)init_data;
deser = pvPortMalloc(sizeof(struct ti926_dev));
if (!deser || !cfg || !cfg->i2c_handle)
return NULL;
memset(deser, 0, sizeof(struct ti926_dev));
deser->i2c_handle = cfg->i2c_handle;
deser->deser_addr = TI926_DESER_ADDR;
deser->gpio_pwn = cfg->gpio[0];
deser->gpio_intn = cfg->gpio[1];
deser->vdev.priv_data = (void *)deser;
deser->lock = osMutexNew(NULL);
ti926_s_power(&deser->vdev, 1);
ret = ti926_check_chip_id(deser);
if (ret == 0)
ti926_s_stream(&deser->vdev, 0);
ti926_s_power(&deser->vdev, 0);
if (ret < 0)
goto err;
deser->vdev.ep.bus_type = VDEV_MBUS_PARALLEL;
deser->vdev.frame_interval.numerator = 1;
if (test_pattern) {
deser->vdev.frame_interval.denominator = 83;
deser->vdev.fmt.width = 1280;
deser->vdev.fmt.height = 480;
} else {
deser->vdev.frame_interval.denominator = 56;
deser->vdev.fmt.width = 1920;
deser->vdev.fmt.height = 720;
}
deser->vdev.fmt.code = VDEV_MBUS_FMT_RGB24;
deser->vdev.fmt.field = VDEV_FIELD_NONE;
deser->vdev.fmt.colorspace = VDEV_COLORSPACE_SRGB;
deser->vdev.ops = ti926_vdev_ops;
g_deser = deser;
return &deser->vdev;
err:
vPortFree(deser);
return NULL;
}
int user_test(int read, uint8_t reg, uint8_t *val)
{
if (g_deser == NULL || val == NULL)
return 0;
if (reg > 0xf8) {
read_all_reg(g_deser);
return 0;
}
if (read) {
ti926_read_reg(g_deser, reg, val);
printf("user read reg[0x%02x]=0x%02x\n", reg, *val);
} else {
printf("user write reg[0x%02x]=0x%02x\n", reg, *val);
ti926_write_reg(g_deser, reg, *val);
}
return 0;
}

View File

@@ -0,0 +1,21 @@
/**
* @file ex_ti926.h
* @brief export header for TI DS90UB926Q
*
* @copyright Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*/
#ifndef __EX_TI926_H__
#define __EX_TI926_H__
#include "exvdev.h"
struct vdev_device *ti926_init(void *init_data);
/* for debug only */
int user_test(int read, uint8_t reg, uint8_t *val);
/* for debug only */
extern int test_pattern;
#endif /* __EX_TI926_H__ */

View File

@@ -0,0 +1,564 @@
#include "ex_tp2825.h"
#include "camera/sdrv-cam-os-def.h"
//#define SENSOR_OV2K10 1
#define SENSOR_OVX3D 1
#define tp2825_DEVICE_ID 0x44 //High 2825 7bit Address
static uint32_t s_tp2825_width = 1920;
static uint32_t s_tp2825_height = 1080;
#if SENSOR_OV2K10
static uint32_t s_tp2825_fps = 30;
#else
static uint32_t s_tp2825_fps = 29;
#endif
enum tp2825_status {
SERSOR_IDLE,
SERSOR_STREAM_ON,
};
struct tp2825_dev {
struct vdev_device vdev;
void *i2c_handle;
uint8_t deser_addr;
uint8_t serder_addr;
uint32_t gpio_pwr; // power supply pin
uint32_t gpio_rst; // reset pin
osMutexId_t lock;
uint32_t status;
};
static int tp2825_write_reg(struct tp2825_dev *sensor, uint8_t reg, uint8_t val)
{
uint8_t buf[2];
int ret = 0;
//sensor_info("%s: addr=0x%x, reg=0x%x, val=0x%x\n", __func__,sensor->deser_addr, reg, val);
buf[0] = reg;
buf[1] = val;
ret = i2c_write(sensor->i2c_handle, sensor->deser_addr, buf, 2);
if (ret < 0) {
sensor_err("%s: error: reg=0x%x, val=%x\n", __func__, reg, val);
return ret;
}
return ret;
}
static int tp2825_read_reg(struct tp2825_dev *sensor, uint8_t reg, uint8_t *val)
{
uint8_t buf[1];
int ret = 0;
memset(buf, 0, sizeof(buf));
ret = i2c_write_read(sensor->i2c_handle, sensor->deser_addr, &reg, 1, buf, 1);
if (ret < 0) {
sensor_err("%s: error: read reg=0x%02x\n", __func__, reg);
return ret;
}
*val = buf[0];
return 0;
}
__UNUSED static int tp2825_check_chip_id(struct tp2825_dev *sensor)
{
int ret = 0;
uint8_t val[2];
uint16_t chip_id = 0;
ret = tp2825_read_reg(sensor, 0xfe, &val[0]);
ret |= tp2825_read_reg(sensor, 0xff, &val[1]);
chip_id = val[1]<<8 | val[0];
if(chip_id != 0x2850) {
ret = -1;
}
sensor_info("%s 0x%x 0x%x 0x%x\n", __func__, val[0],val[1], chip_id);
return ret;
}
static int tp2825_init_setup(struct tp2825_dev *sensor)
{
sensor_info("%s \r\n",__func__ );
#if SENSOR_OV2K10
tp2825_write_reg(sensor,0x0,0x11);
tp2825_write_reg(sensor,0x1,0x7E);
tp2825_write_reg(sensor,0x2,0x40);
tp2825_write_reg(sensor,0x3,0x03);
tp2825_write_reg(sensor,0x4,0x00);
tp2825_write_reg(sensor,0x5,0x00);
tp2825_write_reg(sensor,0x6,0x32);
tp2825_write_reg(sensor,0x7,0xC0);
tp2825_write_reg(sensor,0x8,0x00);
tp2825_write_reg(sensor,0x9,0x24);
tp2825_write_reg(sensor,0xA,0x48);
tp2825_write_reg(sensor,0xB,0xC0);
tp2825_write_reg(sensor,0xC,0x03);
tp2825_write_reg(sensor,0xD,0x50);
tp2825_write_reg(sensor,0xE,0x00);
tp2825_write_reg(sensor,0xF,0x0);
tp2825_write_reg(sensor,0x10,0x00);
tp2825_write_reg(sensor,0x11,0x40);
tp2825_write_reg(sensor,0x12,0x40);
tp2825_write_reg(sensor,0x13,0x00);
tp2825_write_reg(sensor,0x14,0x00);
tp2825_write_reg(sensor,0x15,0x03);
tp2825_write_reg(sensor,0x16,0xD2);
tp2825_write_reg(sensor,0x17,0x80);
tp2825_write_reg(sensor,0x18,0x29);
tp2825_write_reg(sensor,0x19,0x38);
tp2825_write_reg(sensor,0x1A,0x47);
tp2825_write_reg(sensor,0x1B,0x01);
tp2825_write_reg(sensor,0x1C,0x08);
tp2825_write_reg(sensor,0x1D,0x98);
tp2825_write_reg(sensor,0x1E,0x80);
tp2825_write_reg(sensor,0x1F,0x80);
tp2825_write_reg(sensor,0x20,0x30);
tp2825_write_reg(sensor,0x21,0x84);
tp2825_write_reg(sensor,0x22,0x36);
tp2825_write_reg(sensor,0x23,0x3C);
tp2825_write_reg(sensor,0x24,0x04);
tp2825_write_reg(sensor,0x25,0xFF);
tp2825_write_reg(sensor,0x26,0x05);
tp2825_write_reg(sensor,0x27,0x2D);
tp2825_write_reg(sensor,0x28,0x00);
tp2825_write_reg(sensor,0x29,0x48);
tp2825_write_reg(sensor,0x2A,0x30);
tp2825_write_reg(sensor,0x2B,0x60);
tp2825_write_reg(sensor,0x2C,0x0A);
tp2825_write_reg(sensor,0x2D,0x30);
tp2825_write_reg(sensor,0x2E,0x70);
tp2825_write_reg(sensor,0x2F,0x00);
tp2825_write_reg(sensor,0x30,0x48);
tp2825_write_reg(sensor,0x31,0xBB);
tp2825_write_reg(sensor,0x32,0x2E);
tp2825_write_reg(sensor,0x33,0x90);
tp2825_write_reg(sensor,0x34,0x00);
tp2825_write_reg(sensor,0x35,0x05);
tp2825_write_reg(sensor,0x36,0xDC);
tp2825_write_reg(sensor,0x37,0x00);
tp2825_write_reg(sensor,0x38,0x40);
tp2825_write_reg(sensor,0x39,0x1C);
tp2825_write_reg(sensor,0x3A,0x32);
tp2825_write_reg(sensor,0x3B,0x26);
tp2825_write_reg(sensor,0x3C,0x00);
tp2825_write_reg(sensor,0x3D,0x60);
tp2825_write_reg(sensor,0x3E,0x00);
tp2825_write_reg(sensor,0x3F,0x00);
tp2825_write_reg(sensor,0x40,0x00);
tp2825_write_reg(sensor,0x41,0x00);
tp2825_write_reg(sensor,0x42,0x00);
tp2825_write_reg(sensor,0x43,0x00);
tp2825_write_reg(sensor,0x44,0x00);
tp2825_write_reg(sensor,0x45,0x00);
tp2825_write_reg(sensor,0x46,0x00);
tp2825_write_reg(sensor,0x47,0x00);
tp2825_write_reg(sensor,0x48,0x00);
tp2825_write_reg(sensor,0x49,0x00);
tp2825_write_reg(sensor,0x4A,0x00);
tp2825_write_reg(sensor,0x4B,0x00);
tp2825_write_reg(sensor,0x4C,0x43);
tp2825_write_reg(sensor,0x4D,0x00);
tp2825_write_reg(sensor,0x4E,0x0D);
tp2825_write_reg(sensor,0x4F,0x00);
tp2825_write_reg(sensor,0xF0,0x00);
tp2825_write_reg(sensor,0xF1,0x00);
tp2825_write_reg(sensor,0xF2,0x00);
tp2825_write_reg(sensor,0xF3,0x00);
tp2825_write_reg(sensor,0xF4,0x20);
tp2825_write_reg(sensor,0xF5,0x10);
tp2825_write_reg(sensor,0xF6,0x00);
tp2825_write_reg(sensor,0xF7,0x44);
tp2825_write_reg(sensor,0xF8,0x00);
tp2825_write_reg(sensor,0xF9,0x00);
tp2825_write_reg(sensor,0xFA,0x0b);
tp2825_write_reg(sensor,0xFB,0x00);
tp2825_write_reg(sensor,0xFC,0x00);
tp2825_write_reg(sensor,0xFD,0x80);
tp2825_write_reg(sensor,0xFE,0x28);
tp2825_write_reg(sensor,0xF0,0x00);
tp2825_write_reg(sensor,0xF1,0x00);
tp2825_write_reg(sensor,0xF2,0x00);
tp2825_write_reg(sensor,0xF3,0x00);
tp2825_write_reg(sensor,0xF4,0x20);
tp2825_write_reg(sensor,0xF5,0x10);
tp2825_write_reg(sensor,0xF6,0x00);
tp2825_write_reg(sensor,0xF7,0x44);
tp2825_write_reg(sensor,0xF8,0x00);
tp2825_write_reg(sensor,0xF9,0x00);
tp2825_write_reg(sensor,0xFA,0x0b);
tp2825_write_reg(sensor,0xFB,0x00);
tp2825_write_reg(sensor,0xFC,0x00);
tp2825_write_reg(sensor,0xFD,0x80);
tp2825_write_reg(sensor,0xFE,0x28);
tp2825_write_reg(sensor,0x40,0x08); //mpage select
tp2825_write_reg(sensor,0x13,0x04);
tp2825_write_reg(sensor,0x40,0x00); //back to decoder page
#else
//config 0x3D 1920X1080 29 FPS settings
tp2825_write_reg(sensor,0x00,0x11);
tp2825_write_reg(sensor,0x02,0x40);
tp2825_write_reg(sensor,0x03,0x03);
tp2825_write_reg(sensor,0x05,0x00);
tp2825_write_reg(sensor,0x06,0x32);
tp2825_write_reg(sensor,0x07,0xC0);
tp2825_write_reg(sensor,0x08,0x00);
tp2825_write_reg(sensor,0x09,0x24);
tp2825_write_reg(sensor,0x0A,0x48);
tp2825_write_reg(sensor,0x0B,0xC0);
tp2825_write_reg(sensor,0x0C,0x03);
tp2825_write_reg(sensor,0x0D,0x50);
tp2825_write_reg(sensor,0x0E,0x00);
tp2825_write_reg(sensor,0x0F,0x00);
tp2825_write_reg(sensor,0x10,0x00);
tp2825_write_reg(sensor,0x11,0x40);
tp2825_write_reg(sensor,0x12,0x40);
tp2825_write_reg(sensor,0x13,0x00);
tp2825_write_reg(sensor,0x14,0x00);
tp2825_write_reg(sensor,0x15,0x13);
tp2825_write_reg(sensor,0x16,0xE8);
tp2825_write_reg(sensor,0x17,0x80);
tp2825_write_reg(sensor,0x18,0x54);
tp2825_write_reg(sensor,0x19,0x38);
tp2825_write_reg(sensor,0x1A,0x47);
tp2825_write_reg(sensor,0x1B,0x01);
tp2825_write_reg(sensor,0x1C,0x09);
tp2825_write_reg(sensor,0x1D,0xab);
tp2825_write_reg(sensor,0x1E,0x80);
tp2825_write_reg(sensor,0x1F,0x80);
tp2825_write_reg(sensor,0x20,0x30);
tp2825_write_reg(sensor,0x21,0x84);
tp2825_write_reg(sensor,0x22,0x36);
tp2825_write_reg(sensor,0x23,0x3C);
tp2825_write_reg(sensor,0x24,0x04);
tp2825_write_reg(sensor,0x25,0xFF);
tp2825_write_reg(sensor,0x26,0x05);
tp2825_write_reg(sensor,0x27,0x2D);
tp2825_write_reg(sensor,0x28,0x00);
tp2825_write_reg(sensor,0x29,0x48);
tp2825_write_reg(sensor,0x2A,0x30);
tp2825_write_reg(sensor,0x2B,0x60);
tp2825_write_reg(sensor,0x2C,0x0A);
tp2825_write_reg(sensor,0x2D,0x30);
tp2825_write_reg(sensor,0x2E,0x70);
tp2825_write_reg(sensor,0x2F,0x00);
tp2825_write_reg(sensor,0x30,0x48);
tp2825_write_reg(sensor,0x31,0xBB);
tp2825_write_reg(sensor,0x32,0x2E);
tp2825_write_reg(sensor,0x33,0x90);
tp2825_write_reg(sensor,0x34,0x00);
tp2825_write_reg(sensor,0x35,0x14);
tp2825_write_reg(sensor,0x36,0xB5);
tp2825_write_reg(sensor,0x37,0x00);
tp2825_write_reg(sensor,0x38,0x40);
tp2825_write_reg(sensor,0x39,0x1C);
tp2825_write_reg(sensor,0x3A,0x32);
tp2825_write_reg(sensor,0x3B,0x26);
tp2825_write_reg(sensor,0x3C,0x00);
tp2825_write_reg(sensor,0x3D,0x60);
tp2825_write_reg(sensor,0x3E,0x00);
tp2825_write_reg(sensor,0x3F,0x00);
tp2825_write_reg(sensor,0x40,0x00);
tp2825_write_reg(sensor,0x41,0x00);
tp2825_write_reg(sensor,0x42,0x00);
tp2825_write_reg(sensor,0x43,0x00);
tp2825_write_reg(sensor,0x44,0x00);
tp2825_write_reg(sensor,0x45,0x00);
tp2825_write_reg(sensor,0x46,0x00);
tp2825_write_reg(sensor,0x47,0x00);
tp2825_write_reg(sensor,0x48,0x00);
tp2825_write_reg(sensor,0x49,0x00);
tp2825_write_reg(sensor,0x4A,0x00);
tp2825_write_reg(sensor,0x4B,0x00);
tp2825_write_reg(sensor,0x4C,0x43);
tp2825_write_reg(sensor,0x4D,0x00);
tp2825_write_reg(sensor,0x4E,0x0D);
tp2825_write_reg(sensor,0x4F,0x00);
tp2825_write_reg(sensor,0xF0,0x00);
tp2825_write_reg(sensor,0xF1,0x00);
tp2825_write_reg(sensor,0xF2,0x00);
tp2825_write_reg(sensor,0xF3,0x00);
tp2825_write_reg(sensor,0xF4,0x20);
tp2825_write_reg(sensor,0xF5,0x10);
tp2825_write_reg(sensor,0xF6,0x00);
tp2825_write_reg(sensor,0xF7,0x44);
tp2825_write_reg(sensor,0xF8,0x00);
tp2825_write_reg(sensor,0xF9,0x00);
tp2825_write_reg(sensor,0xFA,0x0b);
tp2825_write_reg(sensor,0xFB,0x00);
tp2825_write_reg(sensor,0xFC,0x00);
tp2825_write_reg(sensor,0xFD,0x80);
tp2825_write_reg(sensor,0xFE,0x28);
tp2825_write_reg(sensor,0x40,0x08); // mpage select
tp2825_write_reg(sensor,0x13,0x04);
tp2825_write_reg(sensor,0x40,0x00);//back to decoder page
#endif
return 0;
}
static int tp2825_s_power(struct vdev_device *vdev, int enable)
{
struct tp2825_dev *sensor;
uint32_t pin;
if (!vdev) {
return -1;
}
sensor = (struct tp2825_dev *)vdev->priv_data;
pin = sensor->gpio_rst;
sensor_info("%s: pin %d\r\n", __func__, pin);
return 0;
}
static int tp2825_s_rst(struct vdev_device *vdev, int enable)
{
struct tp2825_dev *sensor;
uint32_t pin;
if (!vdev) {
return -1;
}
sensor = (struct tp2825_dev *)vdev->priv_data;
pin = sensor->gpio_rst;
sensor_info("%s: pin %d, enable %d\r\n", __func__, pin, enable);
gpio_set(NULL, pin, enable ? true : false);
return 0;
}
static int tp2825_read_regs(struct tp2825_dev *sensor)
{
uint8_t val = 0;
uint8_t reg;
//usleep_range(10000, 11000);
tp2825_write_reg(sensor, 0x40, 0x00);
for(reg =0 ; reg<0xff; reg++)
{
tp2825_read_reg(sensor, reg,&val);
sensor_info("reg:0x%x val 0x%x\n\r", reg,val);
}
sensor_info("tp2825 read switch page\n\r");
tp2825_write_reg(sensor, 0x40, 0x08);
for(int reg =0 ;reg<0xff;reg++)
{
tp2825_read_reg(sensor, reg,&val);
sensor_info("reg:0x%x val 0x%x\n\r",reg,val);
}
return 0;
}
static int tp2825_enum_mbus_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fme)
{
struct tp2825_dev *sensor;
if ((vdev == NULL) || (fme == NULL)) {
return -1;
}
sensor_err("%s: \r\n", __func__);
sensor = (struct tp2825_dev *)vdev->priv_data;
if (fme->index > 0) {
fme->index = 0;
return -1;
}
fme->code = sensor->vdev.fmt.code;
fme->field = sensor->vdev.fmt.field;
fme->colorspace = sensor->vdev.fmt.colorspace;
return 0;
}
static int tp2825_enum_frame_size(struct vdev_device *vdev,
struct vdev_frame_size_enum *fse)
{
struct tp2825_dev *sensor;
sensor_err("%s: \r\n", __func__);
if (!vdev || !fse) {
return -1;
}
sensor = (struct tp2825_dev *)vdev->priv_data;
if (fse->index > 0) {
fse->index = 0;
return -1;
}
fse->min_width = sensor->vdev.fmt.width;
fse->max_width = sensor->vdev.fmt.width;
fse->min_height = sensor->vdev.fmt.height;
fse->max_height = sensor->vdev.fmt.height;
return 0;
}
static int tp2825_enum_frame_interval(struct vdev_device *vdev,
struct vdev_frame_interval_enum *fie)
{
sensor_err("%s: \r\n", __func__);
return 0;
}
static int tp2825_get_frame_interval(struct vdev_device *vdev,
struct vdev_fract *fi)
{
if (!vdev || !fi) {
return -1;
}
sensor_err("%s: \r\n", __func__);
*fi = vdev->frame_interval;
return 0;
}
static int tp2825_set_frame_interval(struct vdev_device *vdev,
struct vdev_fract fi)
{
sensor_err("%s: \r\n", __func__);
return 0;
}
static int tp2825_get_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fmt)
{
if (!vdev) {
return -1;
}
sensor_err("%s: \r\n", __func__);
*fmt = vdev->fmt;
return 0;
}
static int tp2825_set_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt fmt)
{
sensor_err("%s: \r\n", __func__);
return 0;
}
static int tp2825_s_stream(struct vdev_device *vdev, int enable)
{
int ret = 0;
struct tp2825_dev *sensor;
if (!vdev) {
return -1;
}
sensor_err("%s: \r\n", __func__);
sensor = (struct tp2825_dev *)vdev->priv_data;
osMutexAcquire(sensor->lock, 0);
if (enable == 1) {
if (sensor->status == SERSOR_STREAM_ON) {
sensor_info("%s: already stream on\r\n", __func__);
goto exit;
}
ret = tp2825_init_setup(sensor);
if (ret < 0)
goto exit;
mdelay(800); //delay 700 ms
tp2825_read_regs(sensor);
sensor->status = SERSOR_STREAM_ON;
} else {
sensor->status = SERSOR_IDLE;
}
exit:
osMutexRelease(sensor->lock);
return ret;
}
static int tp2825_deinit(struct vdev_device *vdev)
{
struct tp2825_dev *sensor;
if (!vdev) {
return -1;
}
sensor = (struct tp2825_dev *)vdev->priv_data;
osMutexDelete(sensor->lock);
free(sensor);
return 0;
}
static const struct vdev_dev_ops tp2825_vdev_ops = {
.s_power = tp2825_s_power,
.enum_mbus_fmt = tp2825_enum_mbus_fmt,
.enum_frame_size = tp2825_enum_frame_size,
.enum_frame_interval = tp2825_enum_frame_interval,
.g_frame_interval = tp2825_get_frame_interval,
.s_frame_interval = tp2825_set_frame_interval,
.get_fmt = tp2825_get_fmt,
.set_fmt = tp2825_set_fmt,
.s_stream = tp2825_s_stream,
.close = tp2825_deinit,
};
struct tp2825_dev *g_tp2825 = NULL;
struct vdev_device *tp2825_init(void *init_data)
{
struct tp2825_dev *sensor;
struct camera_res_cfg *cfg = (struct camera_res_cfg *)init_data;
sensor = pvPortMalloc(sizeof(struct tp2825_dev));
if (!sensor || !cfg)
return NULL;
sensor_err("%s: \r\n", __func__);
memset(sensor, 0, sizeof(struct tp2825_dev));
sensor->i2c_handle = cfg->i2c_handle;
sensor->deser_addr = tp2825_DEVICE_ID;
sensor->gpio_rst = cfg->gpio[0]; //rst pin
sensor->gpio_pwr = cfg->gpio[1]; //pwr pin
sensor->vdev.priv_data = (void *)sensor;
sensor->lock = osMutexNew(NULL);
tp2825_s_rst(&sensor->vdev, 1);
usleep_range(1000, 1100);
tp2825_s_rst(&sensor->vdev, 0);
usleep_range(1000, 1000);
tp2825_s_rst(&sensor->vdev, 1);
//ret = tp2825_check_chip_id(sensor);
tp2825_s_stream(&sensor->vdev, 0);
sensor->vdev.ep.bus_type = VDEV_MBUS_BT1120SDR;
sensor->vdev.frame_interval.numerator = 1;
sensor->vdev.frame_interval.denominator = s_tp2825_fps;
sensor->vdev.fmt.width = s_tp2825_width;
sensor->vdev.fmt.height = s_tp2825_height;
sensor->vdev.fmt.code = VDEV_MBUS_FMT_UYVY16; //VDEV_MBUS_FMT_UYVY16 VDEV_MBUS_FMT_UYVY16
sensor->vdev.fmt.field = VDEV_FIELD_NONE;
sensor->vdev.fmt.colorspace = VDEV_COLORSPACE_SRGB;
sensor->vdev.ops = tp2825_vdev_ops;
g_tp2825 = sensor;
return &sensor->vdev;
}

View File

@@ -0,0 +1,16 @@
/**
* @file ex_tp2825.h
* @brief export header for sensor OV tp2825
*
* @copyright Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*/
#ifndef __EX_tp2825_H__
#define __EX_tp2825_H__
#include "exvdev.h"
struct vdev_device *tp2825_init(void *init_data);
#endif /* __EX_tp2825_H__ */

View File

@@ -0,0 +1,183 @@
/*
* Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _CONFIG_OS_SAFETY_
#include <kernel/mutex.h>
#include <lib/console.h>
#include <platform.h>
#include <uapi/uapi/err.h>
#include "i2c_hal.h"
#else /* _CONFIG_OS_SSDK_ */
#endif
#include "ex_vCamera.h"
struct vCam_dev {
struct vdev_device vdev;
uint32_t streaming;
int power_count;
};
int vCamera_exit(struct vdev_device *vdev);
static inline struct vCam_dev *to_vCamera(struct vdev_device *vdev)
{
return containerof(vdev, struct vCam_dev, vdev);
}
static int vCam_s_power(struct vdev_device *vdev, int on)
{
int ret = 0;
struct vCam_dev *vCam = to_vCamera(vdev);
vCam->power_count += on ? 1 : -1;
return ret;
}
static int vCam_enum_mbus_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fme)
{
struct vCam_dev *vCam = to_vCamera(vdev);
if ((vdev == NULL) || (fme == NULL)) {
return -1;
}
if (fme->index > 0) {
fme->index = 0;
return -1;
}
fme->code = vCam->vdev.fmt.code;
fme->field = vCam->vdev.fmt.field;
fme->colorspace = vCam->vdev.fmt.colorspace;
return 0;
}
static int vCam_enum_frame_size(struct vdev_device *vdev,
struct vdev_frame_size_enum *fse)
{
fse->min_width = 320;
fse->max_width = 3840;
fse->min_height = 240;
fse->max_height = 1080;
return 0;
}
static int vCam_enum_frame_interval(struct vdev_device *vdev,
struct vdev_frame_interval_enum *fie)
{
return 0;
}
static int vCam_g_frame_interval(struct vdev_device *vdev,
struct vdev_fract *fi)
{
if (!vdev || !fi) {
return -1;
}
*fi = vdev->frame_interval;
return 0;
}
static int vCam_s_frame_interval(struct vdev_device *vdev,
struct vdev_fract frame_interval)
{
return 0;
}
static int vCam_get_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fmt)
{
if (!vdev) {
return -1;
}
*fmt = vdev->fmt;
return 0;
;
}
static int vCam_set_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt format)
{
struct vCam_dev *vCam = to_vCamera(vdev);
if (vCam->streaming)
return -1;
vdev->fmt = format;
return 0;
}
static int vCam_s_stream(struct vdev_device *vdev, int enable)
{
struct vCam_dev *vCam = to_vCamera(vdev);
if (vCam->streaming == !enable)
vCam->streaming = enable;
return 0;
}
static const struct vdev_dev_ops vCam_vdev_ops = {
.s_power = vCam_s_power,
.enum_mbus_fmt = vCam_enum_mbus_fmt,
.enum_frame_size = vCam_enum_frame_size,
.enum_frame_interval = vCam_enum_frame_interval,
.g_frame_interval = vCam_g_frame_interval,
.s_frame_interval = vCam_s_frame_interval,
.get_fmt = vCam_get_fmt,
.set_fmt = vCam_set_fmt,
.s_stream = vCam_s_stream,
.close = vCamera_exit,
};
struct vdev_device *ex_vCamera_init(void *init_data)
{
struct vCam_dev *vCam;
struct vdev_mbus_framefmt *fmt;
vCam = pvPortMalloc(sizeof(struct vCam_dev));
if (!vCam)
return NULL;
memset(vCam, 0, sizeof(*vCam));
fmt = &vCam->vdev.fmt;
fmt->code = VDEV_MBUS_FMT_UYVY;
fmt->colorspace = VDEV_COLORSPACE_SRGB;
fmt->width = 640;
fmt->height = 480;
fmt->field = VDEV_FIELD_NONE;
vCam->vdev.frame_interval.numerator = 1;
vCam->vdev.frame_interval.denominator = 30;
vCam->vdev.ep.bus_type = VDEV_MBUS_PARALLEL;
vCam->vdev.ops = vCam_vdev_ops;
printf("%s:\n", __func__);
return &vCam->vdev;
}
int vCamera_exit(struct vdev_device *vdev)
{
struct vCam_dev *vCam = to_vCamera(vdev);
vPortFree(vCam);
return 0;
}

View File

@@ -0,0 +1,16 @@
/**
* @file ex_vCamera.h
* @brief export header for virtual camera
*
* @copyright Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*/
#ifndef __EX_VCAMERA_H__
#define __EX_VCAMERA_H__
#include "exvdev.h"
extern struct vdev_device *ex_vCamera_init(void *init_data);
#endif /* __EX_VCAMERA_H__ */

View File

@@ -0,0 +1,308 @@
/*
* Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#include "ex_x3d.h"
#define X3D_DEVICE_ID 0x58
#define X3D_SERSOR_ADDR 0x2C
#define CONFIG_OV_X3D_FPS 54
#ifdef CONFIG_OV_X3D_FPS
static uint32_t s_x3d_fps = CONFIG_OV_X3D_FPS;
#else
static uint32_t s_x3d_fps = 30;
#endif
#ifdef CONFIG_OV_X3D_W
static uint32_t s_x3d_width = CONFIG_OV_X3D_W;
#else
static uint32_t s_x3d_width = 1280;
#endif
#ifdef CONFIG_OV_X3D_H
static uint32_t s_x3d_height = CONFIG_OV_X3D_H;
#else
static uint32_t s_x3d_height = 720;
#endif
enum x3d_status {
SERSOR_IDLE,
SERSOR_STREAM_ON,
};
struct x3d_dev {
struct vdev_device vdev;
void *i2c_handle;
uint8_t sensor_addr;
uint32_t gpio_pwn;
uint32_t gpio_intn;
osMutexId_t lock;
uint32_t status;
};
__UNUSED static int x3d_write_reg(struct x3d_dev *sensor, uint8_t reg, uint8_t val)
{
uint8_t buf[2];
int ret = 0;
sensor_info("%s: addr=0x%x, reg=0x%x, val=0x%x\n", __func__,
sensor->sensor_addr, reg, val);
buf[0] = reg;
buf[1] = val;
ret = i2c_write(sensor->i2c_handle, sensor->sensor_addr, buf, 2);
if (ret < 0) {
sensor_err("%s: error: reg=0x%x, val=%x\n", __func__, reg, val);
return ret;
}
return ret;
}
__UNUSED static int x3d_read_reg(struct x3d_dev *sensor, uint8_t reg, uint8_t *val)
{
uint8_t buf[1];
int ret = 0;
memset(buf, 0, sizeof(buf));
ret = i2c_write_read(sensor->i2c_handle, sensor->sensor_addr, &reg, 1, buf, 1);
if (ret < 0) {
sensor_err("%s: error: read reg=0x%02x\n", __func__, reg);
return ret;
}
*val = buf[0];
return 0;
}
static int x3d_check_chip_id(struct x3d_dev *sensor)
{
sensor_info("%s\n", __func__);
return 0;
}
static int x3d_init_setup(struct x3d_dev *sensor)
{
sensor_info("x3d init setup done\n");
return 0;
}
//extern gpio_dev_t g_gpio_safety;
static int x3d_s_power(struct vdev_device *vdev, int enable)
{
struct x3d_dev *sensor;
uint32_t pin;
//gpio_dev_t *gpio_dev = &g_gpio_safety;
if (!vdev) {
return -1;
}
sensor = (struct x3d_dev *)vdev->priv_data;
pin = sensor->gpio_pwn;
sensor_info("%s: pin %d, enable %d\n", __func__, pin, enable);
//gpio_set(gpio_dev, pin, enable ? true : false);
//usleep_range(10000, 11000);
return 0;
}
static int x3d_enum_mbus_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fme)
{
struct x3d_dev *sensor;
if ((vdev == NULL) || (fme == NULL)) {
return -1;
}
sensor = (struct x3d_dev *)vdev->priv_data;
if (fme->index > 0) {
fme->index = 0;
return -1;
}
fme->code = sensor->vdev.fmt.code;
fme->field = sensor->vdev.fmt.field;
fme->colorspace = sensor->vdev.fmt.colorspace;
return 0;
}
static int x3d_enum_frame_size(struct vdev_device *vdev,
struct vdev_frame_size_enum *fse)
{
struct x3d_dev *sensor;
if (!vdev || !fse) {
return -1;
}
sensor = (struct x3d_dev *)vdev->priv_data;
if (fse->index > 0) {
fse->index = 0;
return -1;
}
fse->min_width = sensor->vdev.fmt.width;
fse->max_width = sensor->vdev.fmt.width;
fse->min_height = sensor->vdev.fmt.height;
fse->max_height = sensor->vdev.fmt.height;
return 0;
}
static int x3d_enum_frame_interval(struct vdev_device *vdev,
struct vdev_frame_interval_enum *fie)
{
return 0;
}
static int x3d_get_frame_interval(struct vdev_device *vdev,
struct vdev_fract *fi)
{
if (!vdev || !fi) {
return -1;
}
*fi = vdev->frame_interval;
return 0;
}
static int x3d_set_frame_interval(struct vdev_device *vdev,
struct vdev_fract fi)
{
return 0;
}
static int x3d_get_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fmt)
{
if (!vdev) {
return -1;
}
*fmt = vdev->fmt;
return 0;
}
static int x3d_set_fmt(struct vdev_device *vdev,
struct vdev_mbus_framefmt fmt)
{
return 0;
}
static int x3d_s_stream(struct vdev_device *vdev, int enable)
{
int ret = 0;
struct x3d_dev *sensor;
printf("%s \r\n",__func__);
if (!vdev) {
return -1;
}
sensor = (struct x3d_dev *)vdev->priv_data;
osMutexAcquire(sensor->lock, 0);
if (enable == 1) {
if (sensor->status == SERSOR_STREAM_ON) {
sensor_info("%s: already stream on\n", __func__);
goto exit;
}
ret = x3d_init_setup(sensor);
if (ret < 0)
goto exit;
sensor->status = SERSOR_STREAM_ON;
} else {
sensor->status = SERSOR_IDLE;
}
exit:
osMutexRelease(sensor->lock);
return ret;
}
static int x3d_deinit(struct vdev_device *vdev)
{
struct x3d_dev *sensor;
if (!vdev) {
return -1;
}
sensor = (struct x3d_dev *)vdev->priv_data;
osMutexDelete(sensor->lock);
free(sensor);
return 0;
}
static const struct vdev_dev_ops x3d_vdev_ops = {
.s_power = x3d_s_power,
.enum_mbus_fmt = x3d_enum_mbus_fmt,
.enum_frame_size = x3d_enum_frame_size,
.enum_frame_interval = x3d_enum_frame_interval,
.g_frame_interval = x3d_get_frame_interval,
.s_frame_interval = x3d_set_frame_interval,
.get_fmt = x3d_get_fmt,
.set_fmt = x3d_set_fmt,
.s_stream = x3d_s_stream,
.close = x3d_deinit,
};
struct x3d_dev *g_x3d = NULL;
struct vdev_device *x3d_init(void *init_data)
{
int ret;
struct x3d_dev *sensor;
struct camera_res_cfg *cfg = (struct camera_res_cfg *)init_data;
sensor = malloc(sizeof(struct x3d_dev));
if (!sensor || !cfg)
return NULL;
memset(sensor, 0, sizeof(struct x3d_dev));
sensor->i2c_handle = cfg->i2c_handle;
sensor->sensor_addr = X3D_SERSOR_ADDR;
sensor->gpio_pwn = cfg->gpio[0];
sensor->gpio_intn = cfg->gpio[1];
sensor->vdev.priv_data = (void *)sensor;
sensor->lock = osMutexNew(NULL);
x3d_s_power(&sensor->vdev, 1);
ret = x3d_check_chip_id(sensor);
if (ret == 0)
x3d_s_stream(&sensor->vdev, 0);
x3d_s_power(&sensor->vdev, 0);
if (ret < 0)
goto err;
sensor->vdev.ep.bus_type = VDEV_MBUS_PARALLEL2;
sensor->vdev.frame_interval.numerator = 1;
sensor->vdev.frame_interval.denominator = s_x3d_fps;
sensor->vdev.fmt.width = s_x3d_width;
sensor->vdev.fmt.height = s_x3d_height;
sensor->vdev.fmt.code = VDEV_MBUS_FMT_YUYV;
sensor->vdev.fmt.field = VDEV_FIELD_NONE;
sensor->vdev.fmt.colorspace = VDEV_COLORSPACE_SRGB;
sensor->vdev.ops = x3d_vdev_ops;
g_x3d = sensor;
return &sensor->vdev;
err:
free(sensor);
return NULL;
}

View File

@@ -0,0 +1,16 @@
/**
* @file ex_x3d.h
* @brief export header for sensor OV X3D
*
* @copyright Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*/
#ifndef __EX_X3D_H__
#define __EX_X3D_H__
#include "exvdev.h"
struct vdev_device *x3d_init(void *init_data);
#endif /* __EX_X3D_H__ */

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2020-2030 Semidrive, Inc. All Rights Reserved.
*
* This program is vPortFree software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include "exvdev.h"
#if CONFIG_OV5640
#include "ex_ov5640.h"
struct external_camera_api ov5640_interface = {
.name = "ov5640",
.init = ex_ov5640_init,
};
#endif
#if CONFIG_MAX96706
#include "ex_max96706.h"
struct external_camera_api max96706_interface = {
.name = "max96706",
.init = max96706_init,
};
#endif
#if CONFIG_MAX9276
#include "ex_max9276.h"
struct external_camera_api max9276_interface = {
.name = "max9276",
.init = max9276_init,
};
#endif
#if CONFIG_TI926
#include "ex_ti926.h"
struct external_camera_api ti926_interface = {
.name = "ti926",
.init = ti926_init,
};
#endif
#if CONFIG_TC9591
#include "ex_tc9591.h"
struct external_camera_api tc9591_interface = {
.name = "tc9591",
.init = tc9591_init,
};
#endif
#if CONFIG_OVX3D
#include "ex_x3d.h"
struct external_camera_api x3d_interface = {
.name = "ovx3d",
.init = x3d_init,
};
#endif
#if CONFIG_TP2825
#include "ex_tp2825.h"
struct external_camera_api tp2825_interface = {
.name = "tp2825",
.init = tp2825_init,
};
#endif
#if CONFIG_VIRTUAL_CAMERA
#include "ex_vCamera.h"
struct external_camera_api vcamera_interface = {
.name = "vcamera",
.init = ex_vCamera_init,
};
#endif
struct external_camera_api dummy_interface = {
.name = "dummy",
.init = NULL,
};
struct external_camera_api *excam_api_list[] = {
#if CONFIG_OV5640
&ov5640_interface,
#endif
#if CONFIG_MAX96706
&max96706_interface,
#endif
#if CONFIG_MAX9276
&max9276_interface,
#endif
#if CONFIG_TI926
&ti926_interface,
#endif
#if CONFIG_TC9591
&tc9591_interface,
#endif
#if CONFIG_OVX3D
&x3d_interface,
#endif
#if CONFIG_TP2825
&tp2825_interface,
#endif
#if CONFIG_VIRTUAL_CAMERA
&vcamera_interface,
#endif
&dummy_interface,
};
const void *get_excam_api(const char *name)
{
int i, num;
num = sizeof(excam_api_list) / sizeof(excam_api_list[0]);
for (i = 0; i < num; i++) {
if ((excam_api_list[i]->init != NULL) &&
!strcmp(excam_api_list[i]->name, name)) {
return excam_api_list[i];
}
}
return NULL;
}

View File

@@ -0,0 +1,171 @@
/**
* @file exvdev.h
* @brief externel video device header
*
* @copyright Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*/
#ifndef __EXVDEV_H__
#define __EXVDEV_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "camera/cam_res.h"
#include "camera/sdrv-cam-os-def.h"
#include "camera/vdev_defs.h"
struct vdev_fwnode_endpoint {
enum vdev_mbus_type bus_type;
unsigned int flags;
};
/**
* struct vdev_mbus_framefmt - Media bus format enumeration
* @index: for enum index
* @code: data format code (from enum vdev_mbus_format)
* @field: used interlacing type (from enum vdev_field_fmt)
* @colorspace: colorspace of the data (from enum vdev_colorspace)
* @width: frame width
* @height: frame height
* @field_w: field width, [0] for odd, [1] for even
* @field_h: field height, [0] for odd, [1] for even
*/
struct vdev_mbus_framefmt {
uint32_t index;
uint32_t code;
uint32_t field;
uint32_t colorspace;
uint32_t width;
uint32_t height;
uint32_t field_w[2];
uint32_t field_h[2];
};
/**
* struct vdev_frame_size_enum - frame size enumeration
* @index: format index during enumeration
* @code: format code (MEDIA_BUS_FMT_ definitions)
*/
struct vdev_frame_size_enum {
uint32_t index;
uint32_t code;
uint32_t min_width;
uint32_t max_width;
uint32_t min_height;
uint32_t max_height;
};
struct vdev_fract {
uint32_t numerator;
uint32_t denominator;
};
/**
* struct vdev_frame_interval_enum - Frame interval enumeration
* @index: frame interval index during enumeration
* @code: format code (MEDIA_BUS_FMT_ definitions)
* @width: frame width in pixels
* @height: frame height in pixels
* @interval: frame interval in seconds
*/
struct vdev_frame_interval_enum {
uint32_t index;
uint32_t code;
uint32_t width;
uint32_t height;
struct vdev_fract interval;
};
struct vdev_device;
/**
* struct vdev_dev_ops - vdev operations
* @s_power: power on/off, (MUST)
* @get_fmt: get mbus fmt (MUST)
* @s_stream: stream start/stop (MUST)
* @close: exit and deinit device (MUST)
*/
struct vdev_dev_ops {
int (*s_power)(struct vdev_device *vdev, int on);
int (*enum_mbus_fmt)(struct vdev_device *vdev,
struct vdev_mbus_framefmt *fme);
int (*enum_frame_size)(struct vdev_device *vdev,
struct vdev_frame_size_enum *fse);
int (*enum_frame_interval)(struct vdev_device *vdev,
struct vdev_frame_interval_enum *fie);
int (*g_frame_interval)(struct vdev_device *vdev, struct vdev_fract *fi);
int (*s_frame_interval)(struct vdev_device *vdev, struct vdev_fract fi);
int (*get_fmt)(struct vdev_device *vdev, struct vdev_mbus_framefmt *fmt);
int (*set_fmt)(struct vdev_device *vdev, struct vdev_mbus_framefmt fmt);
int (*s_stream)(struct vdev_device *vdev, int enable);
int (*close)(struct vdev_device *vdev);
};
struct vdev_device {
void *priv_data;
struct vdev_fwnode_endpoint ep;
struct vdev_mbus_framefmt fmt;
struct vdev_fract frame_interval;
struct vdev_dev_ops ops;
};
struct external_camera_api {
char name[32];
struct vdev_device *(*init)(void *init_data);
};
#define v_abs(a) ((a > 0) ? a : (0 - a))
#define vdev_find_nearest_size(array, array_size, width_field, height_field, \
width, height) \
({ \
(__typeof(&(array)[0])) __vdev_find_nearest_size( \
(array), array_size, sizeof(*(array)), \
offsetof(__typeof(*(array)), width_field), \
offsetof(__typeof(*(array)), height_field), width, height); \
})
static inline const void *
__vdev_find_nearest_size(const void *array, size_t array_size,
size_t entry_size, size_t width_offset,
size_t height_offset, uint32_t width, uint32_t height)
{
uint32_t error, min_error = (1u << 31);
const void *best = NULL;
unsigned int i;
int32_t w = width;
int32_t h = height;
if (!array)
return NULL;
for (i = 0; i < array_size; i++, array = (uint8_t *)array + entry_size) {
const int32_t *entry_width = (void *)((uint8_t *)array + width_offset);
const int32_t *entry_height =
(void *)((uint8_t *)array + height_offset);
error = v_abs(*entry_width - w) + v_abs(*entry_height - h);
if (error > min_error)
continue;
min_error = error;
best = array;
if (!error)
break;
}
return best;
}
const void *get_excam_api(const char *name);
#ifdef __cplusplus
extern "C" {
#endif
#endif