pwm: pwm-qti-lpg: add qpnp_lpg_pwm_{set_output_type,get_output_types_supported}

'Commit 106a4b85e0
("BACKPORT: FROMLIST: pwm: Add support for different PWM output types")'
cannot be carried forward. Hence to support breath functionality
in leds-qti-tri-led driver, add the following APIs.

- qpnp_lpg_pwm_set_output_type
- qpnp_lpg_pwm_get_output_types_supported

This is needed to switch PWM output type to PWM (fixed) or LPG/PPG
(modulated).

Change-Id: I932dd03dea35377727fb28bafa87cfbcf7e95c97
Signed-off-by: Subbaraman Narayanamurthy <subbaram@codeaurora.org>
This commit is contained in:
Subbaraman Narayanamurthy 2021-09-22 16:14:19 -07:00 committed by David Collins
parent ae52ee9ba4
commit 0963865b68
2 changed files with 88 additions and 37 deletions

View File

@ -18,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/qpnp/qpnp-pbs.h>
#include <linux/qpnp/qti-pwm.h>
#include <linux/regmap.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
@ -186,6 +187,7 @@ struct qpnp_lpg_channel {
struct qpnp_lpg_chip *chip;
struct lpg_pwm_config pwm_config;
struct lpg_ramp_config ramp_config;
enum pwm_output_type output_type;
u32 lpg_idx;
u32 reg_base;
u32 max_pattern_length;
@ -1213,7 +1215,7 @@ static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en)
break;
lpg_idx = chip->lpg_group[i] - 1;
pwm = &chip->pwm_chip.pwms[lpg_idx];
if ((pwm_get_output_type(pwm) == PWM_OUTPUT_MODULATED)
if ((lpg->output_type == PWM_OUTPUT_MODULATED)
&& pwm_is_enabled(pwm)) {
rc = qpnp_lpg_masked_write(&chip->lpgs[lpg_idx],
REG_LPG_ENABLE_CONTROL,
@ -1240,17 +1242,22 @@ static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en)
return rc;
}
static int qpnp_lpg_pwm_set_output_type(struct pwm_chip *pwm_chip,
struct pwm_device *pwm, enum pwm_output_type output_type)
int qpnp_lpg_pwm_set_output_type(struct pwm_device *pwm,
enum pwm_output_type output_type)
{
struct qpnp_lpg_channel *lpg;
enum lpg_src src_sel;
int rc;
bool is_enabled;
lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
if (!pwm) {
pr_err("pwm cannot be NULL\n");
return -ENODEV;
}
lpg = pwm_dev_to_qpnp_lpg(pwm->chip, pwm);
if (lpg == NULL) {
dev_err(pwm_chip->dev, "lpg not found\n");
dev_err(pwm->chip->dev, "lpg not found\n");
return -ENODEV;
}
@ -1259,6 +1266,9 @@ static int qpnp_lpg_pwm_set_output_type(struct pwm_chip *pwm_chip,
return 0;
}
if (output_type == lpg->output_type)
return 0;
src_sel = (output_type == PWM_OUTPUT_MODULATED) ?
LUT_PATTERN : PWM_VALUE;
if (src_sel == lpg->src_sel)
@ -1275,7 +1285,7 @@ static int qpnp_lpg_pwm_set_output_type(struct pwm_chip *pwm_chip,
*/
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",
dev_err(pwm->chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
lpg->lpg_idx, rc);
return rc;
}
@ -1297,7 +1307,7 @@ static int qpnp_lpg_pwm_set_output_type(struct pwm_chip *pwm_chip,
rc = qpnp_lpg_set_ramp_config(lpg);
if (rc < 0) {
dev_err(pwm_chip->dev, "Config LPG%d ramping failed, rc=%d\n",
dev_err(pwm->chip->dev, "Config LPG%d ramping failed, rc=%d\n",
lpg->lpg_idx, rc);
return rc;
}
@ -1308,21 +1318,47 @@ static int qpnp_lpg_pwm_set_output_type(struct pwm_chip *pwm_chip,
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",
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);
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",
lpg->lpg_idx, rc);
return rc;
}
}
lpg->output_type = output_type;
return 0;
}
EXPORT_SYMBOL(qpnp_lpg_pwm_set_output_type);
int qpnp_lpg_pwm_get_output_types_supported(struct pwm_device *pwm)
{
enum pwm_output_type type = PWM_OUTPUT_FIXED;
struct qpnp_lpg_channel *lpg;
if (!pwm) {
pr_err("pwm cannot be NULL\n");
return -ENODEV;
}
lpg = pwm_dev_to_qpnp_lpg(pwm->chip, pwm);
if (lpg == NULL) {
dev_err(pwm->chip->dev, "lpg not found\n");
return -ENODEV;
}
if (lpg->chip->lut != NULL)
type |= PWM_OUTPUT_MODULATED;
return type;
}
EXPORT_SYMBOL(qpnp_lpg_pwm_get_output_types_supported);
static int qpnp_lpg_pwm_enable(struct pwm_chip *pwm_chip,
struct pwm_device *pwm)
@ -1389,38 +1425,11 @@ static void qpnp_lpg_pwm_disable(struct pwm_chip *pwm_chip,
rc);
}
static int qpnp_lpg_pwm_output_types_supported(struct pwm_chip *pwm_chip,
struct pwm_device *pwm)
{
enum pwm_output_type type = PWM_OUTPUT_FIXED;
struct qpnp_lpg_channel *lpg;
lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
if (lpg == NULL) {
dev_err(pwm_chip->dev, "lpg not found\n");
return type;
}
if (lpg->chip->lut != NULL)
type |= PWM_OUTPUT_MODULATED;
return type;
}
static int qpnp_lpg_pwm_apply(struct pwm_chip *pwm_chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
int rc;
if (state->output_type != pwm->state.output_type) {
rc = qpnp_lpg_pwm_set_output_type(pwm->chip, pwm,
state->output_type);
if (rc < 0)
return rc;
pwm->state.output_type = state->output_type;
}
if (state->period != pwm->state.period ||
state->duty_cycle != pwm->state.duty_cycle) {
rc = qpnp_lpg_pwm_config(pwm->chip, pwm,
@ -1449,7 +1458,6 @@ static int qpnp_lpg_pwm_apply(struct pwm_chip *pwm_chip, struct pwm_device *pwm,
static const struct pwm_ops qpnp_lpg_pwm_ops = {
.apply = qpnp_lpg_pwm_apply,
.get_output_type_supported = qpnp_lpg_pwm_output_types_supported,
.owner = THIS_MODULE,
};
@ -1963,6 +1971,7 @@ static int qpnp_lpg_probe(struct platform_device *pdev)
for (i = 0; i < chip->num_lpgs; i++) {
lpg = &chip->lpgs[i];
lpg->output_type = PWM_OUTPUT_FIXED;
if (lpg->enable_pfm) {
rc = qpnp_lpg_write(lpg, REG_PWM_FM_MODE,
FM_MODE_ENABLE);

View File

@ -0,0 +1,42 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef _QTI_PWM_H
#define _QTI_PWM_H
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/pwm.h>
#include <linux/types.h>
/**
* enum pwm_output_type - output type of the PWM signal
* @PWM_OUTPUT_FIXED: PWM output is fixed until a change request
* @PWM_OUTPUT_MODULATED: PWM output is modulated in hardware
* autonomously with a predefined pattern
*/
enum pwm_output_type {
PWM_OUTPUT_FIXED = BIT(0),
PWM_OUTPUT_MODULATED = BIT(1),
};
#if IS_ENABLED(CONFIG_PWM_QTI_LPG)
int qpnp_lpg_pwm_get_output_types_supported(struct pwm_device *pwm);
int qpnp_lpg_pwm_set_output_type(struct pwm_device *pwm,
enum pwm_output_type output_type);
#else
static inline int
qpnp_lpg_pwm_get_output_types_supported(struct pwm_device *pwm)
{
return -EINVAL;
}
static inline int qpnp_lpg_pwm_set_output_type(struct pwm_device *pwm,
enum pwm_output_type output_type)
{
return -EINVAL;
}
#endif
#endif