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

Commit e8cff389 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "input: misc: qti-haptics: Add audio/PWM mode as per effect settings"

parents 6d8164b9 cbb53763
Loading
Loading
Loading
Loading
+20 −14
Original line number Diff line number Diff line
@@ -59,19 +59,6 @@ Properties:
		specified in the LRA actuator datasheet. Allowed values are:
		0 to 20475. If this is not specified, 5715us play rate is used.

- qcom,external-waveform-source
  Usage:      optional
  Value type: <string>
  Definition: The haptics module supports to play with internal constant
		Vmax strength or play with patterns specified in its internal
		8-bytes waveform buffer. It can also play with the audio
		LINE-IN signal or PWM waveform coming from LINE-IN/PWM pin.
		This property specify the kind of the waveform resources
		on the LINE-IN/PWM pins. Allowed values are: "audio", "pwm".
		If this is not specified, internal signals (Vmax or buffer)
		will be selected according to the requriement of the playing
		waveforms.

- vdd-supply
  Usage:      optional
  Value type: <phandle>
@@ -114,7 +101,7 @@ waveforms/effects:
		notification event.

- qcom,wf-pattern
  Usage:      required
  Usage:      optional
  Value type: <prop-encoded-array>
  Definition: Specifies the waveform pattern in a byte array that will be
		played for the effect-id. The bit fields of each byte are:
@@ -172,6 +159,19 @@ waveforms/effects:
  Definition: If specified, the hardware feature of LRA auto resonance detection
		is disabled.

- qcom,wf-line-in-audio
  Usage:      optional
  Value type: <empty>
  Definition: Boolean flag to indicate if the effect is playing the audio signal
		comes into LINE-IN pin. If this is specified, the pattern
		specified in "qcom,wf-pattern" will be ignored.

- qcom,wf-line-in-pwm
  Usage:      optional
  Value type: <empty>
  Definition: Boolean flag to indicate if the effect is playing the PWM signal
		comes into LINE-IN pin. If this is specified, the pattern
		specified in "qcom,wf-pattern" will be ignored.
Example:
  qcom,haptics@c000 {
	compatible = "qcom,haptics";
@@ -200,4 +200,10 @@ Example:
		qcom,wf-play-rate-us = <6250>;
		qcom,wf-pattern = [7e 7e 7e];
	};

	wf_6 {
		/* RINGTONE_x effect */
		qcom,effect-id = <6>;
		qcom,wf-line-in-audio;
	};
  };
+162 −110
Original line number Diff line number Diff line
@@ -179,6 +179,7 @@ struct qti_hap_effect {
	int			brake_pattern_length;
	bool			brake_en;
	bool			lra_auto_res_disable;
	enum wf_src		wf_src;
};

struct qti_hap_play_info {
@@ -193,11 +194,9 @@ struct qti_hap_config {
	enum actutor_type	act_type;
	enum lra_res_sig_shape	lra_shape;
	enum lra_auto_res_mode	lra_auto_res_mode;
	enum wf_src		ext_src;
	u16			vmax_mv;
	u16			play_rate_us;
	bool			lra_allow_variable_play_rate;
	bool			use_ext_wf_src;
};

struct qti_hap_chip {
@@ -205,7 +204,6 @@ struct qti_hap_chip {
	struct device			*dev;
	struct regmap			*regmap;
	struct input_dev		*input_dev;
	struct pwm_device		*pwm_dev;
	struct qti_hap_config		config;
	struct qti_hap_play_info	play;
	struct qti_hap_effect		*predefined;
@@ -228,6 +226,7 @@ struct qti_hap_chip {

static int wf_repeat[8] = {1, 2, 4, 8, 16, 32, 64, 128};
static int wf_s_repeat[4] = {1, 2, 4, 8};
const static char * const wf_src_str[] = {"vmax", "buffer", "audio", "pwm"};

static inline bool is_secure(u8 addr)
{
@@ -398,6 +397,11 @@ static int qti_haptics_config_wf_buffer(struct qti_hap_chip *chip)
	int rc = 0;
	size_t len;

	if (effect->pattern == NULL) {
		dev_dbg(chip->dev, "no pattern for effect %d\n", effect->id);
		return 0;
	}

	if (play->playing_pos == effect->pattern_length) {
		dev_dbg(chip->dev, "pattern playing done\n");
		return 0;
@@ -493,11 +497,8 @@ static int qti_haptics_config_wf_src(struct qti_hap_chip *chip,
	int rc;

	addr = REG_HAP_SEL;
	mask = HAP_WF_SOURCE_MASK | HAP_WF_TRIGGER_BIT;
	mask = HAP_WF_SOURCE_MASK;
	val = src << HAP_WF_SOURCE_SHIFT;
	if (src == EXT_WF_AUDIO || src == EXT_WF_PWM)
		val |= HAP_WF_TRIGGER_BIT;

	rc = qti_haptics_masked_write(chip, addr, mask, val);
	if (rc < 0)
		dev_err(chip->dev, "set HAP_SEL failed, rc=%d\n", rc);
@@ -704,20 +705,22 @@ static int qti_haptics_load_predefined_effect(struct qti_hap_chip *chip,
	if (rc < 0)
		return rc;

	rc = qti_haptics_config_wf_buffer(chip);
	/* Set corresponding WF_SOURCE */
	rc = qti_haptics_config_wf_src(chip, play->effect->wf_src);
	if (rc < 0)
		return rc;

	rc = qti_haptics_config_wf_repeat(chip);
	if (play->effect->wf_src == INT_WF_BUFFER) {
		rc = qti_haptics_config_wf_buffer(chip);
		if (rc < 0)
			return rc;

	/* Set WF_SOURCE to buffer */
	rc = qti_haptics_config_wf_src(chip, INT_WF_BUFFER);
		rc = qti_haptics_config_wf_repeat(chip);
		if (rc < 0)
			return rc;

		play->playing_pattern = true;
	}

	return 0;
}
@@ -812,6 +815,17 @@ static inline void get_play_length(struct qti_hap_play_info *play,
	struct qti_hap_effect *effect = play->effect;
	int tmp;

	/*
	 * Return play_length to 0 if playing LINE-IN signal,
	 * the playing has to be stopped explicitly from the
	 * requester.
	 */
	if (effect->wf_src == EXT_WF_PWM ||
			effect->wf_src == EXT_WF_AUDIO) {
		*length_us = 0;
		return;
	}

	tmp = effect->pattern_length * effect->play_rate_us;
	tmp *= wf_s_repeat[effect->wf_s_repeat_n];
	tmp *= wf_repeat[effect->wf_repeat_n];
@@ -961,12 +975,16 @@ static int qti_haptics_playback(struct input_dev *dev, int effect_id, int val)
				disable_irq_nosync(chip->play_irq);
				chip->play_irq_en = false;
			}

			if (play->length_us != 0) {
				secs = play->length_us / USEC_PER_SEC;
				nsecs = (play->length_us % USEC_PER_SEC) *
					NSEC_PER_USEC;
			hrtimer_start(&chip->stop_timer, ktime_set(secs, nsecs),
				hrtimer_start(&chip->stop_timer,
						ktime_set(secs, nsecs),
						HRTIMER_MODE_REL);
			}
		}
	} else {
		play->length_us = 0;
		rc = qti_haptics_play(chip, false);
@@ -1085,13 +1103,6 @@ static int qti_haptics_hw_init(struct qti_hap_chip *chip)
	if (rc < 0)
		return rc;

	/* Set external waveform source if it's used */
	if (config->use_ext_wf_src) {
		rc = qti_haptics_config_wf_src(chip, config->ext_src);
		if (rc < 0)
			return rc;
	}

	/*
	 * Skip configurations below for ERM actuator
	 * as they're only for LRA actuators
@@ -1206,6 +1217,61 @@ static int qti_haptics_parse_dt_per_effect(struct qti_hap_chip *chip)
			effect->vmax_mv = (tmp > HAP_VMAX_MV_MAX) ?
				HAP_VMAX_MV_MAX : tmp;

		effect->play_rate_us = config->play_rate_us;
		rc = of_property_read_u32(child_node, "qcom,wf-play-rate-us",
				&tmp);
		if (rc < 0)
			dev_dbg(chip->dev, "Read qcom,wf-play-rate-us failed, rc=%d\n",
					rc);
		else
			effect->play_rate_us = tmp;

		if (config->act_type == ACT_LRA &&
				!config->lra_allow_variable_play_rate &&
				config->play_rate_us != effect->play_rate_us) {
			dev_warn(chip->dev, "play rate should match with LRA resonance frequency\n");
			effect->play_rate_us = config->play_rate_us;
		}

		effect->lra_auto_res_disable = of_property_read_bool(child_node,
				"qcom,lra-auto-resonance-disable");

		tmp = of_property_count_elems_of_size(child_node,
				"qcom,wf-brake-pattern", sizeof(u8));
		if (tmp > 0) {
			if (tmp > HAP_BRAKE_PATTERN_MAX) {
				dev_err(chip->dev, "wf-brake-pattern shouldn't be more than %d bytes\n",
						HAP_BRAKE_PATTERN_MAX);
				return -EINVAL;
			}

			rc = of_property_read_u8_array(child_node,
					"qcom,wf-brake-pattern",
					effect->brake, tmp);
			if (rc < 0) {
				dev_err(chip->dev, "Failed to get wf-brake-pattern, rc=%d\n",
						rc);
				return rc;
			}

			effect->brake_pattern_length = tmp;
			verify_brake_setting(effect);
		}

		effect->wf_src = INT_WF_BUFFER;
		if (of_property_read_bool(child_node, "qcom,wf-line-in-pwm"))
			effect->wf_src = EXT_WF_PWM;
		if (of_property_read_bool(child_node, "qcom,wf-line-in-audio"))
			effect->wf_src = EXT_WF_AUDIO;

		/*
		 * Ignore wf-pattern configuration iff it's
		 * supposed to play waveform/signal from LINE-IN
		 * pin
		 */
		if (effect->wf_src != INT_WF_BUFFER)
			continue;

		rc = of_property_count_elems_of_size(child_node,
				"qcom,wf-pattern", sizeof(u8));
		if (rc < 0) {
@@ -1231,22 +1297,6 @@ static int qti_haptics_parse_dt_per_effect(struct qti_hap_chip *chip)
			return rc;
		}

		effect->play_rate_us = config->play_rate_us;
		rc = of_property_read_u32(child_node, "qcom,wf-play-rate-us",
				&tmp);
		if (rc < 0)
			dev_dbg(chip->dev, "Read qcom,wf-play-rate-us failed, rc=%d\n",
					rc);
		else
			effect->play_rate_us = tmp;

		if (config->act_type == ACT_LRA &&
				!config->lra_allow_variable_play_rate &&
				config->play_rate_us != effect->play_rate_us) {
			dev_warn(chip->dev, "play rate should match with LRA resonance frequency\n");
			effect->play_rate_us = config->play_rate_us;
		}

		rc = of_property_read_u32(child_node, "qcom,wf-repeat-count",
				&tmp);
		if (rc < 0) {
@@ -1272,53 +1322,33 @@ static int qti_haptics_parse_dt_per_effect(struct qti_hap_chip *chip)

			effect->wf_s_repeat_n = j;
		}

		effect->lra_auto_res_disable = of_property_read_bool(child_node,
				"qcom,lra-auto-resonance-disable");

		tmp = of_property_count_elems_of_size(child_node,
				"qcom,wf-brake-pattern", sizeof(u8));
		if (tmp <= 0)
			continue;

		if (tmp > HAP_BRAKE_PATTERN_MAX) {
			dev_err(chip->dev, "wf-brake-pattern shouldn't be more than %d bytes\n",
					HAP_BRAKE_PATTERN_MAX);
			return -EINVAL;
		}

		rc = of_property_read_u8_array(child_node,
				"qcom,wf-brake-pattern", effect->brake, tmp);
		if (rc < 0) {
			dev_err(chip->dev, "Failed to get wf-brake-pattern, rc=%d\n",
					rc);
			return rc;
		}

		effect->brake_pattern_length = tmp;
		verify_brake_setting(effect);
	}

	for (j = 0; j < i; j++) {
		dev_dbg(chip->dev, "effect: %d\n", chip->predefined[j].id);
		dev_dbg(chip->dev, "    vmax: %d mv\n",
				chip->predefined[j].vmax_mv);
		dev_dbg(chip->dev, "        play_rate: %d us\n",
				chip->predefined[j].play_rate_us);
		for (m = 0; m < chip->predefined[j].pattern_length; m++)
			dev_dbg(chip->dev, "        pattern[%d]: 0x%x\n",
					m, chip->predefined[j].pattern[m]);
		dev_dbg(chip->dev, "    waveform source: %s\n",
				wf_src_str[chip->predefined[j].wf_src]);
		dev_dbg(chip->dev, "    brake_en: %d\n",
				chip->predefined[j].brake_en);
		for (m = 0; m < chip->predefined[j].brake_pattern_length; m++)
			dev_dbg(chip->dev, "    brake_pattern[%d]: 0x%x\n",
					m, chip->predefined[j].brake[m]);
		dev_dbg(chip->dev, "    brake_en: %d\n",
				chip->predefined[j].brake_en);
		dev_dbg(chip->dev, "    lra_auto_res_disable: %d\n",
				chip->predefined[j].lra_auto_res_disable);
		if (chip->predefined[j].wf_src != INT_WF_BUFFER)
			continue;

		for (m = 0; m < chip->predefined[j].pattern_length; m++)
			dev_dbg(chip->dev, "    pattern[%d]: 0x%x\n",
					m, chip->predefined[j].pattern[m]);
		dev_dbg(chip->dev, "    play_rate: %d us\n",
				chip->predefined[j].play_rate_us);
		dev_dbg(chip->dev, "    wf_repeat_n: %d\n",
				chip->predefined[j].wf_repeat_n);
		dev_dbg(chip->dev, "    wf_s_repeat_n: %d\n",
				chip->predefined[j].wf_s_repeat_n);
		dev_dbg(chip->dev, "    lra_auto_res_disable: %d\n",
				chip->predefined[j].lra_auto_res_disable);
	}

	return 0;
@@ -1422,22 +1452,6 @@ static int qti_haptics_parse_dt(struct qti_hap_chip *chip)
		config->play_rate_us = (tmp >= HAP_PLAY_RATE_US_MAX) ?
			HAP_PLAY_RATE_US_MAX : tmp;

	if (of_find_property(node, "qcom,external-waveform-source", NULL)) {
		if (!of_property_read_string(node,
				"qcom,external-waveform-source", &str)) {
			if (strcmp(str, "audio") == 0) {
				config->ext_src = EXT_WF_AUDIO;
			} else if (strcmp(str, "pwm") == 0) {
				config->ext_src = EXT_WF_PWM;
			} else {
				dev_err(chip->dev, "Invalid external waveform source: %s\n",
						str);
				return -EINVAL;
			}
		}
		config->use_ext_wf_src = true;
	}

	if (of_find_property(node, "vdd-supply", NULL)) {
		chip->vdd_supply = devm_regulator_get(chip->dev, "vdd");
		if (IS_ERR(chip->vdd_supply)) {
@@ -1604,6 +1618,34 @@ static int auto_res_dbgfs_write(void *data, u64 val)
DEFINE_DEBUGFS_ATTRIBUTE(auto_res_debugfs_ops,  auto_res_dbgfs_read,
		auto_res_dbgfs_write, "%llu\n");

#define WF_SRC_BYTES	12
static ssize_t wf_src_dbgfs_read(struct file *filep,
		char __user *buf, size_t count, loff_t *ppos)
{
	struct qti_hap_effect *effect =
		(struct qti_hap_effect *)filep->private_data;
	char kbuf[WF_SRC_BYTES] = {0};
	int rc, length;

	length = snprintf(kbuf, WF_SRC_BYTES, "%s",
			wf_src_str[effect->wf_src]);

	if (length > WF_SRC_BYTES - 2)
		return -EINVAL;

	kbuf[length++] = '\n';
	kbuf[length++] = '\0';

	rc = simple_read_from_buffer(buf, count, ppos, kbuf, length);
	return rc;
}

static const struct file_operations wf_src_dbgfs_ops = {
	.read = wf_src_dbgfs_read,
	.owner = THIS_MODULE,
	.open = simple_open,
};

#define CHAR_PER_PATTERN 8
static ssize_t brake_pattern_dbgfs_read(struct file *filep,
		char __user *buf, size_t count, loff_t *ppos)
@@ -1787,20 +1829,6 @@ static int create_effect_debug_files(struct qti_hap_effect *effect,
		return -ENOMEM;
	}

	file = debugfs_create_file("wf_repeat_n", 0644, dir,
			effect, &wf_repeat_n_debugfs_ops);
	if (!file) {
		pr_err("create wf-repeat debugfs node failed\n");
		return -ENOMEM;
	}

	file = debugfs_create_file("wf_s_repeat_n", 0644, dir,
			effect, &wf_s_repeat_n_debugfs_ops);
	if (!file) {
		pr_err("create wf-s-repeat debugfs node failed\n");
		return -ENOMEM;
	}

	file = debugfs_create_file("lra_auto_res_en", 0644, dir,
			effect, &auto_res_debugfs_ops);
	if (!file) {
@@ -1815,6 +1843,16 @@ static int create_effect_debug_files(struct qti_hap_effect *effect,
		return -ENOMEM;
	}

	file = debugfs_create_file("wf_src", 0444, dir,
			effect, &wf_src_dbgfs_ops);
	if (!file) {
		pr_err("create wf_src debugfs node failed\n");
		return -ENOMEM;
	}

	if (effect->wf_src == EXT_WF_AUDIO || effect->wf_src == EXT_WF_PWM)
		return 0;

	file = debugfs_create_file("pattern", 0644, dir,
			effect, &pattern_dbgfs_ops);
	if (!file) {
@@ -1822,6 +1860,20 @@ static int create_effect_debug_files(struct qti_hap_effect *effect,
		return -ENOMEM;
	}

	file = debugfs_create_file("wf_repeat_n", 0644, dir,
			effect, &wf_repeat_n_debugfs_ops);
	if (!file) {
		pr_err("create wf_repeat debugfs node failed\n");
		return -ENOMEM;
	}

	file = debugfs_create_file("wf_s_repeat_n", 0644, dir,
			effect, &wf_s_repeat_n_debugfs_ops);
	if (!file) {
		pr_err("create wf_s_repeat debugfs node failed\n");
		return -ENOMEM;
	}

	return 0;
}