Example #1
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
}
Example #2
0
/*
** Called to get the device name (device name must be returned as ANSI char)
*/
IMMVIBESPIAPI VibeStatus ImmVibeSPI_Device_GetName(VibeUInt8 nActuatorIndex, char *szDevName, int nSize)
{
    char szRevision[MAX_REVISION_STRING_SIZE];

    if ((!szDevName) || (nSize < 1)) return VIBE_E_FAIL;

    DbgOut((DBL_VERBOSE, "ImmVibeSPI_Device_GetName.\n"));

    switch (g_nDeviceID)
    {
    case DRV2605:
        strncpy(szDevName, "DRV2605", nSize-1);
        break;
    case DRV2604:
        strncpy(szDevName, "DRV2604", nSize-1);
        break;
    default:
        strncpy(szDevName, "Unknown", nSize-1);
        break;
    }

    /* Append revision number to the device name */
    sprintf(szRevision, " Rev:%d", (drv2604_read_reg(SILICON_REVISION_REG) & SILICON_REVISION_MASK));
    if ((strlen(szRevision) + strlen(szDevName)) < nSize - 1)
        strcat(szDevName, szRevision);

    szDevName[nSize - 1] = '\0'; /* make sure the string is NULL terminated */

    return VIBE_S_SUCCESS;
}
/*
** 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;
}
static ssize_t drv_debug_read(struct file *filp,
	char __user *buffer, size_t count, loff_t *ppos)
{
	char *buf;
	size_t len = 0;
	ssize_t ret;
	int i;

	if (*ppos != 0)
		return 0;

	if (count < sizeof(buf))
		return -ENOSPC;

	buf = kzalloc(TSPDRV_BUF_SIZE, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	for(i = 0x00; i <= 0xFF; i += 0x01){
		if(( 0x00 <= i && i <= 0x10) || (0x16 <= i && i <= 0x22) || (0xFD <= i && i<=0xFF)){
			len += snprintf(buf + len, TSPDRV_BUF_SIZE - len,
			"drv2604 reg addr = %x , value =%x\n", i, drv2604_read_reg(i));
		}
	}
	ret = simple_read_from_buffer(buffer, len, ppos, buf, TSPDRV_BUF_SIZE);
	kfree(buf);

	return ret;
}
Example #5
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;
}
Example #6
0
static int drv2604_dbg_get(void *data, u64 *val)
{
    *val = g_nDeviceID;
    drv2604_read_reg(g_dbg_reg);
    return 0;
}
Example #7
0
static void drv2604_poll_go_bit(void)
{
    while (drv2604_read_reg(GO_REG) == GO)
        schedule_timeout_interruptible(msecs_to_jiffies(GO_BIT_POLL_INTERVAL));
}
Example #8
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;
}