Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9f91edb1 authored by Mohan Pallaka's avatar Mohan Pallaka Committed by Abinaya P
Browse files

platform: msm: add support for PWM mode



This patch is ported from 3.4 kernel patch
- 897f2133b9cf5f36397942055ea8a85ec4838f8b,
platform: msm: add support for PWM mode

QPNP vibrator operates in two modes - manual and
PWM (Pulse Width Modulation). PWM is supported
through one of dtest lines connected to the
vibrator. Add support for PWM mode by parsing
channel, period and duty cycle information
from device tree.

Change-Id: I2ba5c1592dfce75d5e8dedd247d1e91c2931a832
Signed-off-by: default avatarMohan Pallaka <mpallaka@codeaurora.org>
parent 6e830ef5
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -13,6 +13,16 @@ Required Properties:
Optional Properties:
 - qcom,vib-timeout-ms: timeout of vibrator, in ms.  Default 15000 ms
 - qcom,vib-vtg-level-mV: voltage level, in mV.  Default 3100 mV
 - qcom,mode: mode in which vibrator is operating. It can be one of
		"manual", "dtest1", "dtest2" and "dtest3"
 - qcom,active-low: boolean to specify if the dtestx is operated
		in active low or high

Required Properties for "dtestx" mode:
 - qcom,pwm-channel: pwm channel number
 - qcom,period-us: period for pwm in micro secs
 - qcom,duty-us: duty cycle for pwm in micro secs


Example:
		qcom,vib@c000 {
@@ -22,4 +32,9 @@ Example:
			label = "vibrator";
			qcom,vib-timeout-ms = <15000>;
			qcom,vib-vtg-level-mV = <3100>;
			qcom,mode = "dtest3";
			qcom,pwm-channel = <5>;
			qcom,period-us = <1000>;
			qcom,duty-us = <750>;
			qcom,active-low;
		};
+152 −76
Original line number Diff line number Diff line
@@ -18,8 +18,8 @@
#include <linux/hrtimer.h>
#include <linux/of_device.h>
#include <linux/spmi.h>

#include <linux/qpnp/vibrator.h>
#include <linux/qpnp/pwm.h>
#include <linux/err.h>
#include "../../staging/android/timed_output.h"

#define QPNP_VIB_VTG_CTL(base)		(base + 0x41)
@@ -35,14 +35,31 @@
#define QPNP_VIB_VTG_SET_MASK		0x1F
#define QPNP_VIB_LOGIC_SHIFT		4

enum qpnp_vib_mode {
	QPNP_VIB_MANUAL,
	QPNP_VIB_DTEST1,
	QPNP_VIB_DTEST2,
	QPNP_VIB_DTEST3,
};

struct qpnp_pwm_info {
	struct pwm_device *pwm_dev;
	u32 pwm_channel;
	u32 duty_us;
	u32 period_us;
};

struct qpnp_vib {
	struct spmi_device *spmi;
	struct hrtimer vib_timer;
	struct timed_output_dev timed_dev;
	struct work_struct work;
	struct qpnp_pwm_info pwm_info;
	enum   qpnp_vib_mode mode;

	u8  reg_vtg_ctl;
	u8  reg_en_ctl;
	u8  active_low;
	u16 base;
	int state;
	int vtg_level;
@@ -50,8 +67,6 @@ struct qpnp_vib {
	struct mutex lock;
};

static struct qpnp_vib *vib_dev;

static int qpnp_vib_read_u8(struct qpnp_vib *vib, u8 *data, u16 reg)
{
	int rc;
@@ -78,52 +93,53 @@ static int qpnp_vib_write_u8(struct qpnp_vib *vib, u8 *data, u16 reg)
	return rc;
}

int qpnp_vibrator_config(struct qpnp_vib_config *vib_cfg)
static int qpnp_vibrator_config(struct qpnp_vib *vib)
{
	u8 reg = 0;
	int rc = -EINVAL, level;

	if (vib_dev == NULL) {
		pr_err("%s: vib_dev is NULL\n", __func__);
		return -ENODEV;
	}

	level = vib_cfg->drive_mV / 100;
	if (level) {
		if ((level < QPNP_VIB_MIN_LEVEL) ||
				(level > QPNP_VIB_MAX_LEVEL)) {
			dev_err(&vib_dev->spmi->dev, "Invalid voltage level\n");
			return -EINVAL;
		}
	} else {
		dev_err(&vib_dev->spmi->dev, "Voltage level not specified\n");
		return -EINVAL;
	}
	int rc;

	/* Configure the VTG CTL regiser */
	reg = vib_dev->reg_vtg_ctl;
	rc = qpnp_vib_read_u8(vib, &reg, QPNP_VIB_VTG_CTL(vib->base));
	if (rc < 0)
		return rc;
	reg &= ~QPNP_VIB_VTG_SET_MASK;
	reg |= (level & QPNP_VIB_VTG_SET_MASK);
	rc = qpnp_vib_write_u8(vib_dev, &reg, QPNP_VIB_VTG_CTL(vib_dev->base));
	reg |= (vib->vtg_level & QPNP_VIB_VTG_SET_MASK);
	rc = qpnp_vib_write_u8(vib, &reg, QPNP_VIB_VTG_CTL(vib->base));
	if (rc)
		return rc;
	vib_dev->reg_vtg_ctl = reg;
	vib->reg_vtg_ctl = reg;

	/* Configure the VIB ENABLE regiser */
	reg = vib_dev->reg_en_ctl;
	reg |= (!!vib_cfg->active_low) << QPNP_VIB_LOGIC_SHIFT;
	if (vib_cfg->enable_mode == QPNP_VIB_MANUAL)
		reg |= QPNP_VIB_EN;
	else
		reg |= BIT(vib_cfg->enable_mode - 1);
	rc = qpnp_vib_write_u8(vib_dev, &reg, QPNP_VIB_EN_CTL(vib_dev->base));
	rc = qpnp_vib_read_u8(vib, &reg, QPNP_VIB_EN_CTL(vib->base));
	if (rc < 0)
		return rc;
	reg |= (!!vib->active_low) << QPNP_VIB_LOGIC_SHIFT;
	if (vib->mode != QPNP_VIB_MANUAL) {
		vib->pwm_info.pwm_dev = pwm_request(vib->pwm_info.pwm_channel,
								 "qpnp-vib");
		if (IS_ERR_OR_NULL(vib->pwm_info.pwm_dev)) {
			dev_err(&vib->spmi->dev, "vib pwm request failed\n");
			return -ENODEV;
		}

		rc = pwm_config(vib->pwm_info.pwm_dev, vib->pwm_info.duty_us,
						vib->pwm_info.period_us);
		if (rc < 0) {
			dev_err(&vib->spmi->dev, "vib pwm config failed\n");
			pwm_free(vib->pwm_info.pwm_dev);
			return -ENODEV;
		}

		reg |= BIT(vib->mode - 1);
	}

	rc = qpnp_vib_write_u8(vib, &reg, QPNP_VIB_EN_CTL(vib->base));
	if (rc < 0)
		return rc;
	vib_dev->reg_en_ctl = reg;
	vib->reg_en_ctl = reg;

	return rc;
}
EXPORT_SYMBOL(qpnp_vibrator_config);

static int qpnp_vib_set(struct qpnp_vib *vib, int on)
{
@@ -131,29 +147,32 @@ static int qpnp_vib_set(struct qpnp_vib *vib, int on)
	u8 val;

	if (on) {
		val = vib->reg_vtg_ctl;
		val &= ~QPNP_VIB_VTG_SET_MASK;
		val |= (vib->vtg_level & QPNP_VIB_VTG_SET_MASK);
		rc = qpnp_vib_write_u8(vib, &val, QPNP_VIB_VTG_CTL(vib->base));
		if (rc < 0)
			return rc;
		vib->reg_vtg_ctl = val;
		if (vib->mode != QPNP_VIB_MANUAL) {
			pwm_enable(vib->pwm_info.pwm_dev);
		} else {
			val = vib->reg_en_ctl;
			val |= QPNP_VIB_EN;
		rc = qpnp_vib_write_u8(vib, &val, QPNP_VIB_EN_CTL(vib->base));
			rc = qpnp_vib_write_u8(vib, &val,
					QPNP_VIB_EN_CTL(vib->base));
			if (rc < 0)
				return rc;
			vib->reg_en_ctl = val;
		}
	} else {
		if (vib->mode != QPNP_VIB_MANUAL) {
			pwm_disable(vib->pwm_info.pwm_dev);
		} else {
			val = vib->reg_en_ctl;
			val &= ~QPNP_VIB_EN;
		rc = qpnp_vib_write_u8(vib, &val, QPNP_VIB_EN_CTL(vib->base));
			rc = qpnp_vib_write_u8(vib, &val,
					QPNP_VIB_EN_CTL(vib->base));
			if (rc < 0)
				return rc;
			vib->reg_en_ctl = val;
		}
	}

	return rc;
	return 0;
}

static void qpnp_vib_enable(struct timed_output_dev *dev, int value)
@@ -225,26 +244,19 @@ static int qpnp_vibrator_suspend(struct device *dev)

static SIMPLE_DEV_PM_OPS(qpnp_vibrator_pm_ops, qpnp_vibrator_suspend, NULL);

static int qpnp_vibrator_probe(struct spmi_device *spmi)
static int qpnp_vib_parse_dt(struct qpnp_vib *vib)
{
	struct qpnp_vib *vib;
	struct resource *vib_resource;
	struct spmi_device *spmi = vib->spmi;
	int rc;
	u8 val;
	const char *mode;
	u32 temp_val;

	vib = devm_kzalloc(&spmi->dev, sizeof(*vib), GFP_KERNEL);
	if (!vib)
		return -ENOMEM;

	vib->spmi = spmi;

	vib->timeout = QPNP_VIB_DEFAULT_TIMEOUT;
	rc = of_property_read_u32(spmi->dev.of_node,
			"qcom,vib-timeout-ms", &temp_val);
	if (!rc) {
		vib->timeout = temp_val;
	} else if (rc != EINVAL) {
	} else if (rc != -EINVAL) {
		dev_err(&spmi->dev, "Unable to read vib timeout\n");
		return rc;
	}
@@ -260,6 +272,71 @@ static int qpnp_vibrator_probe(struct spmi_device *spmi)
	}

	vib->vtg_level /= 100;
	if (vib->vtg_level < QPNP_VIB_MIN_LEVEL)
		vib->vtg_level = QPNP_VIB_MIN_LEVEL;
	else if (vib->vtg_level > QPNP_VIB_MAX_LEVEL)
		vib->vtg_level = QPNP_VIB_MAX_LEVEL;

	vib->mode = QPNP_VIB_MANUAL;
	rc = of_property_read_string(spmi->dev.of_node, "qcom,mode", &mode);
	if (!rc) {
		if (strcmp(mode, "manual") == 0) {
			vib->mode = QPNP_VIB_MANUAL;
		} else if (strcmp(mode, "dtest1") == 0) {
			vib->mode = QPNP_VIB_DTEST1;
		} else if (strcmp(mode, "dtest2") == 0) {
			vib->mode = QPNP_VIB_DTEST2;
		} else if (strcmp(mode, "dtest3") == 0) {
			vib->mode = QPNP_VIB_DTEST3;
		} else {
			dev_err(&spmi->dev, "Invalid mode\n");
			return -EINVAL;
		}
	} else if (rc != -EINVAL) {
		dev_err(&spmi->dev, "Unable to read mode\n");
		return rc;
	}

	if (vib->mode != QPNP_VIB_MANUAL) {
		rc = of_property_read_u32(spmi->dev.of_node,
				"qcom,pwm-channel", &temp_val);
		if (!rc)
			vib->pwm_info.pwm_channel = temp_val;
		else
			return rc;

		rc = of_property_read_u32(spmi->dev.of_node,
				"qcom,period-us", &temp_val);
		if (!rc)
			vib->pwm_info.period_us = temp_val;
		else
			return rc;

		rc = of_property_read_u32(spmi->dev.of_node,
				"qcom,duty-us", &temp_val);
		if (!rc)
			vib->pwm_info.duty_us = temp_val;
		else
			return rc;
	}

	vib->active_low = of_property_read_bool(spmi->dev.of_node,
				"qcom,active-low");

	return 0;
}

static int qpnp_vibrator_probe(struct spmi_device *spmi)
{
	struct qpnp_vib *vib;
	struct resource *vib_resource;
	int rc;

	vib = devm_kzalloc(&spmi->dev, sizeof(*vib), GFP_KERNEL);
	if (!vib)
		return -ENOMEM;

	vib->spmi = spmi;

	vib_resource = spmi_get_resource(spmi, 0, IORESOURCE_MEM, 0);
	if (!vib_resource) {
@@ -268,16 +345,17 @@ static int qpnp_vibrator_probe(struct spmi_device *spmi)
	}
	vib->base = vib_resource->start;

	/* save the control registers values */
	rc = qpnp_vib_read_u8(vib, &val, QPNP_VIB_VTG_CTL(vib->base));
	if (rc < 0)
	rc = qpnp_vib_parse_dt(vib);
	if (rc) {
		dev_err(&spmi->dev, "DT parsing failed\n");
		return rc;
	vib->reg_vtg_ctl = val;
	}

	rc = qpnp_vib_read_u8(vib, &val, QPNP_VIB_EN_CTL(vib->base));
	if (rc < 0)
	rc = qpnp_vibrator_config(vib);
	if (rc) {
		dev_err(&spmi->dev, "vib config failed\n");
		return rc;
	vib->reg_en_ctl = val;
	}

	mutex_init(&vib->lock);
	INIT_WORK(&vib->work, qpnp_vib_update);
@@ -295,8 +373,6 @@ static int qpnp_vibrator_probe(struct spmi_device *spmi)
	if (rc < 0)
		return rc;

	vib_dev = vib;

	return rc;
}

include/linux/qpnp/vibrator.h

deleted100644 → 0
+0 −39
Original line number Diff line number Diff line
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#ifndef __QPNP_VIBRATOR_H__
#define __QPNP_VIBRATOR_H__

enum qpnp_vib_en_mode {
	QPNP_VIB_MANUAL,
	QPNP_VIB_DTEST1,
	QPNP_VIB_DTEST2,
	QPNP_VIB_DTEST3,
};

struct qpnp_vib_config {
	u16			drive_mV;
	u8			active_low;
	enum qpnp_vib_en_mode	enable_mode;
};
#if defined(CONFIG_QPNP_VIBRATOR)

int qpnp_vibrator_config(struct qpnp_vib_config *vib_config);
#else

static inline int qpnp_vibrator_config(struct qpnp_vib_config *vib_config)
{
	return -ENODEV;
}
#endif

#endif /* __QPNP_VIBRATOR_H__ */