/* * 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, };