/*
** Called to enable amp (enable output force)
*/
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpEnable(VibeUInt8 nActuatorIndex)
{
    if (!g_bAmpEnabled)
    {
#if GUARANTEE_AUTOTUNE_BRAKE_TIME
        cancel_delayed_work_sync(&g_brake_complete);
#endif

#if USE_DRV2604_EN_PIN
        drv2604_set_en(true);
#endif

#if USE_DRV2604_STANDBY
        drv2604_change_mode(MODE_DEVICE_READY);
#endif
        // Chip requires minimum 250us power-up delay before RTP value can be set
        // If the chip is powered on <10ms after powering off, it needs 1000us
        // for the internal LDO voltage to stabilize
        usleep_range(1000, 1000);

        /* Workaround for power issue in the DRV2604 */
        /* Restore the register settings if they have reset to the defaults */

        if(drv2604_read_reg(RATED_VOLTAGE_REG) != init_sequence[3])
        {
            drv2604_write_reg_val(init_sequence, sizeof(init_sequence));
        }

        drv2604_change_mode(MODE_REAL_TIME_PLAYBACK);
        g_bAmpEnabled = true;
    }
    return VIBE_S_SUCCESS;
}
/*
** Called to disable amp (disable output force)
*/
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpDisable(VibeUInt8 nActuatorIndex)
{
    if (g_bAmpEnabled)
    {
        /* Set the force to 0 */
        drv2604_set_rtp_val(0);

#if GUARANTEE_AUTOTUNE_BRAKE_TIME
        /* if a brake signal arrived from daemon, let the chip stay on
         * extra time to allow it to brake */
        if (g_brake && g_workqueue)
        {
            queue_delayed_work(g_workqueue,
                               &g_brake_complete,
                               msecs_to_jiffies(AUTOTUNE_BRAKE_TIME));
        }
        else /* disable immediately (smooth effect style) */
#endif
        {
#if USE_DRV2604_STANDBY
            /* Put hardware in standby via i2c */
            drv2604_change_mode(MODE_STANDBY);
#endif

#if USE_DRV2604_EN_PIN
            /* Disable hardware via pin */
            drv2604_set_en(false);
#endif
        }

        g_bAmpEnabled = false;
    }
    return VIBE_S_SUCCESS;
}
Пример #3
0
static void autotune_brake_complete(struct work_struct *work)
{
    /* new nForce value came in before workqueue terminated */
    if (g_lastForce > 0)
        return;

#if USE_DRV2604_STANDBY
    /* Put hardware in standby */
    drv2604_change_mode(MODE_STANDBY);
#endif

#if USE_DRV2604_EN_PIN
    drv2604_set_en(false);
#endif
}
Пример #4
0
/*
** Called to enable amp (enable output force)
*/
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpEnable(VibeUInt8 nActuatorIndex)
{
    if (!g_bAmpEnabled) {
        DbgOut((DBL_VERBOSE, "ImmVibeSPI_ForceOut_AmpEnable.\n"));
        if(g_autotune_brake_enabled)
            cancel_delayed_work_sync(&g_brake_complete);

#if USE_DRV2604_EN_PIN
        drv2604_set_en(true);
#endif

#if USE_DRV2604_STANDBY
#if DRV2604_USE_RTP_MODE
        drv2604_change_mode(MODE_REAL_TIME_PLAYBACK);
#endif
#if DRV2604_USE_PWM_MODE
        /* From Xiaomi start */
        pwm_duty_enable(vibdata.pwm_dev, 0);
        drv2604_change_mode(MODE_PWM_OR_ANALOG_INPUT);
        /* From Xiaomi end */
#endif
#endif
        // Chip requires minimum 250us power-up delay before RTP value can be set
        // If the chip is powered on <10ms after powering off, it needs 1000us
        // for the internal LDO voltage to stabilize

        if(g_hw_version == 6) {
            // X5 use 2604L, dont need to sleep
        } else {
            usleep_range(1000, 1000);

            /* Workaround for power issue in the DRV2604 */
            /* Restore the register settings if they have reset to the defaults */
#if SKIP_LRA_AUTOCAL == 1
            if (drv2604_read_reg(RATED_VOLTAGE_REG) != DRV_init_sequence[43]) {
                printk(KERN_INFO "ImmVibeSPI_ForceOut_AmpEnable: Register values resent.\n");
                drv2604_write_reg_val(DRV_init_sequence, seq_size);
            }
#endif
        }
        g_bAmpEnabled = true;
        g_bNeedToRestartPlayBack = true;
    }
    return VIBE_S_SUCCESS;
}
Пример #5
0
/*
** Called to disable amp (disable output force)
*/
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpDisable(VibeUInt8 nActuatorIndex)
{
    if (g_bAmpEnabled)
    {
        DbgOut((DBL_VERBOSE, "ImmVibeSPI_ForceOut_AmpDisable.\n"));

        /* Set the force to 0 */
#if DRV2604_USE_RTP_MODE
        drv2604_set_rtp_val(0);
#endif
#if DRV2604_USE_PWM_MODE
        /* From Xiaomi start */
        pwm_duty_enable(vibdata.pwm_dev, 0);
        /* From Xiaomi end */
#endif

        /* if a brake signal arrived from daemon, let the chip stay on
         * extra time to allow it to brake */
        if (g_autotune_brake_enabled && g_brake && g_workqueue) {
            queue_delayed_work(g_workqueue,
                               &g_brake_complete,
                               msecs_to_jiffies(AUTOTUNE_BRAKE_TIME));
        } else /* disable immediately (smooth effect style) */
        {
#if USE_DRV2604_STANDBY
            /* Put hardware in standby via i2c */
            drv2604_change_mode(MODE_STANDBY);
#endif
#if USE_DRV2604_EN_PIN
            /* Disable hardware via pin */
            drv2604_set_en(false);
#endif
        }
        g_bAmpEnabled = false;
    }
    return VIBE_S_SUCCESS;
}
Пример #6
0
/* 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;
}