diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/media/sprd_sensor/csi2/csi_api.c | |
Diffstat (limited to 'drivers/media/sprd_sensor/csi2/csi_api.c')
| -rw-r--r-- | drivers/media/sprd_sensor/csi2/csi_api.c | 730 |
1 files changed, 730 insertions, 0 deletions
diff --git a/drivers/media/sprd_sensor/csi2/csi_api.c b/drivers/media/sprd_sensor/csi2/csi_api.c new file mode 100644 index 00000000..12af2bf6 --- /dev/null +++ b/drivers/media/sprd_sensor/csi2/csi_api.c @@ -0,0 +1,730 @@ +#include <linux/kernel.h> +#include <linux/semaphore.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <soc/sprd/sci_glb_regs.h> + +#ifndef CONFIG_64BIT +#include <mach/irqs.h> +#include <soc/sprd/globalregs.h> +#include <soc/sprd/hardware.h> +#else +#include <soc/sprd/irqs.h> +#endif +#include "parse_hwinfo.h" +#include <soc/sprd/sci.h> + +#include "csi_api.h" +#include "csi_log.h" + +#define CSI2_EB (SPRD_MMAHB_BASE) +#define CSI2_EB_BIT (1 << 4) +#define CSI2_RST (SPRD_MMAHB_BASE + 0x4) +#define CSI2_RST_BIT (1 << 5) + +#if defined(CONFIG_SC_FPGA) && defined(CONFIG_MACH_SP7720EA) +extern void usc28c_csi_init(void); +#endif +void csi_api_event1_handler(int irq, void *handle); +void csi_api_event2_handler(int irq, void *handle); + + +static void csi_phy_power_down(u32 phy_id, u32 is_eb) +{ +#if IS_ENABLED(VERSION3T) || IS_ENABLED(VERSION3D) + /*bit0: phya ; bit1: phyb*/ + if (is_eb) { + if (0x03 == (phy_id & 0x03)) { + sci_glb_set(REG_AON_APB_PWR_CTRL, (BIT_CSI0_PHY_PD | BIT_CSI1_PHY_PD)); + } else { + if (0x01 == (phy_id & 0x01)) { + sci_glb_set(REG_AON_APB_PWR_CTRL, BIT_CSI0_PHY_PD); + } + + if (0x02 == (phy_id & 0x02)) { + sci_glb_set(REG_AON_APB_PWR_CTRL, BIT_CSI1_PHY_PD); + } + } + } else { + if (0x03 == (phy_id & 0x03)) { + sci_glb_clr(REG_AON_APB_PWR_CTRL, (BIT_CSI0_PHY_PD | BIT_CSI1_PHY_PD)); + } else { + if (0x01 == (phy_id & 0x01)) { + sci_glb_clr(REG_AON_APB_PWR_CTRL, BIT_CSI0_PHY_PD); + } + + if (0x02 == (phy_id & 0x02)) { + sci_glb_clr(REG_AON_APB_PWR_CTRL, BIT_CSI1_PHY_PD); + } + } + } +#elif IS_ENABLED(VERSION3L) + /*bit0: phya ; bit1: phyb*/ + if (is_eb) { + if (0x03 == (phy_id & 0x03)) { + printk("csi phy erro: scx35l does not support the combination of phya & phyb\n"); + } else { + if (0x01 == (phy_id & 0x01)) { + sci_glb_set(REG_AON_APB_PWR_CTRL, (3 << 12)); + } + + if (0x02 == (phy_id & 0x02)) { + sci_glb_set(REG_AON_APB_PWR_CTRL, (3 << 10)); + } + } + } else { + if (0x03 == (phy_id & 0x03)) { + printk("csi phy erro: scx35l does not support the combination of phya & phyb\n"); + } else { + if (0x01 == (phy_id & 0x01)) { + sci_glb_clr(REG_AON_APB_PWR_CTRL, (1 << 13)); + udelay(500); + sci_glb_clr(REG_AON_APB_PWR_CTRL, (1 << 12)); + } + + if (0x02 == (phy_id & 0x02)) { + sci_glb_clr(REG_AON_APB_PWR_CTRL, (1 << 11)); + udelay(500); + sci_glb_clr(REG_AON_APB_PWR_CTRL, (1 << 10)); + } + } + } +#endif +} + +static void csi_enable(void) +{ + sci_glb_set(CSI2_EB, CSI2_EB_BIT); + sci_glb_set(CSI2_RST, CSI2_RST_BIT); + udelay(1); + sci_glb_clr(CSI2_RST, CSI2_RST_BIT); +} + +int csi_api_malloc(void **handle) +{ + int ret = 0; + struct csi_context *csi_handle = NULL; + csi_handle = (struct csi_context *)vzalloc(sizeof(struct csi_context)); + if (NULL == handle) { + printk("vzalloc fail,no mem"); + return -1; + } + csi_handle->g_csi2_irq = 0x12000034; + spin_lock_init(&csi_handle->csi2_lock); + *handle = csi_handle; + + return ret; +} + +void csi_api_free(void *handle) +{ + if (handle != NULL) { + vfree(handle); + handle = NULL; + } +} + +u8 csi_api_init(u32 bps_per_lane, u32 phy_id) +{ + csi_error_t e = SUCCESS; + u64 base_address = CSI2_BASE; + +#if defined(CONFIG_SC_FPGA) && defined(CONFIG_MACH_SP7720EA) + usc28c_csi_init(); +#endif + + do { + csi_phy_power_down(phy_id, 0); + csi_enable(); + e = csi_init(base_address); + if(e != SUCCESS) { + LOG_ERROR("Unable to initialise driver"); + break; + } + dphy_init(bps_per_lane, phy_id); + }while(0); + + return e; +} + +u8 csi_api_start(void *handle) +{ + csi_error_t e = SUCCESS; + int ret = 0; + struct csi_context *csi_handle = handle; + + if (NULL == handle) { + printk("handle null\n"); + return ERR_UNDEFINED; + } + do { + /* set only one lane (lane 0) as active (ON) */ + e = csi_set_on_lanes(1); + if(e != SUCCESS) { + LOG_ERROR("Unable to set lanes"); + csi_close(); + break; + } + LOG_DEBUG("Lane set OK"); + e = csi_shut_down_phy(0); + if(e != SUCCESS) { + LOG_ERROR("Unable to bring up PHY"); + csi_close(); + break; + } + LOG_DEBUG("PHY power up OK"); + + e = csi_reset_phy(); + if(e != SUCCESS) { + LOG_ERROR("Unable to reset PHY"); + csi_close(); + break; + } + LOG_DEBUG("PHY reset OK"); + + e = csi_reset_controller(); + if(e != SUCCESS) { + LOG_ERROR("Unable to reset controller"); + csi_close(); + break; + } + /* MASK all interrupts */ + csi_event_disable(0xffffffff, 1); + csi_event_disable(0xffffffff, 2); + ret = request_irq(IRQ_CSI_INT0, + (irq_handler_t)csi_api_event1_handler, + IRQF_SHARED, + "CSI2_0", + (void *)csi_handle); + if (ret) { + e = ERR_UNDEFINED; + break; + } + + ret = request_irq(IRQ_CSI_INT1, + (irq_handler_t)csi_api_event2_handler, + IRQF_SHARED, + "CSI2_1", + (void *)csi_handle); + if (ret) { + e = ERR_UNDEFINED; + break; + } + + } while(0); + + return e; +} + +u8 csi_api_close(void *handle, u32 phy_id) +{ + struct csi_context *csi_handle = handle; + if (NULL == handle) { + printk("handle null\n"); + return ERR_UNDEFINED; + } + LOG_DEBUG("exit"); + csi_api_unregister_all_events(handle); + csi_shut_down_phy(1); + free_irq(IRQ_CSI_INT0, &csi_handle->g_csi2_irq); + free_irq(IRQ_CSI_INT1, &csi_handle->g_csi2_irq); + csi_close(); + csi_phy_power_down(phy_id, 1); + + return SUCCESS; +} + +u8 csi_api_set_on_lanes(u8 no_of_lanes) +{ + return csi_set_on_lanes(no_of_lanes); +} + +u8 csi_api_get_on_lanes() +{ + return csi_get_on_lanes(); +} + +csi_lane_state_t csi_api_get_clk_state() +{ + return csi_clk_state(); +} + +csi_lane_state_t csi_api_get_lane_state(u8 lane) +{ + return csi_lane_module_state(lane); +} + +static u32 csi_api_event_map(u8 event, u8 vc_lane) +{ + switch((csi_event_t)(event)) { + case ERR_PHY_TX_START: + return (0x1 << (event + vc_lane)); + case ERR_FRAME_BOUNDARY_MATCH: + return (0x1 << (event + vc_lane)); + case ERR_FRAME_SEQUENCE: + return (0x1 << (event + vc_lane)); + case ERR_CRC_DURING_FRAME: + return (0x1 << (event + vc_lane)); + case ERR_LINE_CRC: + return (0x1 << (24 + vc_lane)); + case ERR_DOUBLE_ECC: + return (0x1 << 28); + case ERR_PHY_ESCAPE_ENTRY: + return (0x1 << (0 + vc_lane)); + case ERR_ECC_SINGLE: + return (0x1 << (8 + vc_lane)); + case ERR_UNSUPPORTED_DATA_TYPE: + return (0x1 << (12 + vc_lane)); + case MAX_EVENT: + break; + default: + break; + } + switch((csi_line_event_t)(event)) { + case ERR_LINE_BOUNDARY_MATCH: + return (0x1 << (16 + (vc_lane % 4))); + case ERR_LINE_SEQUENCE: + return (0x1 << (20 + (vc_lane % 4))); + default: + break; + } + /* writing the following value affects nothing */ + return 0x80000000; +} + +u8 csi_api_register_event(void *handle, csi_event_t event, u8 vc_lane, handler_t handler) +{ + /* the VC_LANE value is the lane number ONLY in PHY ERRORS + * e rest are all virtual channels number except for + * double ECC, where VC is unknown + */ + u8 e = SUCCESS; + struct csi_context *csi_handle = handle; + if (NULL == handle) { + printk("handle null\n"); + return ERR_UNDEFINED; + } + /* the maximum */ + if (vc_lane < 4) { + switch(event) { + case ERR_PHY_TX_START: + LOG_WARNING("the lane value depends on the synthesis defines"); + LOG_WARNING("make sure this value does not exceed these defines"); + case ERR_FRAME_BOUNDARY_MATCH: + case ERR_FRAME_SEQUENCE: + case ERR_CRC_DURING_FRAME: + case ERR_LINE_CRC: + case ERR_DOUBLE_ECC: + e = csi_event_enable(csi_api_event_map(event, vc_lane), 1); + break; + + case ERR_PHY_ESCAPE_ENTRY: + LOG_WARNING("the lane value depends on the synthesis defines"); + LOG_WARNING("make sure this value does not exceed these defines"); + case ERR_ECC_SINGLE: + case ERR_UNSUPPORTED_DATA_TYPE: + e = csi_event_enable(csi_api_event_map(event, vc_lane), 2); + break; + + default: + e = ERROR_EVENT_TYPE_INVALID; + break; + } + } else { + e = ERROR_VC_LANE_OUT_OF_BOUND; + } + if (e == SUCCESS) { + csi_handle->csi_api_event_registry[event + vc_lane] = handler; + } + + return e; +} + +u8 csi_api_unregister_event(void *handle, csi_event_t event, u8 vc_lane) +{ + csi_error_t e = SUCCESS; + struct csi_context *csi_handle = handle; + if (NULL == handle) { + printk("handle null\n"); + return ERR_UNDEFINED; + } + /* the maximum */ + if (vc_lane < 4) { + switch(event) { + case ERR_PHY_TX_START: + LOG_WARNING("the lane value depends on the synthesis defines"); + LOG_WARNING("make sure this value does not exceed these defines"); + case ERR_FRAME_BOUNDARY_MATCH: + case ERR_FRAME_SEQUENCE: + case ERR_CRC_DURING_FRAME: + case ERR_LINE_CRC: + case ERR_DOUBLE_ECC: + e = csi_event_disable(csi_api_event_map(event, vc_lane), 1); + break; + + case ERR_PHY_ESCAPE_ENTRY: + LOG_WARNING("the lane value depends on the synthesis defines"); + LOG_WARNING("make sure this value does not exceed these defines"); + case ERR_ECC_SINGLE: + case ERR_UNSUPPORTED_DATA_TYPE: + e = csi_event_disable(csi_api_event_map(event, vc_lane), 2); + break; + + default: + e = ERROR_EVENT_TYPE_INVALID; + break; + + } + } else { + e = ERROR_VC_LANE_OUT_OF_BOUND; + } + if (e == SUCCESS) { + csi_handle->csi_api_event_registry[event + vc_lane] = NULL; + } + + return e; +} + +u8 csi_api_register_line_event(void *handle, u8 vc, csi_data_type_t data_type, csi_line_event_t line_event, handler_t handler) +{ + u8 id = 0; + int counter = 0; + int first_slot = -1; + int already_registered = 0; + u8 e = SUCCESS; + struct csi_context *csi_handle = handle; + if (NULL == handle) { + printk("handle null\n"); + return ERR_UNDEFINED; + } + + if ((data_type < NULL_PACKET) + || ((data_type > EMBEDDED_8BIT_NON_IMAGE_DATA) && (data_type < YUV420_8BIT)) + || ((data_type > RGB888) && (data_type < RAW6)) + || ((data_type > RAW14) && (data_type < USER_DEFINED_8BIT_DATA_TYPE_1 )) + || (data_type > USER_DEFINED_8BIT_DATA_TYPE_8)) { + /* wrong data type - not long packet */ + return ERROR_DATA_TYPE_INVALID; + } + /* the maximum */ + if (vc > 3) { + return ERROR_VC_LANE_OUT_OF_BOUND; + } + id = (u8)((vc << 6)) | (u8)(data_type); + + for (counter = 0; counter < 8; counter++) { + if (csi_get_registered_line_event(counter) == 0) { + /* first empty slot to register event */ + first_slot = counter; + break; + } + if (id == csi_get_registered_line_event(counter)) { + LOG_WARNING("already registered line/datatype"); + already_registered = 1; + first_slot = counter; /* no need to register it again */ + /* but NOT that there are no slots!*/ + break; + } + } + /* if not all slots are taken */ + if (first_slot != -1) { + /* un-mask (whether registered or not) */ + e = csi_event_enable(csi_api_event_map(line_event, first_slot), (first_slot / 4 == 1)? 2: 1); + if (e == SUCCESS) { + /* if not already registered */ + if (already_registered != 1) { + e = csi_register_line_event(vc, data_type, first_slot); + if (csi_handle->csi_api_event_registry[line_event + first_slot] != NULL) { + LOG_WARNING("already registered event and callback (overwriting!)"); + } + } + /* register callback */ + csi_handle->csi_api_event_registry[line_event + first_slot] = handler; + } + } else { + LOG_WARNING("all slots are taken"); + e = ERROR_SLOTS_FULL; + } + + return e; +} + +u8 csi_api_unregister_line_event(void *handle, u8 vc, csi_data_type_t data_type, csi_line_event_t line_event) +{ + u8 id = 0; + int counter = 0; + int replace_slot = -1; + int last_slot = -1; + u8 vc_new = 0; + csi_data_type_t dt_new; + csi_line_event_t other_line_event; + struct csi_context *csi_handle = handle; + if (NULL == handle) { + printk("handle null\n"); + return ERR_UNDEFINED; + } + + if ((data_type < NULL_PACKET) + || ((data_type > EMBEDDED_8BIT_NON_IMAGE_DATA) && (data_type < YUV420_8BIT)) + || ((data_type > RGB888) && (data_type < RAW6)) + || ((data_type > RAW14) && (data_type < USER_DEFINED_8BIT_DATA_TYPE_1 )) + || (data_type > USER_DEFINED_8BIT_DATA_TYPE_8)) { + /* wrong data type - not long packet */ + return ERROR_DATA_TYPE_INVALID; + } + /* the maximum */ + if (vc > 3) { + return ERROR_VC_LANE_OUT_OF_BOUND; + } + if (line_event == ERR_LINE_BOUNDARY_MATCH) { + other_line_event = ERR_LINE_SEQUENCE; + } else if (line_event == ERR_LINE_SEQUENCE) { + other_line_event = ERR_LINE_BOUNDARY_MATCH; + } else { + return ERROR_DATA_TYPE_INVALID; + } + id = (vc << 6) | data_type; + for (counter = 0; counter < 8; counter++) { + if (id == csi_get_registered_line_event(counter)) { + replace_slot = counter; + } + if (csi_get_registered_line_event(counter) == 0) { + last_slot = counter - 1; + break; + } + } + if (replace_slot == -1) { + return ERROR_NOT_REGISTERED; + } + if (last_slot == -1) { + last_slot = 7; /* the very last entry */ + } + vc_new = csi_get_registered_line_event(last_slot) >> 6; + dt_new = (csi_get_registered_line_event(last_slot) << 2) >> 2; + + if ((csi_handle->csi_api_event_registry[other_line_event + replace_slot] == NULL) && (last_slot != replace_slot)) { + /* swap IDI with last registered if its NOT the last entry*/ + /* copy the last to (over) the one to be deleted*/ + csi_register_line_event(vc_new, dt_new, replace_slot); + /* now that last registered is copied to a new location, unregister old location */ + csi_event_disable(csi_api_event_map(line_event, last_slot), (last_slot / 4 == 1)? 2: 1); + /* swap event registry */ + csi_handle->csi_api_event_registry[line_event + replace_slot] = csi_handle->csi_api_event_registry[line_event + last_slot]; + /* mask last slot */ + if (csi_handle->csi_api_event_registry[other_line_event + last_slot] != NULL) { + /* swap event registry - the other possible line event of the last slot */ + csi_handle->csi_api_event_registry[other_line_event + replace_slot] = csi_handle->csi_api_event_registry[other_line_event + last_slot]; + /* mask last slot, other line event!*/ + csi_event_disable(csi_api_event_map(other_line_event, last_slot), (last_slot / 4 == 1)? 2: 1); + /* un mask other line event */ + csi_event_enable(csi_api_event_map(other_line_event, replace_slot), (replace_slot / 4 == 1)? 2: 1); + } + csi_unregister_line_event(last_slot); + } else { + /* remove from event registry */ + csi_handle->csi_api_event_registry[line_event + replace_slot] = NULL; + /* mask */ + csi_event_disable(csi_api_event_map(line_event, replace_slot), (replace_slot / 4 == 1)? 2: 1); + + if ((csi_handle->csi_api_event_registry[other_line_event + replace_slot] == NULL) && (last_slot == replace_slot)) { /* if it is last entry */ + csi_unregister_line_event(last_slot); + } + } + + return SUCCESS; +} + +void csi_api_event1_handler(int irq, void *handle) +{ + u32 source = 0; + unsigned long flag; + struct csi_context *csi_handle = handle; + if (NULL == handle) { + printk("handle null\n"); + return; + } + + source = csi_event_get_source(1); + spin_lock_irqsave(&csi_handle->csi2_lock,flag); + if (NULL != csi_handle->isr_cb) { + (*csi_handle->isr_cb)(1, source, csi_handle->u_data); + } + spin_unlock_irqrestore(&csi_handle->csi2_lock, flag); + + return; +#if 0 + if (source > 0) { /* map to enumerator csi_event_t or csi_line_event_t */ + if ((source & (0xf << 0)) > 0) { + tmp = ERR_PHY_TX_START + ((source & 0xf) >> 1); + } + if ((source & (0xf << 4)) > 0) { + tmp = ERR_FRAME_BOUNDARY_MATCH + ((source & 0xf0) >> 5); + } + if ((source & (0xf << 8)) > 0) { + tmp = ERR_FRAME_SEQUENCE + ((source & (0xf << 8)) >> 9); + } + if ((source & (0xf << 12)) > 0) { + tmp = ERR_CRC_DURING_FRAME + ((source & (0xf << 12)) >> 13); + } + if ((source & (0xf << 16)) > 0) { + tmp = ERR_LINE_BOUNDARY_MATCH + ((source & (0xf << 16)) >> 17); + } + if ((source & (0xf << 20)) > 0) { + tmp = ERR_LINE_SEQUENCE + ((source & (0xf << 20)) >> 21); + } + if ((source & (0xf << 24)) > 0) { + tmp = ERR_LINE_CRC + ((source & (0xf << 24)) >> 25); + } + if ((source & (0xf << 28)) > 0) { + tmp = ERR_DOUBLE_ECC; + } + /* call callback if valid */ + if (tmp != 0x80000000) { + if (csi_api_event_registry[tmp] != NULL) { + csi_api_event_registry[tmp](param); + } + } + } +#endif +} + +void csi_api_event2_handler(int irq, void *handle) +{ + u32 source = 0; + unsigned long flag; + struct csi_context *csi_handle = handle; + if (NULL == handle) { + printk("handle null\n"); + return ; + } + + source = csi_event_get_source(2); + spin_lock_irqsave(&csi_handle->csi2_lock, flag); + if (csi_handle->isr_cb) { + (*csi_handle->isr_cb)(2, source, csi_handle->u_data); + } + spin_unlock_irqrestore(&csi_handle->csi2_lock, flag); + + return; + +#if 0 + source = csi_event_get_source(2); + + if (source > 0) { + /* map to enumerator csi_event_t or csi_line_event_t */ + if ((source & (0xf << 0)) > 0) { + tmp = ERR_PHY_ESCAPE_ENTRY + ((source & 0xf) >> 1); + } + + if ((source & (0xf << 4)) > 0) { + ; /* error discarded */ + } + + if ((source & (0xf << 8)) > 0) { + tmp = ERR_ECC_SINGLE + ((source & (0xf << 8)) >> 9); + } + if ((source & (0xf << 12)) > 0) { + tmp = ERR_UNSUPPORTED_DATA_TYPE + ((source & (0xf << 12)) >> 13); + } + if ((source & (0xf << 16)) > 0) { + tmp = ERR_LINE_BOUNDARY_MATCH + 4 + ((source & (0xf << 16)) >> 17); + /* 4 added because its in ERR2. ERR1 already has 4 */ + } + if ((source & (0xf << 20)) > 0) { + tmp = ERR_LINE_SEQUENCE + 4 + ((source & (0xf << 20)) >> 21); + } + /* call callback if valid */ + if (tmp != 0x80000000) { + if (csi_api_event_registry[tmp] != NULL) { + csi_api_event_registry[tmp](param); + } + } + } +#endif +} + +u8 csi_api_unregister_all_events(void *handle) +{ + u8 e = SUCCESS; + u8 counter = 0; + struct csi_context *csi_handle = handle; + if (NULL == handle) { + printk("handle null\n"); + return -1; + } + + e = csi_event_disable(0xffffffff, 1); + e = csi_event_disable(0xffffffff, 2); + if (e == SUCCESS) { + for (counter = 0; counter < 8; counter++) { + e = csi_unregister_line_event(counter); + } + for (counter = (u8)(ERR_PHY_TX_START); counter < (u8)(MAX_EVENT); counter++) { + csi_handle->csi_api_event_registry[counter] = NULL; + } + } + + return e; +} + +u8 csi_api_shut_down_phy(u8 shutdown) +{ + return csi_shut_down_phy(shutdown); +} + +u8 csi_api_reset_phy() +{ + return csi_reset_phy(); +} + +u8 csi_api_reset_controller() +{ + return csi_reset_controller(); +} + +u8 csi_api_core_write(csi_registers_t address, u32 data) +{ + return csi_core_write(address, data); +} + +u32 csi_api_core_read(csi_registers_t address) +{ + return csi_core_read(address); +} + +int csi_reg_isr(void *handle, csi2_isr_func user_func, void* user_data) +{ + unsigned long flag; + struct csi_context *csi_handle = handle; + if (NULL == handle) { + printk("handle null\n"); + return ERR_UNDEFINED; + } + + spin_lock_irqsave(&csi_handle->csi2_lock, flag); + csi_handle->isr_cb = user_func; + csi_handle->u_data = user_data; + spin_unlock_irqrestore(&csi_handle->csi2_lock, flag); + + return 0; +} + +int csi_read_registers(u32* reg_buf, u32 *buf_len) +{ + u32 *reg_addr = (u32*)CSI2_BASE; + + if (NULL == reg_buf || NULL == buf_len || 0 != (*buf_len % 4)) { + return -1; + } + + while (buf_len != 0 && reg_addr < (u64)(CSI2_BASE + SPRD_CSI2_REG_SIZE)) { + *reg_buf++ = *(volatile u32*)reg_addr++; + *buf_len -= 4; + } + + *buf_len = SPRD_CSI2_REG_SIZE; + + return 0; +} |
