/* From Xiaomi */ static int drv2604_probe(struct i2c_client* client, const struct i2c_device_id* id) { char status; #if SKIP_LRA_AUTOCAL == 0 int nCalibrationCount = 0; #endif unsigned char tmp[] = { MODE_REG, MODE_STANDBY }; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { DbgOut((DBL_ERROR, "drv2604 on M3 probe failed")); return -ENODEV; } /* Enable power to the chip */ gpio_direction_output(GPIO_VIBTONE_EN1, GPIO_LEVEL_HIGH); /* Wait 30 us */ udelay(30); g_pTheClient = client; g_hw_version = get_hw_version_major(); if(g_hw_version == 5) if(get_hw_version_minor() != 1) g_hw_version = 6; /* compatible with x3,x5 (LRA) and x4 (ERM) */ if (g_hw_version == 4) { DRV_init_sequence = ERM_init_sequence; seq_size = sizeof(ERM_init_sequence); } else if (g_hw_version == 5) { DRV_init_sequence = NID_init_sequence; seq_size = sizeof(NID_init_sequence); } else if (g_hw_version == 6) { DRV_init_sequence = X5LRA_init_sequence; seq_size = sizeof(X5LRA_init_sequence); g_autotune_brake_enabled = true; } else { DRV_init_sequence = LRA_init_sequence; seq_size = sizeof(LRA_init_sequence); g_autotune_brake_enabled = true; } #if SKIP_LRA_AUTOCAL == 1 drv2604_write_reg_val(DRV_init_sequence, seq_size); status = drv2604_read_reg(STATUS_REG); #else /* Run auto-calibration */ do { drv2604_write_reg_val(LRA_autocal_sequence, sizeof(LRA_autocal_sequence)); /* Wait until the procedure is done */ drv2604_poll_go_bit(); /* Read status */ status = drv2604_read_reg(STATUS_REG); nCalibrationCount++; } while (((status & DIAG_RESULT_MASK) == AUTO_CAL_FAILED) && (nCalibrationCount < MAX_AUTOCALIBRATION_ATTEMPT)); /* Check result */ if ((status & DIAG_RESULT_MASK) == AUTO_CAL_FAILED) { DbgOut((DBL_ERROR, "drv2604 auto-calibration failed after %d attempts.\n", nCalibrationCount)); } else { /* restore 0x1a */ drv2604_write_reg_val(LRA_autocal_done_seq, sizeof(LRA_autocal_done_seq)); /* Read calibration results */ drv2604_read_reg(AUTO_CALI_RESULT_REG); drv2604_read_reg(AUTO_CALI_BACK_EMF_RESULT_REG); drv2604_read_reg(FEEDBACK_CONTROL_REG); } #endif /* Read device ID */ g_nDeviceID = (status & DEV_ID_MASK); switch (g_nDeviceID) { case DRV2605: DbgOut((DBL_INFO, "drv2604 driver found: drv2605.\n")); break; case DRV2604: DbgOut((DBL_INFO, "drv2604 driver found: drv2604.\n")); break; default: DbgOut((DBL_INFO, "drv2604 driver found: unknown.\n")); break; } #if USE_DRV2604_STANDBY /* Put hardware in standby */ drv2604_write_reg_val(tmp, sizeof(tmp)); #elif USE_DRV2604_EN_PIN /* enable RTP mode that will be toggled on/off with EN pin */ drv2604_change_mode(MODE_REAL_TIME_PLAYBACK); #endif #if USE_DRV2604_EN_PIN /* turn off chip */ drv2604_set_en(false); #endif debugfs_create_file("drv2604", 0644, NULL, NULL, &drv2604_dbg); DbgOut((DBL_INFO, "drv2604 on M3 probe succeeded")); /* From Xiaomi start */ #if SUPPORT_WRITE_PAT INIT_WORK(&vibdata.pat_work, drv2604_pat_work); vibdata.hap_wq = alloc_workqueue("haptic_wq", WQ_HIGHPRI, 0); if (vibdata.hap_wq == NULL) { printk("drv2604 alloc workqueue failed"); } vibdata.pat = pattern; #endif /* From Xiaomi end */ printk(KERN_ALERT"drv2604 probe succeeded"); return 0; }
static int __devinit drv2604_vibrator_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int nRet, i,error; /* initialized below */ char status; #if SKIP_AUTOCAL == 0 int nCalibrationCount = 0; #endif DbgOut((KERN_INFO "tspdrv: Probe called.\n")); vibrator_drvdata.client = client; if(!(client->dev.of_node)){ DbgOut(KERN_ERR "tspdrv: tspdrv probe failed, DT is NULL"); return -ENODEV; } error = drv2604_parse_dt(&client->dev); if (error) return error; if( gpio_request(vibrator_drvdata.motor_en, "MOTOR_EN") < 0) { return -EINVAL; } gpio_direction_output(vibrator_drvdata.motor_en, 0); gpio_export(vibrator_drvdata.motor_en, 0); nRet = misc_register(&miscdev); if (nRet) { DbgOut((KERN_ERR "tspdrv: misc_register failed.\n")); return nRet; } #if USE_DRV2604_EN_PIN drv2604_set_en(true); #endif #if USE_DRV2604_STANDBY /* Wait 1000 us for chip power to stabilize */ usleep_range(100000, 100000); drv2604_change_mode(MODE_DEVICE_READY); #endif /* Wait 1000 us for chip power to stabilize */ usleep_range(1000, 1000); #if SKIP_AUTOCAL usleep_range(100000, 100000); drv2604_write_reg_val(init_sequence, sizeof(init_sequence)); status = drv2604_read_reg(STATUS_REG); #else /* Run auto-calibration */ do{ drv2604_write_reg_val(autocal_sequence, sizeof(autocal_sequence)); /* Wait until the procedure is done */ drv2604_poll_go_bit(); /* Read status */ status = drv2604_read_reg(STATUS_REG); nCalibrationCount++; } while (((status & DIAG_RESULT_MASK) == AUTO_CAL_FAILED) && (nCalibrationCount < MAX_AUTOCALIBRATION_ATTEMPT)); /* Check result */ if ((status & DIAG_RESULT_MASK) == AUTO_CAL_FAILED) { DbgOut((DBL_ERROR, "drv2604 auto-calibration failed after %d attempts.\n", nCalibrationCount)); } else { /* Read calibration results */ drv2604_read_reg(AUTO_CALI_RESULT_REG); drv2604_read_reg(AUTO_CALI_BACK_EMF_RESULT_REG); drv2604_read_reg(FEEDBACK_CONTROL_REG); } #endif /* Read device ID */ g_nDeviceID = (status & DEV_ID_MASK); switch (g_nDeviceID) { case DRV2605: DbgOut((DBL_INFO, "drv2604 driver found: drv2605.\n")); break; case DRV2604: DbgOut((DBL_INFO, "drv2604 driver found: drv2604.\n")); break; case DRV2604L: DbgOut((DBL_INFO, "drv2604 driver found: drv2604L.\n")); break; case DRV2605L: DbgOut((DBL_INFO, "drv2604 driver found: drv2605L.\n")); break; default: DbgOut((DBL_INFO, "drv2604 driver found: unknown.\n")); break; } #if USE_DRV2604_STANDBY /* Put hardware in standby */ drv2604_change_mode(MODE_STANDBY); #elif USE_DRV2604_EN_PIN /* enable RTP mode that will be toggled on/off with EN pin */ #endif #if USE_DRV2604_EN_PIN /* turn off chip */ drv2604_set_en(false); #endif DbgRecorderInit(()); ImmVibeSPI_ForceOut_Initialize(); VibeOSKernelLinuxInitTimer(); ResetOutputData(); /* Get and concatenate device name and initialize data buffer */ g_cchDeviceName = 0; for (i=0; i<NUM_ACTUATORS; i++) { char *szName = g_szDeviceName + g_cchDeviceName; ImmVibeSPI_Device_GetName(i, szName, VIBE_MAX_DEVICE_NAME_LENGTH); /* Append version information and get buffer length */ strcat(szName, VERSION_STR); g_cchDeviceName += strlen(szName); } vibetonz_start(); return 0; }