移动了文件位置
This commit is contained in:
10
drivers/source/camera/csi-rtos/ReadMe.txt
Normal file
10
drivers/source/camera/csi-rtos/ReadMe.txt
Normal 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
|
||||
409
drivers/source/camera/csi-rtos/cam_hal/cam_hal.c
Normal file
409
drivers/source/camera/csi-rtos/cam_hal/cam_hal.c
Normal 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, ¶m);
|
||||
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,
|
||||
¶m);
|
||||
|
||||
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, ¶m);
|
||||
ret = csi_dev->ops.channel_enum_pixfmt(cam_handle->csi_id,
|
||||
cam_handle->ch_id, ¶m);
|
||||
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,
|
||||
¶m);
|
||||
|
||||
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, ¶m);
|
||||
ret = csi_dev->ops.channel_enum_pixfmt(cam_handle->csi_id,
|
||||
cam_handle->ch_id, ¶m);
|
||||
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,
|
||||
¶m);
|
||||
|
||||
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,
|
||||
¶m);
|
||||
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;
|
||||
}
|
||||
890
drivers/source/camera/csi-rtos/csi_drv/csi_drv.c
Normal file
890
drivers/source/camera/csi-rtos/csi_drv/csi_drv.c
Normal 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,
|
||||
};
|
||||
61
drivers/source/camera/csi-rtos/csi_drv/csi_drv.h
Normal file
61
drivers/source/camera/csi-rtos/csi_drv/csi_drv.h
Normal 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__ */
|
||||
1927
drivers/source/camera/csi-rtos/csi_drv/csi_hw.c
Normal file
1927
drivers/source/camera/csi-rtos/csi_drv/csi_hw.c
Normal file
File diff suppressed because it is too large
Load Diff
196
drivers/source/camera/csi-rtos/csi_drv/csi_hw.h
Normal file
196
drivers/source/camera/csi-rtos/csi_drv/csi_hw.h
Normal 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
|
||||
131
drivers/source/camera/csi-rtos/csi_drv/csi_reg.h
Normal file
131
drivers/source/camera/csi-rtos/csi_drv/csi_reg.h
Normal 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__ */
|
||||
349
drivers/source/camera/csi-rtos/excam/ex_max9276.c
Normal file
349
drivers/source/camera/csi-rtos/excam/ex_max9276.c
Normal 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, ®, 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;
|
||||
}
|
||||
15
drivers/source/camera/csi-rtos/excam/ex_max9276.h
Normal file
15
drivers/source/camera/csi-rtos/excam/ex_max9276.h
Normal 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 */
|
||||
604
drivers/source/camera/csi-rtos/excam/ex_max96706.c
Normal file
604
drivers/source/camera/csi-rtos/excam/ex_max96706.c
Normal 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, ®, 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, ®, 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;
|
||||
}
|
||||
16
drivers/source/camera/csi-rtos/excam/ex_max96706.h
Normal file
16
drivers/source/camera/csi-rtos/excam/ex_max96706.h
Normal 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__ */
|
||||
3251
drivers/source/camera/csi-rtos/excam/ex_ov5640.c
Normal file
3251
drivers/source/camera/csi-rtos/excam/ex_ov5640.c
Normal file
File diff suppressed because it is too large
Load Diff
43
drivers/source/camera/csi-rtos/excam/ex_ov5640.h
Normal file
43
drivers/source/camera/csi-rtos/excam/ex_ov5640.h
Normal 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__*/
|
||||
434
drivers/source/camera/csi-rtos/excam/ex_tc9591.c
Normal file
434
drivers/source/camera/csi-rtos/excam/ex_tc9591.c
Normal 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);
|
||||
}
|
||||
|
||||
16
drivers/source/camera/csi-rtos/excam/ex_tc9591.h
Normal file
16
drivers/source/camera/csi-rtos/excam/ex_tc9591.h
Normal 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__ */
|
||||
418
drivers/source/camera/csi-rtos/excam/ex_ti926.c
Normal file
418
drivers/source/camera/csi-rtos/excam/ex_ti926.c
Normal 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, ®, 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;
|
||||
}
|
||||
21
drivers/source/camera/csi-rtos/excam/ex_ti926.h
Normal file
21
drivers/source/camera/csi-rtos/excam/ex_ti926.h
Normal 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__ */
|
||||
564
drivers/source/camera/csi-rtos/excam/ex_tp2825.c
Normal file
564
drivers/source/camera/csi-rtos/excam/ex_tp2825.c
Normal 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, ®, 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;
|
||||
}
|
||||
|
||||
16
drivers/source/camera/csi-rtos/excam/ex_tp2825.h
Normal file
16
drivers/source/camera/csi-rtos/excam/ex_tp2825.h
Normal 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__ */
|
||||
183
drivers/source/camera/csi-rtos/excam/ex_vCamera.c
Normal file
183
drivers/source/camera/csi-rtos/excam/ex_vCamera.c
Normal 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;
|
||||
}
|
||||
16
drivers/source/camera/csi-rtos/excam/ex_vCamera.h
Normal file
16
drivers/source/camera/csi-rtos/excam/ex_vCamera.h
Normal 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__ */
|
||||
308
drivers/source/camera/csi-rtos/excam/ex_x3d.c
Normal file
308
drivers/source/camera/csi-rtos/excam/ex_x3d.c
Normal 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, ®, 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;
|
||||
}
|
||||
16
drivers/source/camera/csi-rtos/excam/ex_x3d.h
Normal file
16
drivers/source/camera/csi-rtos/excam/ex_x3d.h
Normal 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__ */
|
||||
130
drivers/source/camera/csi-rtos/excam/exdev.c
Normal file
130
drivers/source/camera/csi-rtos/excam/exdev.c
Normal 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;
|
||||
}
|
||||
171
drivers/source/camera/csi-rtos/excam/exvdev.h
Normal file
171
drivers/source/camera/csi-rtos/excam/exvdev.h
Normal 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
|
||||
Reference in New Issue
Block a user