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

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

Merge "pwm: pwm-qti-lpg: Add support for LUT pattern control through SDAM"

parents 1a6791ea b07c05ff
Loading
Loading
Loading
Loading
+418 −26
Original line number Original line Diff line number Diff line
@@ -12,10 +12,12 @@
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/mutex.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/pwm.h>
#include <linux/qpnp/qpnp-pbs.h>
#include <linux/regmap.h>
#include <linux/regmap.h>
#include <linux/seq_file.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/slab.h>
@@ -99,6 +101,34 @@
#define LPG_LUT_VALUE_MSB_MASK		BIT(0)
#define LPG_LUT_VALUE_MSB_MASK		BIT(0)
#define LPG_LUT_COUNT_MAX		47
#define LPG_LUT_COUNT_MAX		47


/* LPG config settings in SDAM */
#define SDAM_REG_PBS_SEQ_EN			0x42
#define PBS_SW_TRG_BIT				BIT(0)

#define SDAM_REG_RAMP_STEP_DURATION		0x47

#define SDAM_LUT_EN_OFFSET			0x0
#define SDAM_PATTERN_CONFIG_OFFSET		0x1
#define SDAM_END_INDEX_OFFSET			0x3
#define SDAM_START_INDEX_OFFSET			0x4
#define SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET	0x6

/* SDAM_REG_LUT_EN */
#define SDAM_LUT_EN_BIT				BIT(0)

/* SDAM_REG_PATTERN_CONFIG */
#define SDAM_PATTERN_LOOP_ENABLE		BIT(3)
#define SDAM_PATTERN_RAMP_TOGGLE		BIT(2)
#define SDAM_PATTERN_EN_PAUSE_END		BIT(1)
#define SDAM_PATTERN_EN_PAUSE_START		BIT(0)

/* SDAM_REG_PAUSE_MULTIPLIER */
#define SDAM_PAUSE_START_SHIFT			4
#define SDAM_PAUSE_START_MASK			GENMASK(7, 4)
#define SDAM_PAUSE_END_MASK			GENMASK(3, 0)

#define SDAM_LUT_COUNT_MAX			64

enum lpg_src {
enum lpg_src {
	LUT_PATTERN = 0,
	LUT_PATTERN = 0,
	PWM_VALUE,
	PWM_VALUE,
@@ -145,6 +175,7 @@ struct qpnp_lpg_channel {
	u32				lpg_idx;
	u32				lpg_idx;
	u32				reg_base;
	u32				reg_base;
	u32				max_pattern_length;
	u32				max_pattern_length;
	u32				lpg_sdam_base;
	u8				src_sel;
	u8				src_sel;
	u8				subtype;
	u8				subtype;
	bool				lut_written;
	bool				lut_written;
@@ -160,7 +191,11 @@ struct qpnp_lpg_chip {
	struct qpnp_lpg_lut	*lut;
	struct qpnp_lpg_lut	*lut;
	struct mutex		bus_lock;
	struct mutex		bus_lock;
	u32			*lpg_group;
	u32			*lpg_group;
	struct nvmem_device	*sdam_nvmem;
	struct device_node	*pbs_dev_node;
	u32			num_lpgs;
	u32			num_lpgs;
	unsigned long		pbs_en_bitmap;
	bool			use_sdam;
};
};


static int qpnp_lpg_read(struct qpnp_lpg_channel *lpg, u16 addr, u8 *val)
static int qpnp_lpg_read(struct qpnp_lpg_channel *lpg, u16 addr, u8 *val)
@@ -187,7 +222,7 @@ static int qpnp_lpg_write(struct qpnp_lpg_channel *lpg, u16 addr, u8 val)
	mutex_lock(&lpg->chip->bus_lock);
	mutex_lock(&lpg->chip->bus_lock);
	rc = regmap_write(lpg->chip->regmap, lpg->reg_base + addr, val);
	rc = regmap_write(lpg->chip->regmap, lpg->reg_base + addr, val);
	if (rc < 0)
	if (rc < 0)
		dev_err(lpg->chip->dev, "Write addr 0x%x with value %d failed, rc=%d\n",
		dev_err(lpg->chip->dev, "Write addr 0x%x with value 0x%x failed, rc=%d\n",
				lpg->reg_base + addr, val, rc);
				lpg->reg_base + addr, val, rc);
	mutex_unlock(&lpg->chip->bus_lock);
	mutex_unlock(&lpg->chip->bus_lock);


@@ -240,6 +275,90 @@ static int qpnp_lut_masked_write(struct qpnp_lpg_lut *lut,
	return rc;
	return rc;
}
}


static int qpnp_sdam_write(struct qpnp_lpg_chip *chip, u16 addr, u8 val)
{
	int rc;

	mutex_lock(&chip->bus_lock);
	rc = nvmem_device_write(chip->sdam_nvmem, addr, 1, &val);
	if (rc < 0)
		dev_err(chip->dev, "write SDAM add 0x%x failed, rc=%d\n",
				addr, rc);

	mutex_unlock(&chip->bus_lock);

	return rc > 0 ? 0 : rc;
}

static int qpnp_lpg_sdam_write(struct qpnp_lpg_channel *lpg, u16 addr, u8 val)
{
	struct qpnp_lpg_chip *chip = lpg->chip;
	int rc;

	mutex_lock(&chip->bus_lock);
	rc = nvmem_device_write(chip->sdam_nvmem,
			lpg->lpg_sdam_base + addr, 1, &val);
	if (rc < 0)
		dev_err(chip->dev, "write SDAM add 0x%x failed, rc=%d\n",
				lpg->lpg_sdam_base + addr, rc);

	mutex_unlock(&chip->bus_lock);

	return rc > 0 ? 0 : rc;
}

static int qpnp_lpg_sdam_masked_write(struct qpnp_lpg_channel *lpg,
					u16 addr, u8 mask, u8 val)
{
	int rc;
	u8 tmp;
	struct qpnp_lpg_chip *chip = lpg->chip;

	mutex_lock(&chip->bus_lock);

	rc = nvmem_device_read(chip->sdam_nvmem,
			lpg->lpg_sdam_base + addr, 1, &tmp);
	if (rc < 0) {
		dev_err(chip->dev, "Read SDAM addr %d failed, rc=%d\n",
				lpg->lpg_sdam_base + addr, rc);
		goto unlock;
	}

	tmp = tmp & ~mask;
	tmp |= val & mask;
	rc = nvmem_device_write(chip->sdam_nvmem,
			lpg->lpg_sdam_base + addr, 1, &tmp);
	if (rc < 0)
		dev_err(chip->dev, "write SDAM addr %d failed, rc=%d\n",
				lpg->lpg_sdam_base + addr, rc);

unlock:
	mutex_unlock(&chip->bus_lock);

	return rc > 0 ? 0 : rc;
}

static int qpnp_lut_sdam_write(struct qpnp_lpg_lut *lut,
		u16 addr, u8 *val, size_t length)
{
	struct qpnp_lpg_chip *chip = lut->chip;
	int rc;

	if (addr >= SDAM_LUT_COUNT_MAX)
		return -EINVAL;

	mutex_lock(&chip->bus_lock);
	rc = nvmem_device_write(chip->sdam_nvmem,
			lut->reg_base + addr, length, val);
	if (rc < 0)
		dev_err(chip->dev, "write SDAM addr %d failed, rc=%d\n",
				lut->reg_base + addr, rc);

	mutex_unlock(&chip->bus_lock);

	return rc > 0 ? 0 : rc;
}

static struct qpnp_lpg_channel *pwm_dev_to_qpnp_lpg(struct pwm_chip *pwm_chip,
static struct qpnp_lpg_channel *pwm_dev_to_qpnp_lpg(struct pwm_chip *pwm_chip,
				struct pwm_device *pwm)
				struct pwm_device *pwm)
{
{
@@ -361,14 +480,111 @@ static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg)
	return rc;
	return rc;
}
}


static int qpnp_lpg_set_sdam_lut_pattern(struct qpnp_lpg_channel *lpg,
		unsigned int *pattern, unsigned int length)
{
	struct qpnp_lpg_lut *lut = lpg->chip->lut;
	int i, rc = 0;
	u8 val[SDAM_LUT_COUNT_MAX + 1], addr;

	if (length > lpg->max_pattern_length) {
		dev_err(lpg->chip->dev, "new pattern length (%d) larger than predefined (%d)\n",
				length, lpg->max_pattern_length);
		return -EINVAL;
	}

	/* Program LUT pattern */
	mutex_lock(&lut->lock);
	addr = lpg->ramp_config.lo_idx;
	for (i = 0; i < length; i++)
		val[i] = pattern[i] * 255 / 100;

	rc = qpnp_lut_sdam_write(lut, addr, val, length);
	if (rc < 0) {
		dev_err(lpg->chip->dev, "Write pattern in SDAM failed, rc=%d\n",
				rc);
		goto unlock;
	}

	lpg->ramp_config.pattern_length = length;
unlock:
	mutex_unlock(&lut->lock);

	return rc;
}

static int qpnp_lpg_set_sdam_ramp_config(struct qpnp_lpg_channel *lpg)
{
	struct lpg_ramp_config *ramp = &lpg->ramp_config;
	u8 addr, mask, val;
	int rc = 0;

	/* clear PBS scatchpad register */
	val = 0;
	rc = qpnp_lpg_sdam_write(lpg,
			SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET, val);
	if (rc < 0) {
		dev_err(lpg->chip->dev, "Write SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET failed, rc=%d\n",
				rc);
		return rc;
	}

	/* Set ramp step duration, one WAIT_TICK is 7.8ms */
	val = (ramp->step_ms * 1000 / 7800) & 0xff;
	if (val > 0)
		val--;
	addr = SDAM_REG_RAMP_STEP_DURATION;
	rc = qpnp_sdam_write(lpg->chip, addr, val);
	if (rc < 0) {
		dev_err(lpg->chip->dev, "Write SDAM_REG_RAMP_STEP_DURATION failed, rc=%d\n",
				rc);
		return rc;
	}

	/* Set hi_idx and lo_idx */
	rc = qpnp_lpg_sdam_write(lpg, SDAM_END_INDEX_OFFSET, ramp->hi_idx);
	if (rc < 0) {
		dev_err(lpg->chip->dev, "Write SDAM_REG_END_INDEX failed, rc=%d\n",
					rc);
		return rc;
	}

	rc = qpnp_lpg_sdam_write(lpg, SDAM_START_INDEX_OFFSET,
						ramp->lo_idx);
	if (rc < 0) {
		dev_err(lpg->chip->dev, "Write SDAM_REG_START_INDEX failed, rc=%d\n",
					rc);
		return rc;
	}

	/* Set LPG_PATTERN_CONFIG */
	addr = SDAM_PATTERN_CONFIG_OFFSET;
	mask = SDAM_PATTERN_LOOP_ENABLE;
	val = 0;
	if (ramp->pattern_repeat)
		val |= SDAM_PATTERN_LOOP_ENABLE;

	rc = qpnp_lpg_sdam_masked_write(lpg, addr, mask, val);
	if (rc < 0) {
		dev_err(lpg->chip->dev, "Write SDAM_REG_PATTERN_CONFIG failed, rc=%d\n",
					rc);
		return rc;
	}

	return rc;
}

static int qpnp_lpg_set_lut_pattern(struct qpnp_lpg_channel *lpg,
static int qpnp_lpg_set_lut_pattern(struct qpnp_lpg_channel *lpg,
		unsigned int *pattern, unsigned int length)
		unsigned int *pattern, unsigned int length)
{
{
	struct qpnp_lpg_lut *lut = lpg->chip->lut;
	struct qpnp_lpg_lut *lut = lpg->chip->lut;
	u16 full_duty_value, pwm_values[SDAM_LUT_COUNT_MAX + 1] = {0};
	int i, rc = 0;
	int i, rc = 0;
	u16 full_duty_value, pwm_values[LPG_LUT_COUNT_MAX + 1] = {0};
	u8 lsb, msb, addr;
	u8 lsb, msb, addr;


	if (lpg->chip->use_sdam)
		return qpnp_lpg_set_sdam_lut_pattern(lpg, pattern, length);

	if (length > lpg->max_pattern_length) {
	if (length > lpg->max_pattern_length) {
		dev_err(lpg->chip->dev, "new pattern length (%d) larger than predefined (%d)\n",
		dev_err(lpg->chip->dev, "new pattern length (%d) larger than predefined (%d)\n",
				length, lpg->max_pattern_length);
				length, lpg->max_pattern_length);
@@ -422,6 +638,9 @@ static int qpnp_lpg_set_ramp_config(struct qpnp_lpg_channel *lpg)
	u8 lsb, msb, addr, mask, val;
	u8 lsb, msb, addr, mask, val;
	int rc = 0;
	int rc = 0;


	if (lpg->chip->use_sdam)
		return qpnp_lpg_set_sdam_ramp_config(lpg);

	/* Set ramp step duration */
	/* Set ramp step duration */
	lsb = ramp->step_ms & 0xff;
	lsb = ramp->step_ms & 0xff;
	msb = ramp->step_ms >> 8;
	msb = ramp->step_ms >> 8;
@@ -503,6 +722,8 @@ static int qpnp_lpg_set_ramp_config(struct qpnp_lpg_channel *lpg)
static void __qpnp_lpg_calc_pwm_period(u64 period_ns,
static void __qpnp_lpg_calc_pwm_period(u64 period_ns,
			struct lpg_pwm_config *pwm_config)
			struct lpg_pwm_config *pwm_config)
{
{
	struct qpnp_lpg_channel *lpg = container_of(pwm_config,
			struct qpnp_lpg_channel, pwm_config);
	struct lpg_pwm_config configs[NUM_PWM_SIZE];
	struct lpg_pwm_config configs[NUM_PWM_SIZE];
	int i, j, m, n;
	int i, j, m, n;
	u64 tmp1, tmp2;
	u64 tmp1, tmp2;
@@ -518,7 +739,12 @@ static void __qpnp_lpg_calc_pwm_period(u64 period_ns,
	 *
	 *
	 * Searching the closest settings for the requested PWM period.
	 * Searching the closest settings for the requested PWM period.
	 */
	 */
	for (n = 0; n < ARRAY_SIZE(pwm_size); n++) {
	if (lpg->chip->use_sdam)
		/* SDAM pattern control can only use 9 bit resolution */
		n = 1;
	else
		n = 0;
	for (; n < ARRAY_SIZE(pwm_size); n++) {
		pwm_clk_period_ns = period_ns >> pwm_size[n];
		pwm_clk_period_ns = period_ns >> pwm_size[n];
		for (i = ARRAY_SIZE(clk_freq_hz) - 1; i >= 0; i--) {
		for (i = ARRAY_SIZE(clk_freq_hz) - 1; i >= 0; i--) {
			for (j = 0; j < ARRAY_SIZE(clk_prediv); j++) {
			for (j = 0; j < ARRAY_SIZE(clk_prediv); j++) {
@@ -666,6 +892,45 @@ static int qpnp_lpg_pwm_config_extend(struct pwm_chip *pwm_chip,
	}
	}


	return qpnp_lpg_config(lpg, duty_ns, period_ns);
	return qpnp_lpg_config(lpg, duty_ns, period_ns);
};

static int qpnp_lpg_pbs_trigger_enable(struct qpnp_lpg_channel *lpg, bool en)
{
	struct qpnp_lpg_chip *chip = lpg->chip;
	int rc = 0;

	if (en) {
		if (chip->pbs_en_bitmap == 0) {
			rc = qpnp_sdam_write(chip, SDAM_REG_PBS_SEQ_EN,
					PBS_SW_TRG_BIT);
			if (rc < 0) {
				dev_err(chip->dev, "Write SDAM_REG_PBS_SEQ_EN failed, rc=%d\n",
						rc);
				return rc;
			}

			rc = qpnp_pbs_trigger_event(chip->pbs_dev_node,
					PBS_SW_TRG_BIT);
			if (rc < 0) {
				dev_err(chip->dev, "Failed to trigger PBS, rc=%d\n",
						rc);
				return rc;
			}
		}
		set_bit(lpg->lpg_idx, &chip->pbs_en_bitmap);
	} else {
		clear_bit(lpg->lpg_idx, &chip->pbs_en_bitmap);
		if (chip->pbs_en_bitmap == 0) {
			rc = qpnp_sdam_write(chip, SDAM_REG_PBS_SEQ_EN, 0);
			if (rc < 0) {
				dev_err(chip->dev, "Write SDAM_REG_PBS_SEQ_EN failed, rc=%d\n",
						rc);
				return rc;
			}
		}
	}

	return rc;
}
}


static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en)
static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en)
@@ -680,7 +945,7 @@ static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en)
					LPG_EN_RAMP_GEN_MASK;
					LPG_EN_RAMP_GEN_MASK;
	val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
	val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;


	if (lpg->src_sel == LUT_PATTERN)
	if (lpg->src_sel == LUT_PATTERN && !chip->use_sdam)
		val |= 1 << LPG_EN_RAMP_GEN_SHIFT;
		val |= 1 << LPG_EN_RAMP_GEN_SHIFT;


	if (en)
	if (en)
@@ -693,6 +958,27 @@ static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en)
		return rc;
		return rc;
	}
	}


	if (chip->use_sdam) {
		if (lpg->src_sel == LUT_PATTERN && en) {
			val = SDAM_LUT_EN_BIT;
			en = true;
		} else {
			val = 0;
			en = false;
		}

		rc = qpnp_lpg_sdam_write(lpg, SDAM_LUT_EN_OFFSET, val);
		if (rc < 0) {
			dev_err(chip->dev, "Write SDAM_REG_LUT_EN failed, rc=%d\n",
					rc);
			return rc;
		}

		qpnp_lpg_pbs_trigger_enable(lpg, en);

		return rc;
	}

	if (lpg->src_sel == LUT_PATTERN && en) {
	if (lpg->src_sel == LUT_PATTERN && en) {
		val = 1 << lpg->lpg_idx;
		val = 1 << lpg->lpg_idx;
		for (i = 0; i < chip->num_lpgs; i++) {
		for (i = 0; i < chip->num_lpgs; i++) {
@@ -735,6 +1021,7 @@ static int qpnp_lpg_pwm_set_output_type(struct pwm_chip *pwm_chip,
	struct qpnp_lpg_channel *lpg;
	struct qpnp_lpg_channel *lpg;
	enum lpg_src src_sel;
	enum lpg_src src_sel;
	int rc;
	int rc;
	bool is_enabled;


	lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
	lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
	if (lpg == NULL) {
	if (lpg == NULL) {
@@ -752,6 +1039,23 @@ static int qpnp_lpg_pwm_set_output_type(struct pwm_chip *pwm_chip,
	if (src_sel == lpg->src_sel)
	if (src_sel == lpg->src_sel)
		return 0;
		return 0;


	is_enabled = pwm_is_enabled(pwm);
	if (is_enabled) {
		/*
		 * Disable the channel first then enable it later to make
		 * sure the output type is changed successfully. This is
		 * especially useful in SDAM use case to stop the PBS
		 * sequence when changing the PWM output type from
		 * MODULATED to FIXED.
		 */
		rc = qpnp_lpg_pwm_src_enable(lpg, false);
		if (rc < 0) {
			dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
					lpg->lpg_idx, rc);
			return rc;
		}
	}

	if (src_sel == LUT_PATTERN) {
	if (src_sel == LUT_PATTERN) {
		/* program LUT if it's never been programmed */
		/* program LUT if it's never been programmed */
		if (!lpg->lut_written) {
		if (!lpg->lut_written) {
@@ -776,7 +1080,14 @@ static int qpnp_lpg_pwm_set_output_type(struct pwm_chip *pwm_chip,


	lpg->src_sel = src_sel;
	lpg->src_sel = src_sel;


	if (pwm_is_enabled(pwm)) {
	if (is_enabled) {
		rc = qpnp_lpg_set_pwm_config(lpg);
		if (rc < 0) {
			dev_err(pwm_chip->dev, "Config PWM failed for channel %d, rc=%d\n",
							lpg->lpg_idx, rc);
			return rc;
		}

		rc = qpnp_lpg_pwm_src_enable(lpg, true);
		rc = qpnp_lpg_pwm_src_enable(lpg, true);
		if (rc < 0) {
		if (rc < 0) {
			dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
			dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
@@ -1056,7 +1367,7 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
	struct qpnp_lpg_channel *lpg;
	struct qpnp_lpg_channel *lpg;
	struct lpg_ramp_config *ramp;
	struct lpg_ramp_config *ramp;
	int rc = 0, i;
	int rc = 0, i;
	u32 base, length, lpg_chan_id, tmp;
	u32 base, length, lpg_chan_id, tmp, max_count;
	const __be32 *addr;
	const __be32 *addr;


	rc = qpnp_get_lpg_channels(chip, &base);
	rc = qpnp_get_lpg_channels(chip, &base);
@@ -1081,18 +1392,47 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
		}
		}
	}
	}


	chip->lut = devm_kmalloc(chip->dev, sizeof(*chip->lut), GFP_KERNEL);
	if (!chip->lut)
		return -ENOMEM;

	chip->sdam_nvmem = devm_nvmem_device_get(chip->dev, "ppg_sdam");
	if (IS_ERR_OR_NULL(chip->sdam_nvmem)) {
		if (PTR_ERR(chip->sdam_nvmem) == -EPROBE_DEFER)
			return -EPROBE_DEFER;

		addr = of_get_address(chip->dev->of_node, 1, NULL, NULL);
		addr = of_get_address(chip->dev->of_node, 1, NULL, NULL);
		if (!addr) {
		if (!addr) {
			pr_debug("NO LUT address assigned\n");
			pr_debug("NO LUT address assigned\n");
			devm_kfree(chip->dev, chip->lut);
			chip->lut = NULL;
			return 0;
			return 0;
		}
		}


	chip->lut = devm_kmalloc(chip->dev, sizeof(*chip->lut), GFP_KERNEL);
		chip->lut->reg_base = be32_to_cpu(*addr);
	if (!chip->lut)
		max_count = LPG_LUT_COUNT_MAX;
		return -ENOMEM;
	} else {
		chip->use_sdam = true;
		chip->pbs_dev_node = of_parse_phandle(chip->dev->of_node,
				"qcom,pbs-client", 0);
		if (!chip->pbs_dev_node) {
			dev_err(chip->dev, "Missing qcom,pbs-client property\n");
			return -EINVAL;
		}

		rc = of_property_read_u32(chip->dev->of_node,
				"qcom,lut-sdam-base",
				&chip->lut->reg_base);
		if (rc < 0) {
			dev_err(chip->dev, "Read qcom,lut-sdam-base failed, rc=%d\n",
					rc);
			return rc;
		}

		max_count = SDAM_LUT_COUNT_MAX;
	}


	chip->lut->chip = chip;
	chip->lut->chip = chip;
	chip->lut->reg_base = be32_to_cpu(*addr);
	mutex_init(&chip->lut->lock);
	mutex_init(&chip->lut->lock);


	rc = of_property_count_elems_of_size(chip->dev->of_node,
	rc = of_property_count_elems_of_size(chip->dev->of_node,
@@ -1104,13 +1444,13 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
	}
	}


	length = rc;
	length = rc;
	if (length > LPG_LUT_COUNT_MAX) {
	if (length > max_count) {
		dev_err(chip->dev, "qcom,lut-patterns length %d exceed max %d\n",
		dev_err(chip->dev, "qcom,lut-patterns length %d exceed max %d\n",
				length, LPG_LUT_COUNT_MAX);
				length, max_count);
		return -EINVAL;
		return -EINVAL;
	}
	}


	chip->lut->pattern = devm_kcalloc(chip->dev, LPG_LUT_COUNT_MAX,
	chip->lut->pattern = devm_kcalloc(chip->dev, max_count,
			sizeof(*chip->lut->pattern), GFP_KERNEL);
			sizeof(*chip->lut->pattern), GFP_KERNEL);
	if (!chip->lut->pattern)
	if (!chip->lut->pattern)
		return -ENOMEM;
		return -ENOMEM;
@@ -1137,12 +1477,24 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
			return rc;
			return rc;
		}
		}


		if (lpg_chan_id > chip->num_lpgs) {
		if (lpg_chan_id < 1 || lpg_chan_id > chip->num_lpgs) {
			dev_err(chip->dev, "lpg-chann-id %d is out of range 1~%d\n",
			dev_err(chip->dev, "lpg-chann-id %d is out of range 1~%d\n",
					lpg_chan_id, chip->num_lpgs);
					lpg_chan_id, chip->num_lpgs);
			return -EINVAL;
			return -EINVAL;
		}
		}


		if (chip->use_sdam) {
			rc = of_property_read_u32(child,
					"qcom,lpg-sdam-base",
					&tmp);
			if (rc < 0) {
				dev_err(chip->dev, "get qcom,lpg-sdam-base failed for lpg%d, rc=%d\n",
						lpg_chan_id, rc);
				return rc;
			}
			chip->lpgs[lpg_chan_id - 1].lpg_sdam_base = tmp;
		}

		/* lpg channel id is indexed from 1 in hardware */
		/* lpg channel id is indexed from 1 in hardware */
		lpg = &chip->lpgs[lpg_chan_id - 1];
		lpg = &chip->lpgs[lpg_chan_id - 1];
		ramp = &lpg->ramp_config;
		ramp = &lpg->ramp_config;
@@ -1162,9 +1514,9 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
			return rc;
			return rc;
		}
		}
		ramp->lo_idx = (u8)tmp;
		ramp->lo_idx = (u8)tmp;
		if (ramp->lo_idx >= LPG_LUT_COUNT_MAX) {
		if (ramp->lo_idx >= max_count) {
			dev_err(chip->dev, "qcom,ramp-low-index should less than max %d\n",
			dev_err(chip->dev, "qcom,ramp-low-index should less than max %d\n",
					LPG_LUT_COUNT_MAX);
						max_count);
			return -EINVAL;
			return -EINVAL;
		}
		}


@@ -1176,14 +1528,14 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
		}
		}
		ramp->hi_idx = (u8)tmp;
		ramp->hi_idx = (u8)tmp;


		if (ramp->hi_idx > LPG_LUT_COUNT_MAX) {
		if (ramp->hi_idx > max_count) {
			dev_err(chip->dev, "qcom,ramp-high-index shouldn't exceed max %d\n",
			dev_err(chip->dev, "qcom,ramp-high-index shouldn't exceed max %d\n",
						LPG_LUT_COUNT_MAX);
						max_count);
			return -EINVAL;
			return -EINVAL;
		}
		}


		if (ramp->hi_idx <= ramp->lo_idx) {
		if (chip->use_sdam && ramp->hi_idx <= ramp->lo_idx) {
			dev_err(chip->dev, "high-index(%d) should be larger than low-index(%d)\n",
			dev_err(chip->dev, "high-index(%d) should be larger than low-index(%d) when SDAM used\n",
						ramp->hi_idx, ramp->lo_idx);
						ramp->hi_idx, ramp->lo_idx);
			return -EINVAL;
			return -EINVAL;
		}
		}
@@ -1192,6 +1544,12 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
		ramp->pattern = &chip->lut->pattern[ramp->lo_idx];
		ramp->pattern = &chip->lut->pattern[ramp->lo_idx];
		lpg->max_pattern_length = ramp->pattern_length;
		lpg->max_pattern_length = ramp->pattern_length;


		ramp->pattern_repeat = of_property_read_bool(child,
				"qcom,ramp-pattern-repeat");

		if (chip->use_sdam)
			continue;

		rc = of_property_read_u32(child,
		rc = of_property_read_u32(child,
				"qcom,ramp-pause-hi-count", &tmp);
				"qcom,ramp-pause-hi-count", &tmp);
		if (rc < 0)
		if (rc < 0)
@@ -1209,9 +1567,6 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
		ramp->ramp_dir_low_to_hi = of_property_read_bool(child,
		ramp->ramp_dir_low_to_hi = of_property_read_bool(child,
				"qcom,ramp-from-low-to-high");
				"qcom,ramp-from-low-to-high");


		ramp->pattern_repeat = of_property_read_bool(child,
				"qcom,ramp-pattern-repeat");

		ramp->toggle =  of_property_read_bool(child,
		ramp->toggle =  of_property_read_bool(child,
				"qcom,ramp-toggle");
				"qcom,ramp-toggle");
	}
	}
@@ -1266,6 +1621,36 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
	return 0;
	return 0;
}
}


static int qpnp_lpg_sdam_hw_init(struct qpnp_lpg_chip *chip)
{
	struct qpnp_lpg_channel *lpg;
	int i, rc = 0;

	if (!chip->use_sdam)
		return 0;

	for (i = 0; i < chip->num_lpgs; i++) {
		lpg = &chip->lpgs[i];
		if (lpg->lpg_sdam_base != 0) {
			rc = qpnp_lpg_sdam_write(lpg, SDAM_LUT_EN_OFFSET, 0);
			if (rc < 0) {
				dev_err(chip->dev, "Write SDAM_REG_LUT_EN failed, rc=%d\n",
						rc);
				return rc;
			}
			rc = qpnp_lpg_sdam_write(lpg,
					SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET, 0);
			if (rc < 0) {
				dev_err(lpg->chip->dev, "Write SDAM_REG_PBS_SCRATCH_LUT_COUNTER failed, rc=%d\n",
						rc);
				return rc;
			}
		}
	}

	return rc;
}

static int qpnp_lpg_probe(struct platform_device *pdev)
static int qpnp_lpg_probe(struct platform_device *pdev)
{
{
	int rc;
	int rc;
@@ -1290,6 +1675,13 @@ static int qpnp_lpg_probe(struct platform_device *pdev)
		goto err_out;
		goto err_out;
	}
	}


	rc = qpnp_lpg_sdam_hw_init(chip);
	if (rc < 0) {
		dev_err(chip->dev, "SDAM HW init failed, rc=%d\n",
				rc);
		goto err_out;
	}

	dev_set_drvdata(chip->dev, chip);
	dev_set_drvdata(chip->dev, chip);
	chip->pwm_chip.dev = chip->dev;
	chip->pwm_chip.dev = chip->dev;
	chip->pwm_chip.base = -1;
	chip->pwm_chip.base = -1;