/*
** 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;
}
예제 #2
0
static void vibrator_enable(struct timed_output_dev *dev, int value)
{
#if SUPPORT_TIMED_OUTPUT
    char mode;
    hrtimer_cancel(&vibdata.timer);
    cancel_work_sync(&vibdata.work);

#if SUPPORT_WRITE_PAT
    cancel_work_sync(&vibdata.pat_work);
#endif
    mutex_lock(&vibdata.lock);

    if (value) {
        wake_lock(&vibdata.wklock);
        drv2604_read_reg(STATUS_REG);

        /* Added by Ken on 20120531 */
        if (!g_bAmpEnabled) {
            mode = drv2604_read_reg(MODE_REG) & DRV2604_MODE_MASK;
            /* Modified by Ken on 20120530 */
#if DRV2604_USE_RTP_MODE
            /* Only change the mode if not already in RTP mode; RTP input already set at init */
            if (mode != MODE_REAL_TIME_PLAYBACK) {
                drv2604_change_mode(MODE_REAL_TIME_PLAYBACK);
                drv2604_set_rtp_val(vibe_strength);
                vibrator_is_playing = YES;
                g_bAmpEnabled = true;
            }
#endif
#if DRV2604_USE_PWM_MODE
            /* Only change the mode if not already in PWM mode */
            if (mode != MODE_PWM_OR_ANALOG_INPUT) {
                pwm_duty_enable(vibdata.pwm_dev, 0);
                drv2604_change_mode(MODE_PWM_OR_ANALOG_INPUT);
                vibrator_is_playing = YES;
                g_bAmpEnabled = true;
            }
#endif
        }

        if (value > 0) {
            if (value > MAX_TIMEOUT)
                value = MAX_TIMEOUT;
            hrtimer_start(&vibdata.timer, ns_to_ktime((u64)value * NSEC_PER_MSEC), HRTIMER_MODE_REL);
        }
    } else
        vibrator_off();

    mutex_unlock(&vibdata.lock);
#endif // SUPPORT_TIMED_OUTPUT
}
예제 #3
0
static ssize_t drv2604_write_pattern(struct file *filp, struct kobject *kobj,
                                     struct bin_attribute *attr,
                                     char *buffer, loff_t offset, size_t count)
{
#if SUPPORT_TIMED_OUTPUT
#if SUPPORT_WRITE_PAT
    mutex_lock(&vibdata.lock);
    wake_lock(&vibdata.wklock);
    pr_debug("%s count:%d [%d %d %d %d %d %d %d %d %d ]", __func__,
             count, buffer[0], buffer[1], buffer[2], buffer[3],
             buffer[4], buffer[5], buffer[6], buffer[7], buffer[8]);

    vibdata.pat_len = 0;
    cancel_work_sync(&vibdata.pat_work);

    memcpy(pattern, buffer, count);
    pattern[count] = 0;	// add 0 in end in case user forgot to disable
    pattern[count + 1] = 0;
    vibdata.pat_mode = pattern[0];
    vibdata.pat_len = count + 2;
    vibdata.pat_i = 1;

    drv2604_change_mode(MODE_PWM_OR_ANALOG_INPUT);
    queue_work(vibdata.hap_wq, &vibdata.pat_work);

    mutex_unlock(&vibdata.lock);
#endif
#endif // SUPPORT_TIMED_OUTPUT
    return 0;
}
예제 #4
0
static void drv2604_pat_work(struct work_struct *work)
{
    int i;
    u32 value = 0;
    u32 time  = 0;

    for (i = 1; i < vibdata.pat_len; i += 2) {
        time = (u8)vibdata.pat[i + 1];
        if (vibdata.pat[i] != 0) {
            value = (vibdata.pat[i] > 0)?(vibdata.pat[i]):0;
            if(value > 126)
                value = 256;
            else
                value += 128;
            pwm_duty_enable(vibdata.pwm_dev, value);
            msleep(time);
        } else {
            if ((time == 0) || (i + 2 >= vibdata.pat_len )) { /* the end */
                pwm_disable(vibdata.pwm_dev);
                drv2604_change_mode(MODE_STANDBY);
                pr_debug("drv2604 vib len:%d time:%d", vibdata.pat_len, time);
                break;
            } else {
                pwm_duty_enable(vibdata.pwm_dev, 0);
                msleep(time);
            }
        }
        pr_debug("%s: %d vib:%d time:%d value:%u",__func__, i, vibdata.pat[i], time, value);
    }
    wake_unlock(&vibdata.wklock);
}
/*
** 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;
}
예제 #6
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;
}
예제 #7
0
static void vibrator_off(void)
{
#if SUPPORT_TIMED_OUTPUT
    if (vibrator_is_playing) {
        vibrator_is_playing = NO;
        drv2604_change_mode(MODE_STANDBY);
        /* Added by Ken on 20120531 */
        g_bAmpEnabled = false;
    }

    wake_unlock(&vibdata.wklock);
#endif
}
예제 #8
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
}
예제 #9
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;
}
예제 #10
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;
}