410 lines
13 KiB
C
410 lines
13 KiB
C
/*
|
|
* 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;
|
|
}
|