summaryrefslogtreecommitdiff
path: root/drivers/media/sprd_sensor/csi2/csi_driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/sprd_sensor/csi2/csi_driver.c')
-rw-r--r--drivers/media/sprd_sensor/csi2/csi_driver.c502
1 files changed, 502 insertions, 0 deletions
diff --git a/drivers/media/sprd_sensor/csi2/csi_driver.c b/drivers/media/sprd_sensor/csi2/csi_driver.c
new file mode 100644
index 00000000..dc4c922c
--- /dev/null
+++ b/drivers/media/sprd_sensor/csi2/csi_driver.c
@@ -0,0 +1,502 @@
+#include <linux/delay.h>
+#include "csi_driver.h"
+#include "csi_access.h"
+#include "csi_log.h"
+#ifndef CONFIG_64BIT
+#include <soc/sprd/hardware.h>
+#else
+#include "parse_hwinfo.h"
+#endif
+#include <soc/sprd/sci.h>
+
+
+static int csi_core_initialized = 0;
+
+static const struct csi_pclk_cfg csi_pclk_setting[CSI_PCLK_CFG_COUNTER] = {
+ {80, 90, 0x00, 4},
+ {90, 100, 0x10, 4},
+ {100, 110, 0x20, 4},
+ {110, 130, 0x01, 6},
+ {130, 140, 0x11, 6},
+ {140, 150, 0x21, 6},
+ {150, 170, 0x02, 9},
+ {170, 180, 0x12, 9},
+ {180, 200, 0x22, 9},
+ {200, 220, 0x03, 10},
+ {220, 240, 0x13, 10},
+ {240, 250, 0x23, 10},
+ {250, 270, 0x04, 13},
+ {270, 300, 0x14, 13},
+ {300, 330, 0x05, 17},
+ {330, 360, 0x15, 17},
+ {360, 400, 0x25, 17},
+#ifdef IS_ENABLED(VERSION3T) || IS_ENABLED(VERSION3D)
+ {400, 450, 0x06, 25},
+ {450, 500, 0x16, 26},
+#elif IS_ENABLED(VERSION3L)
+ {400, 450, 0x06, 23},
+ {450, 500, 0x16, 23},
+#endif
+ {500, 550, 0x07, 28},
+ {550, 600, 0x17, 28},
+ {600, 650, 0x08, 33},
+ {650, 700, 0x18, 33},
+ {700, 750, 0x09, 38},
+ {750, 800, 0x19, 38},
+ {800, 850, 0x29, 38},
+ {850, 900, 0x39, 38},
+ {900, 950, 0x0A, 52},
+ {950, 1000, 0x1A, 52}
+};
+
+#if IS_ENABLED(VERSION3T)
+static void dpy_ab_clr(void)
+{
+ sci_glb_clr(SPRD_MMAHB_BASE + 0x000C, 0x1F);
+}
+
+static void dpy_a_enable(void)
+{
+ sci_glb_clr(SPRD_MMAHB_BASE + 0x000C, 0x07);
+ sci_glb_set(SPRD_MMAHB_BASE + 0x000C, 0x0C);
+}
+
+static void dpy_b_enable(void)
+{
+ sci_glb_clr(SPRD_MMAHB_BASE + 0x000C, 0x07);
+ sci_glb_set(SPRD_MMAHB_BASE + 0x000C, 0x0F);
+}
+
+static void dpy_c_enable(void)
+{
+
+}
+
+static void dpy_ab_sync(void)
+{
+ sci_glb_clr(SPRD_MMAHB_BASE + 0x000C, 0x07);
+}
+#elif IS_ENABLED(VERSION3D)
+static void dpy_ab_clr(void)
+{
+ sci_glb_clr(SPRD_MMAHB_BASE + 0x000C, 0x3F);
+}
+
+static void dpy_a_enable(void)
+{
+ sci_glb_clr(SPRD_MMAHB_BASE + 0x000C, 0x07);
+ sci_glb_set(SPRD_MMAHB_BASE + 0x000C, 0x0C);
+}
+
+static void dpy_b_enable(void)
+{
+ sci_glb_clr(SPRD_MMAHB_BASE + 0x000C, 0x07);
+ sci_glb_set(SPRD_MMAHB_BASE + 0x000C, 0x0F);
+}
+
+static void dpy_c_enable(void)
+{
+ sci_glb_clr(SPRD_MMAHB_BASE + 0x000C, 0x3F);
+ sci_glb_set(SPRD_MMAHB_BASE + 0x000C, 0x2C);
+}
+
+static void dpy_ab_sync(void)
+{
+ sci_glb_clr(SPRD_MMAHB_BASE + 0x000C, 0x07);
+}
+#else IS_ENABLED(VERSION3L)
+static void dpy_ab_clr(void)
+{
+}
+static void dpy_a_enable(void)
+{
+ sci_glb_clr(SPRD_MMAHB_BASE + 0x000C, 0x01);
+}
+static void dpy_b_enable(void)
+{
+ sci_glb_set(SPRD_MMAHB_BASE + 0x000C, 0x01);
+}
+
+static void dpy_c_enable(void)
+{
+}
+
+static void dpy_ab_sync(void)
+{
+}
+#endif
+
+static void dphy_write(u8 test_code, u8 test_data, u8* test_out)
+{
+ u32 temp = 0xffffff00;
+
+ csi_core_write_part(PHY_TST_CRTL1, 0, PHY_TESTEN, 1); //phy_testen = 0
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL0,1, PHY_TESTCLK, 1); //phy_testclk = 1
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL1, test_code, PHY_TESTDIN, PHY_TESTDIN_W); //phy_testdin
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL1, 1, PHY_TESTEN, 1);//phy_testen = 1
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL0, 0, PHY_TESTCLK, 1);//phy_testclk = 0
+ udelay(1);
+ temp = csi_core_read_part(PHY_TST_CRTL1, PHY_TESTDOUT,PHY_TESTDOUT_W);
+ *test_out = (u8)temp;
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL1, 0, PHY_TESTEN, 1); //phy_testen = 0
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL1, test_data, PHY_TESTDIN, PHY_TESTDIN_W);//phy_testdin
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL0,1, PHY_TESTCLK, 1);//phy_testclk = 1
+ udelay(1);
+}
+
+static void dphy_cfg_start(void)
+{
+ csi_core_write_part(PHY_TST_CRTL1, 0, PHY_TESTEN, 1); //phy_testen = 0
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL0,1, PHY_TESTCLK, 1); //phy_testclk = 1
+ udelay(1);
+}
+
+static void dphy_cfg_done(void)
+{
+ csi_core_write_part(PHY_TST_CRTL1, 0, PHY_TESTEN, 1); //phy_testen = 0
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL0,1, PHY_TESTCLK, 1); //phy_testclk = 1
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL1, 0, PHY_TESTDIN, PHY_TESTDIN_W);//phy_testdin
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL1, 1, PHY_TESTEN, 1);//phy_testen = 1
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL0, 0, PHY_TESTCLK, 1);//phy_testclk = 0
+ udelay(1);
+}
+
+static void csi_get_pclk_cfg(u32 bps_per_lane, struct csi_pclk_cfg *csi_pclk_cfg_ptr)
+{
+ u32 i = 0;
+ int rtn = -1;
+
+ for (i =0 ; i < CSI_PCLK_CFG_COUNTER; i++) {
+ if ( bps_per_lane >= csi_pclk_setting[i].pclk_start && bps_per_lane < csi_pclk_setting[i].pclk_end) {
+ csi_pclk_cfg_ptr->hsfreqrange = csi_pclk_setting[i].hsfreqrange;
+ csi_pclk_cfg_ptr->hsrxthssettle = csi_pclk_setting[i].hsrxthssettle;
+ rtn = 0;
+ break;
+ }
+ }
+
+ if (rtn) {
+ csi_pclk_cfg_ptr->hsfreqrange = csi_pclk_setting[CSI_PCLK_CFG_COUNTER -2].hsfreqrange;
+ csi_pclk_cfg_ptr->hsrxthssettle = csi_pclk_setting[CSI_PCLK_CFG_COUNTER -2].hsrxthssettle;
+ }
+}
+
+static void dphy_init_common(u32 bps_per_lane, u32 phy_id, u32 rx_mode)
+{
+ u8 temp = 0;
+ struct csi_pclk_cfg csi_pclk_cfg_val = {0, 0, 0, 0};
+
+ csi_core_write_part(PHY_SHUTDOWNZ, 0, 0, 1);
+ csi_core_write_part(DPHY_RSTZ, 0, 0, 1);
+ csi_core_write_part(PHY_TST_CRTL0, 1, PHY_TESTCLR, 1);
+ udelay(1);
+ csi_core_write_part(PHY_TST_CRTL0, 0, PHY_TESTCLR, 1);
+ udelay(1);
+ dphy_cfg_start();
+
+#if defined(CONFIG_MACH_SP7720EA)
+#if defined(CONFIG_SC_FPGA)
+ sci_glb_write(SPRD_GPIO_BASE + 0x0008, 0xd, -1UL);
+ dphy_write(0x30, 0xc0, &temp);
+#endif
+ dphy_write(0x37, 0x03, &temp);
+ //LOG_DEBUG2 ("%04x default reg value: %04x\r\n", 0x37, temp);
+ dphy_write(0x47, 0x03, &temp);
+ //LOG_DEBUG2 ("%04x default reg value: %04x\r\n", 0x47, temp);
+ dphy_write(0x57, 0x03, &temp);
+ //LOG_DEBUG2 ("%04x default reg value: %04x\r\n", 0x57, temp);
+ dphy_write(0x67, 0x03, &temp);
+ dphy_write(0x77, 0x03, &temp);
+#else
+ csi_get_pclk_cfg(bps_per_lane, &csi_pclk_cfg_val);
+
+#if IS_ENABLED(VERSION3L) || IS_ENABLED(VERSION3T) || IS_ENABLED(VERSION3D)
+ if (0x03 == phy_id) {
+ if (0x00 == rx_mode) {
+ dphy_write(0x34, 0x14, &temp);
+ } else {
+ dphy_write(0x34, 0xA0, &temp);
+ }
+ } else {
+ dphy_write(0x34, 0x14, &temp);
+ }
+#else
+ dphy_write(0x34, 0x14, &temp);
+#endif
+
+ dphy_write(0x44, (((csi_pclk_cfg_val.hsfreqrange & 0x3F) << 1) & 0x7E), &temp);
+ dphy_write(0x75, (0x80 | (csi_pclk_cfg_val.hsrxthssettle & 0x7F)), &temp);
+ dphy_write(0x54, 0x14, &temp);
+ dphy_write(0x64, 0x14, &temp);
+ dphy_write(0x74, 0x14, &temp);
+#endif
+ dphy_cfg_done();
+}
+
+void dphy_init(u32 bps_per_lane, u32 phy_id)
+{
+#if IS_ENABLED(VERSION3L) || IS_ENABLED(VERSION3T) || IS_ENABLED(VERSION3D)
+ dpy_ab_clr();
+
+ if (phy_id & 0x01) {
+ dpy_a_enable();
+ dphy_init_common(bps_per_lane, phy_id, 0);
+ }
+
+ if (phy_id & 0x02) {
+ dpy_b_enable();
+ dphy_init_common(bps_per_lane, phy_id, 1);
+ }
+
+ if (0x03 == (phy_id & 0x03)) {
+ dpy_ab_sync();
+ }
+
+ if (phy_id & 0x04) {
+ dpy_c_enable();
+ dphy_init_common(bps_per_lane, phy_id, 0);
+ }
+
+#else
+ dphy_init_common(bps_per_lane, phy_id, 0);
+#endif
+}
+
+u8 csi_init(u64 base_address)
+{
+ csi_error_t e = SUCCESS;
+ do {
+ if (csi_core_initialized == 0) {
+ access_init((u32*)base_address);
+ if (csi_core_read(VERSION) == (u32)(CURRENT_VERSION)) {
+ csi_core_initialized = 1;
+ break;
+ } else {
+ LOG_ERROR("Driver not compatible with core");
+ e = ERR_NOT_COMPATIBLE;
+ break;
+ }
+ } else {
+ LOG_ERROR("driver already initialised");
+ e = ERR_ALREADY_INIT;
+ break;
+ }
+ } while(0);
+
+ return e;
+}
+
+u8 csi_close()
+{
+ csi_shut_down_phy(1);
+ csi_reset_controller();
+ csi_core_initialized = 0;
+ return SUCCESS;
+}
+
+u8 csi_get_on_lanes()
+{
+ return (csi_core_read_part(N_LANES, 0, 2) + 1);
+}
+
+u8 csi_set_on_lanes(u8 lanes)
+{
+ return csi_core_write_part(N_LANES, (lanes - 1), 0, 2);
+}
+
+u8 csi_shut_down_phy(u8 shutdown)
+{
+ LOG_DEBUG2("shutdown", shutdown);
+ /* active low - bit 0 */
+ return csi_core_write_part(PHY_SHUTDOWNZ, shutdown? 0: 1, 0, 1);
+}
+
+u8 csi_reset_phy()
+{
+ /* active low - bit 0 */
+ int retVal = 0xffff;
+ retVal = csi_core_write_part(DPHY_RSTZ, 0, 0, 1);
+ switch (retVal) {
+ case SUCCESS:
+ return csi_core_write_part(DPHY_RSTZ, 1, 0, 1);
+ break;
+ case ERR_NOT_INIT:
+ LOG_ERROR("Driver not initialized");
+ return retVal;
+ break;
+ default:
+ LOG_ERROR("Undefined error");
+ return ERR_UNDEFINED;
+ break;
+ }
+}
+
+u8 csi_reset_controller()
+{
+ /* active low - bit 0 */
+ int retVal = 0xffff;
+ retVal = csi_core_write_part(CSI2_RESETN, 0, 0, 1);
+ switch (retVal) {
+ case SUCCESS:
+ return csi_core_write_part(CSI2_RESETN, 1, 0, 1);
+ break;
+ case ERR_NOT_INIT:
+ LOG_ERROR("Driver not initialized");
+ return retVal;
+ break;
+ default:
+ LOG_ERROR("Undefined error");
+ return ERR_UNDEFINED;
+ break;
+ }
+}
+
+csi_lane_state_t csi_lane_module_state(u8 lane)
+{
+ if (lane < csi_core_read_part(N_LANES, 0, 2) + 1) {
+ if (csi_core_read_part(PHY_STATE, lane, 1)) {
+ return CSI_LANE_ULTRA_LOW_POWER;
+ }
+ else if (csi_core_read_part(PHY_STATE, (lane + 4), 1)) {
+ return CSI_LANE_STOP;
+ }
+ return CSI_LANE_ON;
+ } else {
+ LOG_WARNING("Lane switched off");
+ return CSI_LANE_OFF;
+ }
+}
+
+csi_lane_state_t csi_clk_state()
+{
+ if (!csi_core_read_part(PHY_STATE, 9, 1)) {
+ return CSI_LANE_ULTRA_LOW_POWER;
+ } else if (csi_core_read_part(PHY_STATE, 10, 1)) {
+ return CSI_LANE_STOP;
+ } else if (csi_core_read_part(PHY_STATE, 8, 1)) {
+ return CSI_LANE_HIGH_SPEED;
+ }
+
+ return CSI_LANE_ON;
+}
+
+u8 csi_payload_bypass(u8 on)
+{
+ return csi_core_write_part(PHY_STATE, on? 1: 0, 11, 1);
+}
+
+u8 csi_register_line_event(u8 virtual_channel_no, csi_data_type_t data_type, u8 offset)
+{
+ u8 id = 0;
+ csi_registers_t reg_offset = 0;
+ LOG_TRACE();
+ if ((virtual_channel_no > 4) || (offset > 8)) {
+ return ERR_OUT_OF_BOUND;
+ }
+ id = (virtual_channel_no << 6) | data_type;
+
+ reg_offset = ((offset / 4) == 1) ? DATA_IDS_2: DATA_IDS_1;
+
+ return csi_core_write_part(reg_offset, id, (offset * 8), 8);
+}
+u8 csi_unregister_line_event(u8 offset)
+{
+ csi_registers_t reg_offset = 0;
+ LOG_TRACE();
+ if (offset > 8) {
+ return ERR_OUT_OF_BOUND;
+ }
+ reg_offset = ((offset / 4) == 1) ? DATA_IDS_2: DATA_IDS_1;
+ return csi_core_write_part(reg_offset, 0x00, (offset * 8), 8);
+}
+
+u8 csi_get_registered_line_event(u8 offset)
+{
+ csi_registers_t reg_offset = 0;
+ LOG_TRACE();
+ if (offset > 8) {
+ return ERR_OUT_OF_BOUND;
+ }
+ reg_offset = ((offset / 4) == 1) ? DATA_IDS_2: DATA_IDS_1;
+ return (u8)csi_core_read_part(reg_offset, (offset * 8), 8);
+}
+
+u8 csi_event_disable(u32 mask, u8 err_reg_no)
+{
+ switch (err_reg_no) {
+ case 1:
+ return csi_core_write(MASK1, mask | csi_core_read(MASK1));
+ case 2:
+ return csi_core_write(MASK2, mask | csi_core_read(MASK2));
+ default:
+ return ERR_OUT_OF_BOUND;
+ }
+}
+
+u8 csi_event_enable(u32 mask, u8 err_reg_no)
+{
+ switch (err_reg_no) {
+ case 1:
+ return csi_core_write(MASK1, (~mask) & csi_core_read(MASK1));
+ case 2:
+ return csi_core_write(MASK2, (~mask) & csi_core_read(MASK2));
+ default:
+ return ERR_OUT_OF_BOUND;
+ }
+}
+u32 csi_event_get_source(u8 err_reg_no)
+{
+ switch (err_reg_no) {
+ case 1:
+ return csi_core_read(ERR1);
+ case 2:
+ return csi_core_read(ERR2);
+ default:
+ return ERR_OUT_OF_BOUND;
+ }
+}
+
+/* register methods */
+u32 csi_core_read(csi_registers_t address)
+{
+ return access_read(address>>2);
+}
+
+u8 csi_core_write(csi_registers_t address, u32 data)
+{
+ if (csi_core_initialized == 0) {
+ LOG_ERROR("driver not initialised");
+ return ERR_NOT_INIT;
+ }
+ LOG_DEBUG3("write", data, address>>2);
+ access_write(data, address>>2);
+
+ return SUCCESS;
+}
+
+u8 csi_core_write_part(csi_registers_t address, u32 data, u8 shift, u8 width)
+{
+ u32 mask = (1 << width) - 1;
+ u32 temp = csi_core_read(address);
+ temp &= ~(mask << shift);
+ temp |= (data & mask) << shift;
+ return csi_core_write(address, temp);
+}
+
+u32 csi_core_read_part(csi_registers_t address, u8 shift, u8 width)
+{
+ return (csi_core_read(address) >> shift) & ((1 << width) - 1);
+}
+