/* * csi_hw.c * * Semidrive platform csi controller driver core file (system independent) * * Copyright (C) 2022, Semidrive Communications Inc. * * This file is licensed under a dual GPLv2 or X11 license. */ #include "camera/sdrv-cam-os-def.h" #include "csi_reg.h" #include "csi_hw.h" #define REMAPPING_WORDS ((64 * 6 + 31) / 32) #define DEFAULT_SKIP_FRAMES 1 #define IMG_MAX_WIDTH ((1 << 16) - 1) #define IMG_MAX_HEIGHT ((1 << 16) - 1) #define FIELD_HAS_BOTH(field) (field == CSI_FIELD_INTERLACED) #define CSI_IMG_ERR_RETURN(csi_dev, img_id, param) \ do { \ if ((csi_dev == NULL) || (img_id >= CSI_IMG_NUM) || (param == NULL)) { \ csi_err("%s: failed to get csi_dev %p, img_id %d, param %p\n", \ __func__, csi_dev, img_id, param); \ return -1; \ } \ } while (0); enum csi_ip_version { CSI_V1 = 0, CSI_V2, }; enum frame_buffer_mode { FB_MODE_NORMAL = 0, FB_MODE_ROUND, }; /* Please define CSI_V1 for Kunlun, CSI_V2 for Taishan(E3) */ #ifndef CONFIG_CSI_VERSION #define CONFIG_CSI_VERSION CSI_V1 #endif #define IMG_STREAM_NUM_V1 2 #define IMG_STREAM_NUM 3 static int s_test_cnt; int is_addr_64bit = (sizeof(dma_addr_t) / 8); enum image_status { IMG_STATUS_IDLE = 0, IMG_STATUS_STREAM_ON, IMG_STATUS_MAX, }; int csi_cfg_img_buf(struct csi_hw_dev *csi_dev, uint32_t img_id, struct csi_img_buf *buf); struct csi_core_internal; struct csi_format_pack { uint32_t output_fmt; uint32_t bus_fmt; uint8_t is_parallel; uint8_t bpp[3]; uint8_t planes; uint8_t fc_dualline; uint8_t valid_pix_odd; /* valid pixel count in one pack(48 bits) for odd line */ uint8_t valid_pix_even; /* valid pixel count in one pack(48 bits) for even line */ uint8_t pack_cycle_odd; /* parallel: package how many bytes(one byte per cycle) in one pack(48 bits) for odd line */ uint8_t pack_cycle_even; /* parallel: package how many bytes(one byte per cycle) in one pack(48 bits) for even line */ uint8_t pack_cycle_uv_odd; /* parallel: U/V exists in pack for odd line */ uint8_t pack_cycle_uv_even; /* parallel: U/V exists in pack for even line */ uint32_t split[3]; /* data_sel, [0] for stream0(plane1), [1] for stream1(plane2) */ uint32_t pack[3]; /* color_depth, [0] for stream0(plane1), [1] for stream1(plane2) */ /* parallel: remapping pack(48bits) to 64bits( 4-ch x 16bits) */ const uint32_t *bitmap; }; struct csi_img_err_cnt { uint32_t bt_err; uint32_t bt_fatal; uint32_t crop_err; uint32_t pix_err; uint32_t overflow; uint32_t stream0_err; uint32_t stream1_err; uint32_t stream2_err; }; struct csi_img_internal { uint32_t id; uint32_t frm_cnt; uint32_t bus_type; /* should be enum csi_bus_type */ uint32_t bus_fmt; /* should be enum csi_mbus_fmt */ uint32_t bus_flag; uint32_t output_fmt; /* should be enum csi_out_fmt */ uint32_t field; uint32_t second_field; uint32_t skip_frame; uint32_t shadow_cnt; uint32_t status; spinlock_t status_lock; struct csi_img_err_cnt err_cnt; const struct csi_format_pack *pfmt; struct csi_img_buf next_buf; /* [0] for frame and odd field, [1] for even field */ struct csi_img_size size[2]; struct csi_img_crop crop[2]; uint32_t crop_update; spinlock_t crop_lock; uint32_t fb_mode; struct mem_range round_mem[3]; struct csi_core_internal *core; struct csi_callback_t irq_cb; }; struct csi_core_internal { uint32_t host_id; uint32_t inited; uint32_t sync; uint32_t sync_irq; uint32_t ip_version; reg_addr_t base; spinlock_t glb_reg_lock; struct csi_hw_dev ex_dev; struct csi_img_internal images[CSI_IMG_NUM]; }; struct wdma_fifo_t { uint32_t dfifo; uint32_t cfifo; }; static struct csi_core_internal csi_cores[CSI_HOST_NUM] = { { .host_id = CSI_HOST_0, .ip_version = CONFIG_CSI_VERSION, .ex_dev = { .host_id = CSI_HOST_0, }, }, { .host_id = CSI_HOST_1, .ip_version = CONFIG_CSI_VERSION, .ex_dev = { .host_id = CSI_HOST_1, }, }, { .host_id = CSI_HOST_2, .ip_version = CONFIG_CSI_VERSION, .ex_dev = { .host_id = CSI_HOST_2, }, }, }; /* * formats pack for mipi-csi input */ const struct csi_format_pack csi_mipi_fmts[] = { /* idx = 0 */ { .output_fmt = CSI_FMT_UYVY, .bus_fmt = CSI_MBUS_UYVY8_2X8, .bpp = {16}, .planes = 1, .valid_pix_odd = 1, /* 1: 2 pixels one pack, 0: 1 pixels one pack */ .valid_pix_even = 1, .split = {0x1A}, /* 3bits-per-sec: 0 | 0 | 3 | 2, */ .pack = {0x108}, /* 5bits-per-sec: 0 | 0 | 8 | 8, */ }, /* idx = 1 */ { .output_fmt = CSI_FMT_YUYV, .bus_fmt = CSI_MBUS_UYVY8_2X8, .bpp = {16}, .planes = 1, .valid_pix_odd = 1, .valid_pix_even = 1, .split = {0x13}, /* 3bits-per-sec: 0 | 0 | 2 | 3, */ .pack = {0x108}, /* 5bits-per-sec: 0 | 0 | 8 | 8, */ }, /* idx = 2 */ { .output_fmt = CSI_FMT_UYVY, .bus_fmt = CSI_MBUS_UYVY10_2X10, .bpp = {16}, .planes = 1, .valid_pix_odd = 1, .valid_pix_even = 1, .split = {0x1A}, /* 3bits-per-sec: 0 | 0 | 3 | 2, */ .pack = {0x108}, /* 5bits-per-sec: 0 | 0 | 8 | 8, */ }, /* idx = 3 */ { .output_fmt = CSI_FMT_YUYV, .bus_fmt = CSI_MBUS_UYVY10_2X10, .bpp = {16}, .planes = 1, .valid_pix_odd = 1, .valid_pix_even = 1, .split = {0x13}, /* 3bits-per-sec: 0 | 0 | 2 | 3, */ .pack = {0x108}, /* 5bits-per-sec: 0 | 0 | 8 | 8, */ }, /* idx = 4 */ { .output_fmt = CSI_FMT_RGB24, .bus_fmt = CSI_MBUS_RGB888_1X24, .bpp = {24}, .planes = 1, .valid_pix_odd = 0, .valid_pix_even = 0, .split = {0xE53}, /* 3bits-per-sec: x | 1 | 2 | 3, x=16bit(0) */ .pack = {0x2108}, /* 5bits-per-sec: 0 | 8 | 8 | 8, */ }, /* idx = 5 */ { .output_fmt = CSI_FMT_BGR24, .bus_fmt = CSI_MBUS_RGB888_1X24, .bpp = {24}, .planes = 1, .valid_pix_odd = 0, /* 0: 1 pixels one pack */ .valid_pix_even = 0, .split = {0xED1}, /* 3bits-per-sec: x | 3 | 2 | 1, x=16bit(0) */ .pack = {0x2108}, /* 5bits-per-sec: 0 | 8 | 8 | 8, */ }, }; /* * bit 00-15: 4 5 6 7 8 9 0 1, 2 3 4 5 6 7 8 9 * bit 16-31: 14 15 16 17 18 19 10 11, 12 13 14 15 16 17 18 19 * bit 32-47: 24 25 26 27 28 29 20 21, 22 23 24 25 26 27 28 29 * bit 48-63: 34 35 36 37 38 39 30 31, 32 33 34 35 36 37 38 39 */ const uint32_t mapping_default[][REMAPPING_WORDS] = { [CSI_V1] = {/* for Kunlun */ 0x481c6144, 0x40c20402, 0x2481c614, 0xd24503ce, 0xe34c2ca4, 0x4d24503c, 0x5c6da658, 0x85d65547, 0x75c6da65, 0xe69648e2, 0x28607de9, 0x9e69648e }, [CSI_V2] = {/* for Taishan 8bits */ 0x00000000, 0x20400000, 0x1C61440C, 0x00000000, 0xC2CA0000, 0x4503CE34, 0x00000000, 0x65540000, 0x6DA6585D, 0x00000000, 0x07DE0000, 0x9648E286 }, }; const uint32_t mapping_e3_24bit[REMAPPING_WORDS] = { /* for Taishan 24 bits */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x20400000, 0x1c61440c, 0x00000000, 0xa2480000, 0x3ce34c2c, 0x00000000, 0x24500000, 0x5d65544d, }; const uint32_t mapping_e3_24bit_dc[REMAPPING_WORDS] = { /* for Taishan 24 bits input from dmux */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40c20400, 0x2481c614, 0x00000000, 0xe34c2ca0, 0x4d24503c, 0x00000000, 0x85d65540, 0x75c6da65, }; /* * bit 00-15: 0 0 0 0 0 0 0 0, 2 3 4 5 6 7 8 9 * bit 16-31: 0 0 0 0 0 0 0 0, 12 13 14 15 16 17 18 19 * bit 32-47: 0 0 0 0 0 0 0 0, 0 0 0 0 0 0 0 0 * bit 48-63: 0 0 0 0 0 0 0 0, 0 0 0 0 0 0 0 0 */ const uint32_t mapping_e3_bt1120[REMAPPING_WORDS] = { /* for Taishan bt1120 16 bits */ 0x00000000, 0x40c20000, 0x2481c614, 0x00000000, 0xe34c0000, 0x4d24503c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; const uint32_t mapping_e3_16bit[REMAPPING_WORDS] = { /* for Taishan YUYV/UYVY 16 bits */ 0x00000000, 0x20400000, 0x1c61440c, 0x00000000, 0xa2480000, 0x3ce34c2c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; /* * formats pack for parallel input */ const struct csi_format_pack csi_parallel_fmts[] = { /* idx = 0 */ { .output_fmt = CSI_FMT_UYVY, .bus_fmt = CSI_MBUS_UYVY8_2X8, .bpp = {16}, .planes = 1, .valid_pix_odd = 1, .valid_pix_even = 1, .pack_cycle_odd = 3, /* 3: pack 4bytes in 48bits pack for odd line */ .pack_cycle_even = 3, /* 3: pack 4bytes in 48bits pack for even line */ .pack_cycle_uv_odd = 3, .pack_cycle_uv_even = 3, .split = {0x53}, /* 3bits-per-sec: 0 | 1 | 2 | 3, */ .pack = {0x42108, 0x3F}, /* 5bits-per-sec: 8 | 8 | 8 | 8, */ .bitmap = mapping_default[CONFIG_CSI_VERSION], }, /* idx = 1 */ { .output_fmt = CSI_FMT_YUYV, .bus_fmt = CSI_MBUS_UYVY8_2X8, .bpp = {16}, .planes = 1, .valid_pix_odd = 1, .valid_pix_even = 1, .pack_cycle_odd = 3, /* 3: pack 4bytes in 48bits pack for odd line */ .pack_cycle_even = 3, /* 3: pack 4bytes in 48bits pack for even line */ .pack_cycle_uv_odd = 3, .pack_cycle_uv_even = 3, .split = {0x21A}, /* 3bits-per-sec: 1 | 0 | 3 | 2, */ .pack = {0x42108, 0x3F}, /* 5bits-per-sec: 8 | 8 | 8 | 8, */ .bitmap = mapping_default[CONFIG_CSI_VERSION], }, /* idx = 2 */ { .output_fmt = CSI_FMT_UYVY, .bus_fmt = CSI_MBUS_YUYV8_2X8, .bpp = {16}, .planes = 1, .valid_pix_odd = 1, .valid_pix_even = 1, .pack_cycle_odd = 3, /* 3: pack 4bytes in 48bits pack for odd line */ .pack_cycle_even = 3, /* 3: pack 4bytes in 48bits pack for even line */ .pack_cycle_uv_odd = 3, .pack_cycle_uv_even = 3, .split = {0x21A}, /* 3bits-per-sec: 1 | 0 | 3 | 2, */ .pack = {0x42108}, /* 5bits-per-sec: 8 | 8 | 8 | 8, */ .bitmap = mapping_default[CONFIG_CSI_VERSION], }, /* idx = 3 */ { .output_fmt = CSI_FMT_YUYV, .bus_fmt = CSI_MBUS_YUYV8_2X8, .bpp = {16}, .planes = 1, .valid_pix_odd = 1, .valid_pix_even = 1, .pack_cycle_odd = 3, /* 3: pack 4bytes in 48bits pack for odd line */ .pack_cycle_even = 3, /* 3: pack 4bytes in 48bits pack for even line */ .pack_cycle_uv_odd = 3, .pack_cycle_uv_even = 3, .split = {0x53}, /* 3bits-per-sec: 0 | 1 | 2 | 3, */ .pack = {0x42108}, /* 5bits-per-sec: 8 | 8 | 8 | 8, */ .bitmap = mapping_default[CONFIG_CSI_VERSION], }, /* idx = 4 */ { .output_fmt = CSI_FMT_RGB24, .bus_fmt = CSI_MBUS_RGB888_1X24, .bpp = {24}, .planes = 1, .valid_pix_odd = 0, .valid_pix_even = 0, .pack_cycle_odd = 2, /* 2: pack 3bytes in 48bits pack for odd line */ .pack_cycle_even = 2, /* 2: pack 3bytes in 48bits pack for even line */ .split = {0xE53}, /* 3bits-per-sec: x | 1 | 2 | 3, x=16bit(0) */ .pack = {0x2108}, /* 5bits-per-sec: 0 | 8 | 8 | 8, */ .bitmap = mapping_default[CONFIG_CSI_VERSION], }, /* idx = 5 */ { .output_fmt = CSI_FMT_BGR24, .bus_fmt = CSI_MBUS_RGB888_1X24, .bpp = {24}, .planes = 1, .valid_pix_odd = 0, .valid_pix_even = 0, .pack_cycle_odd = 2, /* 2: pack 3bytes in 48bits pack for odd line */ .pack_cycle_even = 2, /* 2: pack 3bytes in 48bits pack for even line */ .split = {0xED1}, /* 3bits-per-sec: x | 3 | 2 | 1, x=16bit(0) */ .pack = {0x2108}, /* 5bits-per-sec: 0 | 8 | 8 | 8, */ .bitmap = mapping_default[CONFIG_CSI_VERSION], }, /* idx = 6 */ { .output_fmt = CSI_FMT_NV16, /* Y-Y-Y-Y, U-V-U-V, two planes*/ .bus_fmt = CSI_MBUS_UYVY8_2X8, .bpp = {8, 8}, .planes = 2, .valid_pix_odd = 1, .valid_pix_even = 1, .pack_cycle_odd = 3, /* 3: pack 4bytes in 48bits pack for odd line */ .pack_cycle_even = 3, /* 3: pack 4bytes in 48bits pack for even line */ .pack_cycle_uv_odd = 3, .pack_cycle_uv_even = 3, .split = {0x02, 0x0B}, /* 3bits-per-sec: 0|0|0|2, 0|0|1|3 */ .pack = {0x108, 0x108}, /* 5bits-per-sec: 0|0|8|8, 0|0|8|8 */ .bitmap = mapping_default[CONFIG_CSI_VERSION], }, /* idx = 7 */ { .output_fmt = CSI_FMT_NV61, /* Y-Y-Y-Y, V-U-V-U, two planes*/ .bus_fmt = CSI_MBUS_UYVY8_2X8, .bpp = {8, 8}, .planes = 2, .valid_pix_odd = 1, .valid_pix_even = 1, .pack_cycle_odd = 3, /* 3: pack 4bytes in 48bits pack for odd line */ .pack_cycle_even = 3, /* 3: pack 4bytes in 48bits pack for even line */ .pack_cycle_uv_odd = 3, .pack_cycle_uv_even = 3, .split = {0x02, 0x19}, /* 3bits-per-sec: 0|0|0|2, 0|0|3|1 */ .pack = {0x108, 0x108}, /* 5bits-per-sec: 0|0|8|8, 0|0|8|8 */ .bitmap = mapping_default[CONFIG_CSI_VERSION], }, /* idx = 8 */ { .output_fmt = CSI_FMT_YUV422P, /* Y, U, V, three planes*/ .bus_fmt = CSI_MBUS_UYVY8_2X8, .bpp = {8, 4, 4}, .planes = 3, .valid_pix_odd = 1, .valid_pix_even = 1, .pack_cycle_odd = 3, /* 3: pack 4bytes in 48bits pack for odd line */ .pack_cycle_even = 3, /* 3: pack 4bytes in 48bits pack for even line */ .pack_cycle_uv_odd = 3, .pack_cycle_uv_even = 3, .split = {0x02, 0x03, 0x01}, /* 0|0|0|2, 0|0|0|3, 0|0|0|1 */ .pack = {0x108, 0x08, 0x08}, /* 0|0|8|8, 0|0|0|8, 0|0|0|8 */ .bitmap = mapping_default[CONFIG_CSI_VERSION], }, /* idx = 9 */ { .output_fmt = CSI_FMT_RGB16, .bus_fmt = CSI_MBUS_RGB565_1X16, .bpp = {16}, .planes = 1, .valid_pix_odd = 1, .valid_pix_even = 1, .pack_cycle_odd = 3, /* 3: pack 4bytes in 48bits pack for odd line */ .pack_cycle_even = 3, /* 3: pack 4bytes in 48bits pack for even line */ .split = {0xE53}, /* 3bits-per-sec: x | 1 | 2 | 3, x=16bit(0) */ .pack = {0x42108}, /* 5bits-per-sec: 8 | 8 | 8 | 8, */ .bitmap = mapping_default[CONFIG_CSI_VERSION], }, /* idx = 10 */ { .output_fmt = CSI_FMT_RGB24, .bus_fmt = CSI_MBUS_RGB24_1X24, .bpp = {24}, .planes = 1, .valid_pix_odd = 0, .valid_pix_even = 0, .pack_cycle_odd = 0, /* 0: pack 1cycle in 48bits pack for odd line */ .pack_cycle_even = 0, /* 0: pack 1cycle in 48bits pack for even line */ .split = {0xE53}, /* 3bits-per-sec: x | 1 | 2 | 3, x=16bit(0) */ .pack = {0x2108}, /* 5bits-per-sec: 0 | 8 | 8 | 8, */ .bitmap = mapping_e3_24bit, }, /* idx = 11 */ { .output_fmt = CSI_FMT_RGB24, .bus_fmt = CSI_MBUS_BGR24_1X24, .bpp = {24}, .planes = 1, .valid_pix_odd = 0, .valid_pix_even = 0, .pack_cycle_odd = 0, /* 0: pack 1cycle in 48bits pack for odd line */ .pack_cycle_even = 0, /* 0: pack 1cycle in 48bits pack for even line */ .split = {0xED1}, /* 3bits-per-sec: x | 3 | 2 | 1, x=16bit(0) */ .pack = {0x2108}, /* 5bits-per-sec: 0 | 8 | 8 | 8, */ .bitmap = mapping_e3_24bit, }, /* idx = 12 */ { .output_fmt = CSI_FMT_NV16, /* Y-Y-Y-Y, U-V-U-V, two planes*/ .bus_fmt = CSI_MBUS_YUYV8_2X8, .bpp = {8, 8}, .planes = 2, .valid_pix_odd = 1, .valid_pix_even = 1, .pack_cycle_odd = 3, /* 3: pack 4bytes in 48bits pack for odd line */ .pack_cycle_even = 3, /* 3: pack 4bytes in 48bits pack for even line */ .pack_cycle_uv_odd = 3, .pack_cycle_uv_even = 3, .split = {0x0B, 0x02}, /* 3bits-per-sec: 0|0|0|2, 0|0|1|3 */ .pack = {0x108, 0x108}, /* 5bits-per-sec: 0|0|8|8, 0|0|8|8 */ .bitmap = mapping_default[CONFIG_CSI_VERSION], }, /* idx = 13 */ { .output_fmt = CSI_FMT_YUYV, .bus_fmt = CSI_MBUS_YUYV8_1X16, // YUYV8_1X16 .bpp = {16}, .planes = 1, .valid_pix_odd = 0, .valid_pix_even = 0, .pack_cycle_odd = 0, /* 0: pack 1cycle in 48bits pack for odd line */ .pack_cycle_even = 0, /* 0: pack 1cycle in 48bits pack for even line */ .split = {0x008}, /* 3bits-per-sec: 0 | 0 | 1 | 0, x=16bit(0) */ .pack = {0x108}, /* 5bits-per-sec: 0 | 0 | 8 | 8, */ .bitmap = mapping_e3_bt1120, }, /* idx = 14 */ { .output_fmt = CSI_FMT_NV16, .bus_fmt = CSI_MBUS_YUYV8_1X16, // YUYV8_1X16 .bpp = {8, 8}, .planes = 2, .valid_pix_odd = 0, .valid_pix_even = 0, .pack_cycle_odd = 0, /* 0: pack 1cycle in 48bits pack for odd line */ .pack_cycle_even = 0, /* 0: pack 1cycle in 48bits pack for even line */ .split = {0x01, 0x00},/* 3bits-per-sec: 0|0|0|1, 0|0|0|0 */ .pack = {0x08, 0x08}, /* 5bits-per-sec: 0|0|0|8, 0|0|0|8 */ .bitmap = mapping_e3_bt1120, }, /* idx = 15 */ { .output_fmt = CSI_FMT_YUYV, .bus_fmt = CSI_MBUS_UYVY8_1X16, // UYVY8_1X16 .bpp = {16}, .planes = 1, .valid_pix_odd = 0, .valid_pix_even = 0, .pack_cycle_odd = 0, /* 0: pack 1cycle in 48bits pack for odd line */ .pack_cycle_even = 0, /* 0: pack 1cycle in 48bits pack for even line */ .split = {0x001}, /* 3bits-per-sec: 0 | 0 | 0 | 1, x=16bit(0) */ .pack = {0x108}, /* 5bits-per-sec: 0 | 0 | 8 | 8, */ .bitmap = mapping_e3_bt1120, }, /* idx = 16 */ { .output_fmt = CSI_FMT_NV16, .bus_fmt = CSI_MBUS_UYVY8_1X16, // UYVY8_1X16 .bpp = {8, 8}, .planes = 2, .valid_pix_odd = 0, .valid_pix_even = 0, .pack_cycle_odd = 0, /* 0: pack 1cycle in 48bits pack for odd line */ .pack_cycle_even = 0, /* 0: pack 1cycle in 48bits pack for even line */ .split = {0x00, 0x01},/* 3bits-per-sec: 0|0|0|0, 0|0|0|1 */ .pack = {0x08, 0x08}, /* 5bits-per-sec: 0|0|0|8, 0|0|0|8 */ .bitmap = mapping_e3_bt1120, }, }; static inline void csi_set(struct csi_core_internal *csi_core, uint32_t addr, uint32_t mask, uint32_t set_val) { unsigned long flags; spin_lock_irqsave(&csi_core->glb_reg_lock, flags); reg_write(csi_core->base, addr, (reg_read(csi_core->base, addr) & ~mask) | set_val); spin_unlock_irqrestore(&csi_core->glb_reg_lock, flags); } /* set reg to default value */ static void csi_img_set_default(struct csi_img_internal *img) { struct csi_core_internal *csi_core = img->core; int i, n = img->id; reg_write(csi_core->base, CSI_REG_IMG_RGBY_BADDR_H(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_RGBY_BADDR_L(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_U_BADDR_H(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_U_BADDR_L(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_V_BADDR_H(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_V_BADDR_L(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_RGBY_STRIDE(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_U_STRIDE(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_V_STRIDE(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_SIZE(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_IPI_CTRL(n), 0x4000); reg_write(csi_core->base, CSI_REG_IMG_IF_PIXEL_MASK0(n), 0xFFFFFFFF); reg_write(csi_core->base, CSI_REG_IMG_IF_PIXEL_MASK1(n), 0xFFFFFFFF); reg_write(csi_core->base, CSI_REG_IMG_CHN_CTRL(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_CHN_SPLIT0(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_CHN_SPLIT1(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_CHN_CROP0(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_CHN_CROP1(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_CHN_PACK0(n), 0x00); reg_write(csi_core->base, CSI_REG_IMG_CHN_PACK1(n), 0x00); i = n * 2; reg_write(csi_core->base, CSI_REG_WDMA_CHN_DFIFO(i), 0x00400008); reg_write(csi_core->base, CSI_REG_WDMA_CHN_CFIFO(i), 0x04); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL0(i), 0x3C8); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL1(i), 0x00); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL2(i), 0x0B); i++; reg_write(csi_core->base, CSI_REG_WDMA_CHN_DFIFO(i), 0x00400008); reg_write(csi_core->base, CSI_REG_WDMA_CHN_CFIFO(i), 0x04); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL0(i), 0x3C8); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL1(i), 0x00); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL2(i), 0x0B); reg_write(csi_core->base, CSI_REG_PARA_BT_CTRL0(n), 0x00); reg_write(csi_core->base, CSI_REG_PARA_BT_CTRL1(n), 0x00); reg_write(csi_core->base, CSI_REG_PARA_BT_CTRL2(n), 0x00); if (csi_core->ip_version < CSI_V2) return; i = 0; reg_write(csi_core->base, CSI_REG_WDMA_CHN_DFIFO(i), 0x00400008); reg_write(csi_core->base, CSI_REG_WDMA_CHN_CFIFO(i), 0x04); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL0(i), 0x3C8); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL1(i), 0x00); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL2(i), 0x0B); i = 1; reg_write(csi_core->base, CSI_REG_WDMA_CHN_DFIFO(i), 0x00200004); reg_write(csi_core->base, CSI_REG_WDMA_CHN_CFIFO(i), 0x04); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL0(i), 0x3C8); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL1(i), 0x00); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL2(i), 0x0B); i = 2; reg_write(csi_core->base, CSI_REG_WDMA_CHN_DFIFO(i), 0x00200004); reg_write(csi_core->base, CSI_REG_WDMA_CHN_CFIFO(i), 0x04); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL0(i), 0x3C8); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL1(i), 0x00); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL2(i), 0x0B); i = 3; reg_write(csi_core->base, CSI_REG_WDMA_CHN_DFIFO(i), 0x00400008); reg_write(csi_core->base, CSI_REG_WDMA_CHN_CFIFO(i), 0x04); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL0(i), 0x3C8); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL1(i), 0x00); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL2(i), 0x0B); reg_write(csi_core->base, CSI_REG_IMG_FB_CTRL(n), 0x00); csi_set(csi_core, CSI_REG_ENABLE, (3 << 16), 0); } /* set ipi ctrl, channel ctrl */ static int csi_img_set_channel(struct csi_img_internal *img) { struct csi_core_internal *csi_core = img->core; const struct csi_format_pack *fmt = img->pfmt; int i, n, stream_num; uint32_t chn_ctrl, ipi_ctrl; n = img->id; stream_num = (fmt->planes > IMG_STREAM_NUM) ? IMG_STREAM_NUM : fmt->planes; ipi_ctrl = reg_read(csi_core->base, CSI_REG_IMG_IPI_CTRL(n)); ipi_ctrl &= ~(0x7); ipi_ctrl |= (img->bus_type & 7); if (img->bus_type == CSI_BUS_MIPICSI2) { switch (fmt->bus_fmt) { case CSI_MBUS_UYVY8_2X8: case CSI_MBUS_YUYV8_2X8: case CSI_MBUS_UYVY10_2X10: ipi_ctrl |= (1 << 6); break; case CSI_MBUS_UYVY8_1_5X8: ipi_ctrl |= (1 << 4); break; case CSI_MBUS_VYUY8_1_5X8: ipi_ctrl |= (1 << 3); break; default: break; } } else { ipi_ctrl &= ~(0x1F << 9); ipi_ctrl |= ((fmt->fc_dualline & 1) << 13); ipi_ctrl |= ((fmt->valid_pix_even & 3) << 11) | ((fmt->valid_pix_odd & 3) << 9); } s_test_cnt++; // img->skip_frame = (s_test_cnt>>3)&3; csi_err("s_test_cnt %d, skip_frame: %d\n", s_test_cnt, img->skip_frame); /* vsync_mask_count */ ipi_ctrl &= ~(3 << 7); ipi_ctrl |= ((img->skip_frame & 3) << 7); reg_write(csi_core->base, CSI_REG_IMG_IPI_CTRL(n), ipi_ctrl); /* set channel ctrl: stream enable */ chn_ctrl = reg_read(csi_core->base, CSI_REG_IMG_CHN_CTRL(n)); chn_ctrl &= ~(0x7); chn_ctrl |= ((1 << stream_num) - 1); reg_write(csi_core->base, CSI_REG_IMG_CHN_CTRL(n), chn_ctrl); for (i = 0; i < stream_num; i++) { int split[4], color_depth[4]; split[0] = (fmt->split[i] & 0x7); split[1] = ((fmt->split[i] >> 3) & 0x7); split[2] = ((fmt->split[i] >> 6) & 0x7); split[3] = ((fmt->split[i] >> 9) & 0x7); color_depth[0] = (fmt->pack[i] & 0x1F); color_depth[1] = ((fmt->pack[i] >> 5) & 0x1F); color_depth[2] = ((fmt->pack[i] >> 10) & 0x1F); color_depth[3] = ((fmt->pack[i] >> 15) & 0x1F); csi_info("%s: pix_fmt: 0x%08x, bus_type 0x%x\n", __func__, fmt->output_fmt, fmt->bus_fmt); csi_info("plane.%d, split= [%d, %d, %d, %d], color_depth = [%d, %d, " "%d, %d]\n", i, split[3], split[2], split[1], split[0], color_depth[3], color_depth[2], color_depth[1], color_depth[0]); reg_write(csi_core->base, CSI_REG_IMG_CHN_SPLIT0(n) + i * 4, fmt->split[i]); reg_write(csi_core->base, CSI_REG_IMG_CHN_PACK0(n) + i * 4, fmt->pack[i]); } return 0; } /* set crop */ static int csi_img_set_crop(struct csi_img_internal *img) { struct csi_core_internal *csi_core = img->core; const struct csi_format_pack *fmt = img->pfmt; int i, n, stream_num; uint32_t crop_en, start, length, val; uint32_t chn_ctrl; n = img->id; stream_num = (fmt->planes > IMG_STREAM_NUM) ? IMG_STREAM_NUM : fmt->planes; /* set hcrop: width crop */ crop_en = (img->crop[0].w == img->size[0].w) ? 0 : 1; if (!crop_en) { csi_set(csi_core, CSI_REG_IMG_CHN_CTRL(n), 0x38, 0); goto set_vcrop; } csi_info("%s: csi%d img%d width crop enable\n", __func__, csi_core->host_id, img->id); chn_ctrl = reg_read(csi_core->base, CSI_REG_IMG_CHN_CTRL(n)); chn_ctrl &= ~(0x38); /* bit[5:3] */ crop_en = (1 << stream_num) - 1; chn_ctrl |= (crop_en << 3); reg_write(csi_core->base, CSI_REG_IMG_CHN_CTRL(n), chn_ctrl); for (i = 0; i < stream_num; i++) { start = img->crop[0].x / (fmt->valid_pix_odd + 1); length = img->crop[0].w / (fmt->valid_pix_odd + 1); val = (((length - 1) & 0xFFFF) << 16) | (start & 0xFFFF); reg_write(csi_core->base, CSI_REG_IMG_CHN_CROP0(n) + i * 4, val); } set_vcrop: /* height crop is not supported by Kunlun */ if (csi_core->ip_version < CSI_V2) return 0; /* set vcrop: height crop, height crop is supported by Taishan(E3) */ crop_en = (img->crop[0].h == img->size[0].h) ? 0 : 1; if (!crop_en) { csi_set(csi_core, CSI_REG_IMG_CHN_CTRL(n), 0x1C00, 0); return 0; } csi_info("%s: csi%d img%d height crop enable\n", __func__, csi_core->host_id, img->id); chn_ctrl = reg_read(csi_core->base, CSI_REG_IMG_CHN_CTRL(n)); chn_ctrl &= ~(0x1C00); /* bit[12:10] */ crop_en = (1 << stream_num) - 1; chn_ctrl |= (crop_en << 10); reg_write(csi_core->base, CSI_REG_IMG_CHN_CTRL(n), chn_ctrl); for (i = 0; i < stream_num; i++) { start = img->crop[0].y; length = img->crop[0].h; val = (((length - 1) & 0xFFFF) << 16) | (start & 0xFFFF); reg_write(csi_core->base, CSI_REG_IMG_CHN_VCROP0(n) + i * 4, val); } return 0; } /* set size, stride */ static int csi_img_set_size(struct csi_img_internal *img) { struct csi_core_internal *csi_core = img->core; const struct csi_format_pack *fmt = img->pfmt; int i, n = img->id; uint32_t width, height, out_width, stride; /* set size */ if (img->bus_type == CSI_BUS_MIPICSI2) width = img->size[0].w - 1; else width = img->size[0].w - (1 + fmt->valid_pix_odd); height = img->size[0].h - 1; reg_write(csi_core->base, CSI_REG_IMG_SIZE(n), ((width & 0xFFFF) << 16) | (height & 0xFFFF)); /* set stride */ out_width = img->crop[0].w; for (i = 0; i < fmt->planes; i++) { if (FIELD_HAS_BOTH(img->field)) stride = 2 * out_width * fmt->bpp[i] / 8; else stride = out_width * fmt->bpp[i] / 8; reg_write(csi_core->base, CSI_REG_IMG_RGBY_STRIDE(n) + i * 4, stride); } csi_img_set_crop(img); return 0; } static int csi_img_set_wdma(struct csi_img_internal *img) { struct csi_core_internal *csi_core = img->core; int i, k, n; uint32_t axi0 = 0x3C8; uint32_t axi1 = 0x00; uint32_t axi2 = 0x0B; struct wdma_fifo_t wfifo[2][2] = { { /* for one plane output, assign all fifo to stream0 */ {0x00400010, 0x08}, {0x00400000, 0x00}, }, { /* for two planes output */ {0x00400008, 0x04}, {0x00400008, 0x04}, }, }; k = (img->pfmt->planes == 1) ? 0 : 1; n = img->id * 2; for (i = 0; i < IMG_STREAM_NUM_V1; i++, n++) { reg_write(csi_core->base, CSI_REG_WDMA_CHN_DFIFO(n), wfifo[k][i].dfifo); reg_write(csi_core->base, CSI_REG_WDMA_CHN_CFIFO(n), wfifo[k][i].cfifo); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL0(n), axi0); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL1(n), axi1); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL2(n), axi2); } return 0; } /* * for CSI V2 (taishan) * ch0: thread streams max, ch1: only one stream */ static int csi_img_set_wdma_v2(struct csi_img_internal *img) { struct csi_core_internal *csi_core = img->core; int i, k, n, stream_num; uint32_t axi0 = 0x3C8; uint32_t axi1 = 0x00; uint32_t axi2 = 0x0B; struct wdma_fifo_t wfifo[4][3] = { { /* for one plane output, assign all fifo to stream0 */ {0x00400010, 0x08}, {0x00200000, 0x00}, {0x00200000, 0x00}, }, { /* for two planes output */ {0x00400008, 0x04}, {0x00400008, 0x04}, {0x00200000, 0x00}, }, { /* for three planes output */ {0x00400008, 0x04}, {0x00200004, 0x02}, {0x00200004, 0x02}, }, { /* for channel[1] one plane output, assign all fifo to stream0 */ {0x00400008, 0x04}, {0x00400000, 0x00}, {0x00400000, 0x00}, }, }; k = (img->id == 1) ? 3 : (img->pfmt->planes - 1); stream_num = (img->id == 1) ? 1 : IMG_STREAM_NUM; n = (img->id == 1) ? 3 : 0; for (i = 0; i < stream_num; i++, n++) { csi_info("ch%d, n=%d, i=%d, k=%d, difo 0x%08x cfifo 0x%08x\n", img->id, n, i, k, wfifo[k][i].dfifo, wfifo[k][i].cfifo); reg_write(csi_core->base, CSI_REG_WDMA_CHN_DFIFO(n), wfifo[k][i].dfifo); reg_write(csi_core->base, CSI_REG_WDMA_CHN_CFIFO(n), wfifo[k][i].cfifo); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL0(n), axi0); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL1(n), axi1); reg_write(csi_core->base, CSI_REG_WDMA_CHN_AXI_CTRL2(n), axi2); } return 0; } static int csi_img_set_para_bt(struct csi_img_internal *img) { struct csi_core_internal *csi_core = img->core; int i; uint32_t value0 = 0, value1 = 0, value2 = 0, height; const struct csi_format_pack *fmt = img->pfmt; const uint32_t *map = fmt->bitmap; if (img->bus_type == CSI_BUS_MIPICSI2) { reg_write(csi_core->base, CSI_REG_PARA_BT_CTRL0(img->id), value0); return 0; } if ((img->id == 1) && (fmt->bus_fmt == CSI_MBUS_RGB24_1X24 || fmt->bus_fmt == CSI_MBUS_BGR24_1X24)) { map = mapping_e3_24bit_dc; } if ((img->bus_type != CSI_BUS_BT1120_SDR) && (fmt->bus_fmt == CSI_MBUS_UYVY8_1X16 || fmt->bus_fmt == CSI_MBUS_YUYV8_1X16)) { map = mapping_e3_16bit; } value0 |= (1 << 0) | ((fmt->pack_cycle_odd & 3) << 1) | ((fmt->pack_cycle_even & 3) << 3) | ((fmt->pack_cycle_uv_odd & 3) << 6) | ((fmt->pack_cycle_uv_even & 3) << 8); /* data enable pol */ if (img->bus_flag & MBUS_DE_ACTIVE_LOW) value0 |= (1 << 10); /* vsync pol */ if (img->bus_flag & MBUS_VSYNC_ACTIVE_LOW) value0 |= (1 << 11); /* hsync pol */ if (img->bus_flag & MBUS_HSYNC_ACTIVE_HIGH) value0 |= (1 << 12); /* pclk pol */ if (img->bus_flag & MBUS_PCLK_SAMPLE_RISING) value0 |= (1 << 13); if (img->bus_type == CSI_BUS_BT656 || img->bus_type == CSI_BUS_BT1120_SDR) { if (FIELD_HAS_BOTH(img->field)) { height = img->size[1].h - 1; value0 |= (1 << 14) | (height << 15); value1 = (1 << 2); } else { value1 = (1 << 0) | (1 << 2) | (img->size[0].w << 16); } } else if (img->bus_type == CSI_BUS_PARALLEL4) { value1 = (1 << 0) | (1 << 2) | (img->size[0].w << 16); } if (FIELD_HAS_BOTH(img->field)) value2 = 0; else value2 = (1 << 17); reg_write(csi_core->base, CSI_REG_PARA_BT_CTRL0(img->id), value0); reg_write(csi_core->base, CSI_REG_PARA_BT_CTRL1(img->id), value1); reg_write(csi_core->base, CSI_REG_PARA_BT_CTRL2(img->id), value2); for (i = 0; i < REMAPPING_WORDS; i++) reg_write(csi_core->base, CSI_REG_PIXEL_MAP(img->id) + i * 4, map[i]); /* for Mapping debug if (1) { uint32_t imap[3], bit_map[16]; int j; for (j = 0; j < 4; j++) { imap[0] = map[j * 3 + 0]; imap[1] = map[j * 3 + 1]; imap[2] = map[j * 3 + 2]; bit_map[0] = ((imap[0] >> 0) & 0x3F); bit_map[1] = ((imap[0] >> 6) & 0x3F); bit_map[2] = ((imap[0] >> 12) & 0x3F); bit_map[3] = ((imap[0] >> 18) & 0x3F); bit_map[4] = ((imap[0] >> 24) & 0x3F); bit_map[5] = ((imap[0] >> 30) & 0x3); bit_map[5] |= ((imap[1] << 2) & 0x3C); bit_map[6] = ((imap[1] >> 4) & 0x3F); bit_map[7] = ((imap[1] >> 10) & 0x3F); bit_map[8] = ((imap[1] >> 16) & 0x3F); bit_map[9] = ((imap[1] >> 22) & 0x3F); bit_map[10] = ((imap[1] >> 28) & 0xF); bit_map[10] |= ((imap[2] << 4) & 0x30); bit_map[11] = ((imap[2] >> 2) & 0x3F); bit_map[12] = ((imap[2] >> 8) & 0x3F); bit_map[13] = ((imap[2] >> 14) & 0x3F); bit_map[14] = ((imap[2] >> 20) & 0x3F); bit_map[15] = ((imap[2] >> 26) & 0x3F); csi_info("map idx %d, data %d %d %d %d %d %d %d %d, %d %d %d %d %d %d %d %d\n", j * 16, bit_map[0], bit_map[1], bit_map[2], bit_map[3], bit_map[4], bit_map[5], bit_map[6], bit_map[7], bit_map[8], bit_map[9], bit_map[10], bit_map[11], bit_map[12], bit_map[13], bit_map[14], bit_map[15]); } } */ return 0; } static int csi_img_set_memrange(struct csi_img_internal *img) { struct csi_core_internal *csi_core = img->core; int n = img->id; uint32_t mask = (1 << 28) - 1; // asw/dc singnal enable csi_info("%s: asw/dc irq enable\n", __func__); csi_set(csi_core, CSI_REG_ENABLE, (3 << 16), (3 << 16)); reg_write(csi_core->base, CSI_REG_IMG_FB_CTRL(n), (img->fb_mode & 1)); if (img->fb_mode == FB_MODE_NORMAL) return 0; csi_set(csi_core, CSI_REG_ENABLE, (3 << 16), (3 << 16)); reg_write(csi_core->base, CSI_REG_IMG_UP_RGBY_ADDR(n), ((img->round_mem[0].up >> 12) & mask)); reg_write(csi_core->base, CSI_REG_IMG_LOW_RGBY_ADDR(n), ((img->round_mem[0].low >> 12) & mask)); reg_write(csi_core->base, CSI_REG_IMG_UP_U_ADDR(n), ((img->round_mem[1].up >> 12) & mask)); reg_write(csi_core->base, CSI_REG_IMG_LOW_U_ADDR(n), ((img->round_mem[1].low >> 12) & mask)); reg_write(csi_core->base, CSI_REG_IMG_UP_V_ADDR(n), ((img->round_mem[2].up >> 12) & mask)); reg_write(csi_core->base, CSI_REG_IMG_LOW_V_ADDR(n), ((img->round_mem[2].low >> 12) & mask)); /* TODO - temp set circle buffer range and stride */ csi_set(csi_core, CSI_REG_IMG_FB_CTRL(n), (7 << 1), (img->round_mem[2].low << 1)); reg_write(csi_core->base, CSI_REG_IMG_RGBY_STRIDE(n) + 0, img->round_mem[2].up); return 0; } /* set img 1~3 buffer address base on buffer of img0 for sync mode */ static void csi_img_set_sync_buf(struct csi_img_internal *img, struct csi_img_buf *buf) { int i, n = img->id; struct csi_core_internal *csi_core = img->core; const struct csi_format_pack *fmt = img->pfmt; dma_addr_t paddr[3]; uint32_t *ptr_u32; uint32_t addr_l[3], addr_h[3], stride[3], height[3], offset[3]; for (i = 0; i < (int)fmt->planes; i++) { stride[i] = reg_read(csi_core->base, CSI_REG_IMG_RGBY_STRIDE(n) + i * 4); height[i] = img->size[0].h; /* todo: U/V height may be 1/2, such as YUV420 */ offset[i] = n * stride[i] * height[i]; paddr[i] = buf->paddr[i] + offset[i]; ptr_u32 = (uint32_t *)&paddr[i]; addr_l[i] = ptr_u32[0]; addr_h[i] = (is_addr_64bit ? ptr_u32[1] : 0); reg_write(csi_core->base, CSI_REG_IMG_RGBY_BADDR_H(n) + i * 8, addr_h[i]); reg_write(csi_core->base, CSI_REG_IMG_RGBY_BADDR_L(n) + i * 8, addr_l[i]); } csi_set(csi_core, CSI_REG_REG_LOAD, (1 << n), (1 << n)); } /* update buffer addr for second field */ static int csi_img_set_field_buf(struct csi_img_internal *img, struct csi_img_buf *buf) { struct csi_core_internal *csi_core = img->core; const struct csi_format_pack *fmt = img->pfmt; int i, n = img->id; dma_addr_t paddr[3]; uint32_t *ptr_u32; uint32_t addr_l[3], addr_h[3], stride[3]; /* default set bottom field as second */ for (i = 0; i < (int)fmt->planes; i++) { stride[i] = reg_read(csi_core->base, CSI_REG_IMG_RGBY_STRIDE(n) + i * 4); paddr[i] = buf->paddr[i] + (stride[i] / 2); ptr_u32 = (uint32_t *)&paddr[i]; addr_l[i] = ptr_u32[0]; addr_h[i] = (is_addr_64bit ? ptr_u32[1] : 0); reg_write(csi_core->base, CSI_REG_IMG_RGBY_BADDR_H(n) + i * 8, addr_h[i]); reg_write(csi_core->base, CSI_REG_IMG_RGBY_BADDR_L(n) + i * 8, addr_l[i]); } /* shadow_set_n*/ csi_set(csi_core, CSI_REG_REG_LOAD, (1 << n), (1 << n)); return 0; } static int csi_img_set_regs(struct csi_img_internal *img) { int ret = 0, n = img->id; uint32_t set_val; struct csi_core_internal *csi_core = img->core; struct csi_img_buf *base_buf = &img->next_buf; csi_img_set_default(img); ret |= csi_cfg_img_buf(&csi_core->ex_dev, n, &img->next_buf); ret |= csi_img_set_channel(img); ret |= csi_img_set_size(img); ret |= csi_img_set_para_bt(img); if (csi_core->ip_version < CSI_V2) { ret |= csi_img_set_wdma(img); } else { ret |= csi_img_set_wdma_v2(img); ret |= csi_img_set_memrange(img); } if (ret) { csi_err("%s, csi%d, img %d, failed to set regs\n", __func__, csi_core->host_id, img->id); return ret; } if (csi_core->sync == 0) { /* enalbe irq */ if (img->bus_type != CSI_BUS_BT656) csi_set(csi_core, CSI_REG_INT_MASK0, INT0_MASK(n), INT0_MASK(n)); else csi_set(csi_core, CSI_REG_INT_MASK0, INT0_BT_MASK(n), INT0_BT_MASK(n)); csi_set(csi_core, CSI_REG_INT_MASK1, INT1_ERR_MASK(n), INT1_ERR_MASK(n)); csi_err("irq_mask: 0x%08x 0x%08x\n", reg_read(csi_core->base, CSI_REG_INT_MASK0), reg_read(csi_core->base, CSI_REG_INT_MASK1)); /* [17]: WDMA_CFG_LOAD, [15:8]: update mask_n, [7:4]: shadow_update_n * by software(force shashow) */ set_val = ((1 << 17)) | 0x5500 | (0x11 << n); csi_set(csi_core, CSI_REG_REG_LOAD, set_val, set_val); /* enable stream */ csi_set(csi_core, CSI_REG_ENABLE, (1 << n), (1 << n)); return 0; } for (n = CSI_IMG1; n <= CSI_IMG3; n++) { img = &csi_core->images[n]; csi_img_set_default(img); ret |= csi_img_set_channel(img); ret |= csi_img_set_size(img); ret |= csi_img_set_para_bt(img); ret |= csi_img_set_wdma(img); if (ret) { csi_err("%s, csi%d, img %d, failed to set regs\n", __func__, csi_core->host_id, img->id); return ret; } csi_img_set_sync_buf(&csi_core->images[n], base_buf); /* enalbe irq */ csi_set(csi_core, CSI_REG_INT_MASK0, INT0_MASK(n), INT0_MASK(n)); csi_set(csi_core, CSI_REG_INT_MASK1, INT1_ERR_MASK(n), INT1_ERR_MASK(n)); } set_val = ((1 << 17)) | 0xFFF0; csi_set(csi_core, CSI_REG_REG_LOAD, set_val, set_val); /* enable all stream */ csi_set(csi_core, CSI_REG_ENABLE, 0xF, 0xF); return 0; } /* setup parameter base on input configuration */ static int csi_img_setup_param(struct csi_img_internal *img) { int i, total; uint32_t n; struct csi_core_internal *csi_core = img->core; const struct csi_format_pack *fmt_pack; if ((img->bus_type >= CSI_BUS_TYPE_MAX) || (img->bus_type == CSI_BUS_PARALLEL1) || (img->bus_type == CSI_BUS_BT1120_DDR)) { csi_err("%s, csi%d, img %d, unsupported bus_type %d\n", __func__, csi_core->host_id, img->id, img->bus_type); return -1; } if (img->field != CSI_FIELD_NONE && img->field != CSI_FIELD_INTERLACED) { csi_err("%s, csi%d, img %d, unsupported field_type %d\n", __func__, csi_core->host_id, img->id, img->field); return -1; } total = FIELD_HAS_BOTH(img->field) ? 2 : 1; for (i = 0; i < total; i++) { if ((img->size[i].w > IMG_MAX_WIDTH) || (img->size[i].h > IMG_MAX_HEIGHT)) { csi_err("%s, csi%d, img %d, unsupported size %d %d\n", __func__, csi_core->host_id, img->id, img->size[i].w, img->size[i].h); return -1; } if (img->crop[i].x == 0 && img->crop[i].y == 0 && img->crop[i].w == 0 && img->crop[i].h == 0) { img->crop[i].w = img->size[i].w; img->crop[i].h = img->size[i].h; } if (((img->crop[i].x + img->crop[i].w) > img->size[i].w) || ((img->crop[i].y + img->crop[i].h) > img->size[i].h)) { csi_err( "%s, csi%d, img %d, error crop (%d %d %d %d) for size %d %d\n", __func__, csi_core->host_id, img->id, img->crop[i].x, img->crop[i].y, img->crop[i].w, img->crop[i].h, img->size[i].w, img->size[i].h); return -1; } } if (img->bus_type == CSI_BUS_MIPICSI2) { fmt_pack = &csi_mipi_fmts[0]; total = sizeof(csi_mipi_fmts) / sizeof(csi_mipi_fmts[0]); } else { fmt_pack = &csi_parallel_fmts[0]; total = sizeof(csi_parallel_fmts) / sizeof(csi_parallel_fmts[0]); } for (i = 0; i < total; i++, fmt_pack++) { if (fmt_pack->bus_fmt != img->bus_fmt || fmt_pack->output_fmt != img->output_fmt) continue; img->pfmt = fmt_pack; csi_info("%s, csi%d, img%d, bus type %d bus_fmt %d, outfmt: 0x%08x, " "index %d\n", __func__, csi_core->host_id, img->id, img->bus_type, img->bus_fmt, fmt_pack->output_fmt, i); } if (img->pfmt == NULL) { csi_err( "%s, csi%d, img%d, bus type %d bus_fmt %d, error outfmt: 0x%08x\n", __func__, csi_core->host_id, img->id, img->bus_type, img->bus_fmt, fmt_pack->output_fmt); } if (img->bus_type == CSI_BUS_MIPICSI2) img->skip_frame = 0; else img->skip_frame = DEFAULT_SKIP_FRAMES; csi_info("%s, csi%d img%d setup params done\n", __func__, img->core->host_id, img->id); if (csi_core->sync == 0) return 0; for (n = CSI_IMG1; n <= CSI_IMG3; n++) { img = &csi_core->images[n]; memcpy(img, &csi_core->images[0], sizeof(struct csi_img_internal)); spin_lock_init(&img->status_lock); img->id = n; } return 0; } static void csi_err_handle(struct csi_img_internal *img, uint32_t sync, uint32_t value0, uint32_t value1) { int n = img->id; if (sync) { if (value0 & CSI_INT0_BT_ERR) img->err_cnt.bt_err++; if (value0 & CSI_INT0_BT_FATAL) img->err_cnt.bt_fatal++; if (value1 & (CSI_INT1_CROP_ERR0 | CSI_INT1_CROP_ERR1 | CSI_INT1_CROP_ERR2 | CSI_INT1_CROP_ERR3)) img->err_cnt.crop_err++; if (value1 & (CSI_INT1_PIXEL_ERR0 | CSI_INT1_PIXEL_ERR1 | CSI_INT1_PIXEL_ERR2 | CSI_INT1_PIXEL_ERR3)) img->err_cnt.pix_err++; if (value1 & (CSI_INT1_OVERFLOW0 | CSI_INT1_OVERFLOW1 | CSI_INT1_OVERFLOW2 | CSI_INT1_OVERFLOW3)) img->err_cnt.overflow++; if (value1 & (CSI_INT1_IMG0_BUSERR0 | CSI_INT1_IMG1_BUSERR0 | CSI_INT1_IMG2_BUSERR0 | CSI_INT1_IMG3_BUSERR0)) img->err_cnt.stream0_err++; if (value1 & (CSI_INT1_IMG0_BUSERR1 | CSI_INT1_IMG1_BUSERR1 | CSI_INT1_IMG2_BUSERR1 | CSI_INT1_IMG3_BUSERR1)) img->err_cnt.stream1_err++; if (value1 & (CSI_INT1_IMG0_BUSERR2 | CSI_INT1_IMG1_BUSERR2 | CSI_INT1_IMG2_BUSERR2 | CSI_INT1_IMG3_BUSERR2)) img->err_cnt.stream2_err++; } else { if (value0 & (CSI_INT0_BT_ERR << n)) img->err_cnt.bt_err++; if (value0 & (CSI_INT0_BT_FATAL << n)) img->err_cnt.bt_fatal++; if (value1 & (CSI_INT1_CROP_ERR0 << n)) img->err_cnt.crop_err++; if (value1 & (CSI_INT1_PIXEL_ERR0 << n)) img->err_cnt.pix_err++; if (value1 & (CSI_INT1_OVERFLOW0 << n)) img->err_cnt.overflow++; if (value1 & (CSI_INT1_IMG0_BUSERR0 << n)) img->err_cnt.stream0_err++; if (value1 & (CSI_INT1_IMG0_BUSERR1 << n)) img->err_cnt.stream1_err++; if (value1 & (CSI_INT1_IMG0_BUSERR2 << n)) img->err_cnt.stream2_err++; } csi_err("%s, csi%d img%d error. int0=0x%08x, int1=0x%08x\n", __func__, img->core->host_id, img->id, value0, value1); csi_err("frm_cnt %d, bt_err %d, bt_fatal %d, crop_err %d, pix_err %d, " "overflow %d\n", img->frm_cnt, img->err_cnt.bt_err, img->err_cnt.bt_fatal, img->err_cnt.crop_err, img->err_cnt.pix_err, img->err_cnt.overflow); } /* ============================== external API below * ===================================== */ /* ============== following interface for csi_image ================== */ int csi_reset_img(struct csi_hw_dev *csi_dev, uint32_t img_id, uint32_t cmd) { int n = img_id; struct csi_core_internal *csi_core; struct csi_img_internal *img; CSI_IMG_ERR_RETURN(csi_dev, img_id, &cmd); csi_core = (struct csi_core_internal *)csi_dev->priv_data; img = &csi_core->images[img_id]; if (cmd | CSI_RESET_IMG_HW) { csi_set(csi_core, CSI_REG_ENABLE, (1 << n), (1 << n)); usleep_range(2, 10); csi_set(csi_core, CSI_REG_ENABLE, (1 << n), 0); } if (cmd | CSI_RESET_IMG_CTX) { memset(img, 0, sizeof(struct csi_img_internal)); spin_lock_init(&img->status_lock); spin_lock_init(&img->crop_lock); img->core = csi_core; img->id = n; } csi_info("%s, csi%d img%d reset cmd 0x%x done\n", __func__, img->core->host_id, img->id, cmd); return 0; } int csi_register_callback(struct csi_hw_dev *csi_dev, uint32_t img_id, struct csi_callback_t *cb) { struct csi_core_internal *csi_core; struct csi_img_internal *img; CSI_IMG_ERR_RETURN(csi_dev, img_id, cb); csi_core = (struct csi_core_internal *)csi_dev->priv_data; img = &csi_core->images[img_id]; memcpy(&img->irq_cb, cb, sizeof(struct csi_callback_t)); csi_info("%s, csi%d img%d done\n", __func__, img->core->host_id, img->id); return 0; } int csi_cfg_bus(struct csi_hw_dev *csi_dev, uint32_t img_id, struct csi_bus_info *bus) { struct csi_core_internal *csi_core; struct csi_img_internal *img; CSI_IMG_ERR_RETURN(csi_dev, img_id, bus); csi_core = (struct csi_core_internal *)csi_dev->priv_data; img = &csi_core->images[img_id]; img->bus_type = bus->bus_type; img->bus_fmt = bus->mbus_fmt; img->bus_flag = bus->bus_flag; csi_info("%s, csi%d, img%d, bus type %d bus_fmt %d, bus_flag 0x%x\n", __func__, csi_core->host_id, img_id, img->bus_type, img->bus_fmt, img->bus_flag); return 0; } int csi_query_outfmts(struct csi_hw_dev *csi_dev, uint32_t img_id, struct csi_outfmt_info *fmt) { int i, total; struct csi_core_internal *csi_core; struct csi_img_internal *img; const struct csi_format_pack *fmt_pack; CSI_IMG_ERR_RETURN(csi_dev, img_id, fmt); csi_core = (struct csi_core_internal *)csi_dev->priv_data; img = &csi_core->images[img_id]; if (img->bus_type == CSI_BUS_MIPICSI2) { fmt_pack = &csi_mipi_fmts[0]; total = sizeof(csi_mipi_fmts) / sizeof(csi_mipi_fmts[0]); } else { fmt_pack = &csi_parallel_fmts[0]; total = sizeof(csi_parallel_fmts) / sizeof(csi_parallel_fmts[0]); } fmt->count = 0; for (i = 0; i < total; i++, fmt_pack++) { if (fmt_pack->bus_fmt != img->bus_fmt) continue; fmt->fmts[fmt->count] = fmt_pack->output_fmt; fmt->count++; csi_info("%s, csi%d, img%d, bus type %d bus_fmt %d, outfmt%d: 0x%08x\n", __func__, csi_core->host_id, img_id, img->bus_type, img->bus_fmt, fmt->count, fmt_pack->output_fmt); } return 0; } int csi_cfg_outfmt(struct csi_hw_dev *csi_dev, uint32_t img_id, uint32_t outfmt) { struct csi_core_internal *csi_core; struct csi_img_internal *img; CSI_IMG_ERR_RETURN(csi_dev, img_id, &outfmt); csi_core = (struct csi_core_internal *)csi_dev->priv_data; img = &csi_core->images[img_id]; img->output_fmt = outfmt; csi_info("%s, csi%d, img%d, output fmt %d\n", __func__, csi_core->host_id, img_id, img->output_fmt); return 0; } int csi_cfg_field(struct csi_hw_dev *csi_dev, uint32_t img_id, struct csi_field_info *field) { struct csi_core_internal *csi_core; struct csi_img_internal *img; CSI_IMG_ERR_RETURN(csi_dev, img_id, field); csi_core = (struct csi_core_internal *)csi_dev->priv_data; img = &csi_core->images[img_id]; img->field = field->field_type; csi_info("%s, csi%d, img%d, field_type %d\n", __func__, csi_core->host_id, img_id, img->field); return 0; } int csi_cfg_size(struct csi_hw_dev *csi_dev, uint32_t img_id, struct csi_img_size *size) { struct csi_core_internal *csi_core; struct csi_img_internal *img; CSI_IMG_ERR_RETURN(csi_dev, img_id, size); csi_core = (struct csi_core_internal *)csi_dev->priv_data; img = &csi_core->images[img_id]; img->size[0] = size[0]; img->size[1] = size[1]; csi_info("%s, csi%d, img%d, size0[ %d x %d ], size1[ %d x %d ]\n", __func__, csi_core->host_id, img_id, size[0].w, size[0].h, size[1].w, size[1].h); return 0; } int csi_cfg_crop(struct csi_hw_dev *csi_dev, uint32_t img_id, struct csi_img_crop *crop) { struct csi_core_internal *csi_core; struct csi_img_internal *img; unsigned long flags = 0; CSI_IMG_ERR_RETURN(csi_dev, img_id, crop); csi_core = (struct csi_core_internal *)csi_dev->priv_data; img = &csi_core->images[img_id]; csi_info("%s, csi%d, img%d, crop0[%d %d %d %d], crop1[%d %d %d %d]\n", __func__, csi_core->host_id, img_id, crop[0].x, crop[0].y, crop[0].w, crop[0].h, crop[1].x, crop[1].y, crop[1].w, crop[1].h); if (img->status != IMG_STATUS_STREAM_ON) { img->crop[0] = crop[0]; img->crop[1] = crop[1]; return 0; } if (((crop[0].x + crop[0].w) > img->size[0].w) || ((crop[0].y + crop[0].h) > img->size[0].h) || (crop[0].w == 0) || (crop[0].h == 0)) { csi_info("%s, csi%d, img%d, error crop0[%d %d %d %d]\n", __func__, csi_core->host_id, img_id, crop[0].x, crop[0].y, crop[0].w, crop[0].h); return -1; } spin_lock_irqsave(&img->crop_lock, flags); if (img->crop_update == 0) { /* make sure previous setting is update to register. */ img->crop_update = (img->crop[0].x ^ crop[0].x); img->crop_update |= (img->crop[0].y ^ crop[0].y); img->crop_update |= (img->crop[0].w ^ crop[0].w); img->crop_update |= (img->crop[0].h ^ crop[0].h); if (img->crop_update) img->crop[0] = crop[0]; } spin_unlock_irqrestore(&img->crop_lock, flags); return 0; } int csi_cfg_img_buf(struct csi_hw_dev *csi_dev, uint32_t img_id, struct csi_img_buf *buf) { struct csi_core_internal *csi_core; struct csi_img_internal *img; int i, n = img_id; uint32_t addr_l[3], addr_h[3]; CSI_IMG_ERR_RETURN(csi_dev, img_id, buf); csi_core = (struct csi_core_internal *)csi_dev->priv_data; img = &csi_core->images[img_id]; img->next_buf = *buf; if (csi_core->sync && img_id != CSI_IMG0) { csi_err("%s: invalid img_id %d for sync mode\n\n", __func__, img_id); return -1; } if (img->pfmt == NULL) { csi_err("csi img%d is not stream on\n", img_id); return 0; } for (i = 0; i < img->pfmt->planes; i++) { uint32_t *ptr_u32 = (uint32_t *)&buf->paddr[i]; addr_l[i] = ptr_u32[0]; addr_h[i] = (is_addr_64bit ? ptr_u32[1] : 0); reg_write(csi_core->base, CSI_REG_IMG_RGBY_BADDR_H(n) + i * 8, addr_h[i]); reg_write(csi_core->base, CSI_REG_IMG_RGBY_BADDR_L(n) + i * 8, addr_l[i]); } /* shadow_set_n*/ csi_set(csi_core, CSI_REG_REG_LOAD, (1 << n), (1 << n)); if (!csi_core->sync || img->status != IMG_STATUS_STREAM_ON) return 0; /* if sync mode and is stream on, img1~3 buffer should be updated as well */ for (n = CSI_IMG1; n <= CSI_IMG3; n++) csi_img_set_sync_buf(&csi_core->images[n], buf); return 0; } int csi_cfg_mem_range(struct csi_hw_dev *csi_dev, uint32_t img_id, struct mem_range *mem) { struct csi_core_internal *csi_core; struct csi_img_internal *img; CSI_IMG_ERR_RETURN(csi_dev, img_id, mem); csi_core = (struct csi_core_internal *)csi_dev->priv_data; img = &csi_core->images[img_id]; img->fb_mode = FB_MODE_ROUND; memcpy(img->round_mem, mem, sizeof(img->round_mem)); csi_info("%s, csi%d, img%d, mem [0x%08x 0x%08x], [0x%08x 0x%08x], [0x%08x " "0x%08x]\n", __func__, csi_core->host_id, img_id, mem[0].low, mem[0].up, mem[1].low, mem[1].up, mem[2].low, mem[2].up); return 0; } void show_csi_regs(struct csi_core_internal *csi_core) { uint32_t i, val; csi_info("Not show zero value register\n"); for (i = 0; i < 0x8A0; i += 4) { val = reg_read(csi_core->base, i); if (val) csi_info("[0x%08x]:0x%x\n", (uint32_t)(csi_core->base + i), val); } } int csi_stream_on(struct csi_hw_dev *csi_dev, uint32_t img_id) { int ret; struct csi_core_internal *csi_core; struct csi_img_internal *img; if ((csi_dev == NULL) || (img_id >= CSI_IMG_NUM)) { csi_err("%s: failed to get csi_dev %p, img_id %d\n", __func__, csi_dev, img_id); return -1; } csi_core = (struct csi_core_internal *)csi_dev->priv_data; img = &csi_core->images[img_id]; ret = csi_img_setup_param(img); if (ret) { csi_err("%s: csi%d, img_id %d failed to setup param\n", __func__, csi_core->host_id, img_id); return -1; } ret = csi_img_set_regs(img); if (ret) { csi_err("%s: csi%d, img_id %d failed to stream on\n", __func__, csi_core->host_id, img_id); return -1; } show_csi_regs(csi_core); memset(&img->err_cnt, 0, sizeof(struct csi_img_err_cnt)); img->frm_cnt = 0; img->shadow_cnt = 0; img->status = IMG_STATUS_STREAM_ON; return 0; } int csi_stream_off(struct csi_hw_dev *csi_dev, uint32_t img_id) { struct csi_core_internal *csi_core; struct csi_img_internal *img; int n, img_s, img_e; unsigned long flags = 0; if ((csi_dev == NULL) || (img_id >= CSI_IMG_NUM)) { csi_err("%s: failed to get csi_dev %p, img_id %d\n", __func__, csi_dev, img_id); return -1; } csi_core = (struct csi_core_internal *)csi_dev->priv_data; if (csi_core->sync == 0) { img_s = img_id; img_e = img_id; } else { img_s = CSI_IMG0; img_e = CSI_IMG3; } for (n = img_s; n <= img_e; n++) { img = &csi_core->images[n]; spin_lock_irqsave(&img->status_lock, flags); /* disable stream */ csi_set(csi_core, CSI_REG_ENABLE, (1 << n), 0); /* disable irq */ csi_set(csi_core, CSI_REG_INT_MASK0, INT0_MASK(n), 0); csi_set(csi_core, CSI_REG_INT_MASK1, INT1_ERR_MASK(n), 0); csi_set(csi_core, CSI_REG_REG_LOAD, (1 << (4 + n)), (1 << (4 + n))); img->status = IMG_STATUS_IDLE; spin_unlock_irqrestore(&img->status_lock, flags); } return 0; } /* ================ following interface for csi_core =============== */ int csi_cfg_sync(struct csi_hw_dev *csi_dev, uint32_t sync) { struct csi_core_internal *csi_core; if (csi_dev == NULL || csi_dev->host_id >= CSI_HOST_NUM) { csi_err("%s: fail to get input csi dev %p\n", __func__, csi_dev); return -1; } csi_core = (struct csi_core_internal *)csi_dev->priv_data; csi_core->sync = !!sync; csi_info("%s: csi%d sync_mode %d\n", __func__, csi_core->host_id, csi_core->sync); return 0; } int csi_irq_handle(struct csi_hw_dev *csi_dev) { struct csi_core_internal *csi_core; struct csi_img_internal *img; int i, is_err; uint32_t value0, value1; unsigned long flags = 0; if (csi_dev == NULL) { csi_err("%s: failed to get csi_dev %p\n", __func__, csi_dev); return -1; } csi_core = (struct csi_core_internal *)csi_dev->priv_data; value0 = reg_read(csi_core->base, CSI_REG_INT_STAT0); value1 = reg_read(csi_core->base, CSI_REG_INT_STAT1); reg_write(csi_core->base, CSI_REG_INT_STAT0, value0); reg_write(csi_core->base, CSI_REG_INT_STAT1, value1); csi_debug("int: value 0x%08x 0x%08x, sync %d\n", value0, value1, csi_core->sync); if (csi_core->sync) { img = &csi_core->images[0]; spin_lock_irqsave(&img->status_lock, flags); if (img->status != IMG_STATUS_STREAM_ON) { spin_unlock_irqrestore(&img->status_lock, flags); return 0; } /* [0:3] - img store done, [4:7] - img shadow done */ csi_core->sync_irq |= (value0 & 0xff); if ((csi_core->sync_irq & 0x0f) == 0x0f) { if (img->irq_cb.csi_frame_done) img->irq_cb.csi_frame_done(img->irq_cb.caller, img->id); csi_core->sync_irq &= 0xf0; img->frm_cnt++; } if ((csi_core->sync_irq & 0xf0) == 0xf0) { if (img->irq_cb.csi_update_buf) img->irq_cb.csi_update_buf(img->irq_cb.caller, img->id); csi_core->sync_irq &= 0x0f; } if ((img->frm_cnt > 1) && ((value0 & INT0_ERR_MASK_ALL) || (value1 & INT1_ERR_MASK_ALL))) { if (img->irq_cb.csi_error_handle) img->irq_cb.csi_error_handle(img->irq_cb.caller, img->id); csi_err_handle(img, 1, value0, value1); } spin_unlock_irqrestore(&img->status_lock, flags); } else { for (i = CSI_IMG0; i <= CSI_IMG3; i++) { img = &csi_core->images[i]; spin_lock_irqsave(&img->status_lock, flags); if (img->status != IMG_STATUS_STREAM_ON) { spin_unlock_irqrestore(&img->status_lock, flags); continue; } csi_debug("int: img%d status %d proc \n", img->id, img->status); is_err = value0 & INT0_ERR_MASK(i); is_err |= (value1 & INT1_ERR_MASK(i)); if (value0 & (CSI_INT0_STORE_DONE0 << i)) { csi_debug("int: img%d status %d csi_frame_done %p\n", img->id, img->status, img->irq_cb.csi_frame_done); if (img->shadow_cnt && !is_err && img->irq_cb.csi_frame_done) { img->irq_cb.csi_frame_done(img->irq_cb.caller, img->id); } if (img->frm_cnt < 5) csi_info("frame_done: %d\n", img->frm_cnt); img->frm_cnt++; } if (value0 & (CSI_INT0_SHADOW_SET0 << i)) { if (FIELD_HAS_BOTH(img->field) && img->second_field == 0) { img->second_field = 1; csi_img_set_field_buf(img, &img->next_buf); } else if (!is_err && img->irq_cb.csi_update_buf) { img->second_field = 0; csi_debug("int: img%d updata buf caller %p\n", img->id, img->irq_cb.caller); img->irq_cb.csi_update_buf(img->irq_cb.caller, img->id); } spin_lock_irqsave(&img->crop_lock, flags); if (img->crop_update) { csi_img_set_crop(img); csi_set(csi_core, CSI_REG_REG_LOAD, (1 << i), (1 << i)); img->crop_update = 0; } spin_unlock_irqrestore(&img->crop_lock, flags); if (img->shadow_cnt < 5) csi_info("shadow_done: %d\n", img->shadow_cnt); img->shadow_cnt++; } if ((img->frm_cnt > 1) && is_err) { if (img->irq_cb.csi_error_handle) img->irq_cb.csi_error_handle(img->irq_cb.caller, img->id); csi_err_handle(img, 0, value0, value1); } spin_unlock_irqrestore(&img->status_lock, flags); } } return 0; } const struct csi_hw_operations csi_ops = { .csi_irq_handle = csi_irq_handle, .csi_cfg_sync = csi_cfg_sync, .csi_reset_img = csi_reset_img, .csi_register_callback = csi_register_callback, .csi_cfg_bus = csi_cfg_bus, .csi_query_outfmts = csi_query_outfmts, .csi_cfg_outfmt = csi_cfg_outfmt, .csi_cfg_field = csi_cfg_field, .csi_cfg_size = csi_cfg_size, .csi_cfg_crop = csi_cfg_crop, .csi_cfg_img_buf = csi_cfg_img_buf, .csi_cfg_mem_range = csi_cfg_mem_range, .csi_stream_on = csi_stream_on, .csi_stream_off = csi_stream_off, }; struct csi_hw_dev *csi_hw_init(reg_addr_t base, uint32_t host_id) { struct csi_core_internal *csi_core; int i; if (host_id >= CSI_HOST_NUM) { csi_err("%s: error input csi host id %d, should be 0~2\n", __func__, host_id); return NULL; } csi_core = &csi_cores[host_id]; if (csi_core->inited) { csi_warn("%s: csi%d is already inited\n", __func__, host_id); return &csi_core->ex_dev; } for (i = 0; i < CSI_IMG_NUM; i++) { csi_core->images[i].id = i; csi_core->images[i].core = csi_core; spin_lock_init(&csi_core->images[i].status_lock); spin_lock_init(&csi_core->images[i].crop_lock); } spin_lock_init(&csi_core->glb_reg_lock); csi_core->base = base; /* global reset */ reg_write(csi_core->base, CSI_REG_ENABLE, (1 << 8)); usleep_range(2, 10); reg_write(csi_core->base, CSI_REG_ENABLE, 0); csi_core->ex_dev.priv_data = (void *)csi_core; csi_core->ex_dev.ops = csi_ops; csi_core->inited = 1; csi_info("%s: csi%d init done. ver %d\n", __func__, host_id, csi_core->ip_version); return &csi_core->ex_dev; } int csi_hw_deinit(struct csi_hw_dev *csi_dev) { struct csi_core_internal *csi_core; if (csi_dev == NULL) { csi_err("%s: NULL input csi dev\n", __func__); return -1; } if (csi_dev->host_id >= CSI_HOST_NUM) { csi_err("%s: error input csi dev host_id %d\n", __func__, csi_dev->host_id); return -1; } csi_core = (struct csi_core_internal *)csi_dev->priv_data; /* global reset */ reg_write(csi_core->base, CSI_REG_ENABLE, (1 << 8)); usleep_range(2, 10); reg_write(csi_core->base, CSI_REG_ENABLE, 0); csi_core->inited = 0; csi_info("%s: csi%d deinit done\n", __func__, csi_core->host_id); return 0; }