Files
linux-nv-oot/drivers/media/platform/tegra/cdi/cdi_pwm.c
Laxman Dewangan 9de2a9da7f pwm: Drop owner from pwm_ops for Linux 6.7
The member "owner" is removed from the pwm_ops from
Linux 6.7 onwards with below change.
***
commit 384461abcab6602abc06c2dfb8fb99beeeaa12b0
Author: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

    pwm: Manage owner assignment implicitly for drivers
***

Remove this member.

Bug 4346767

Change-Id: I7893c29641d15ac7ef56d29625a2df696872c659
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3019999
Reviewed-by: Suresh Mangipudi <smangipudi@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
2023-11-23 11:09:33 -08:00

250 lines
5.4 KiB
C

// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2016-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/pwm.h>
#include <linux/atomic.h>
#include <linux/version.h>
#include "cdi-pwm-priv.h"
/*
* Below values are configured during suspend.
* Invalid values are chosen so that PWM
* configuration in resume works fine.
* Period is chosen as least non-zero value
* and duty-ratio zero.
*/
#define PWM_SUSPEND_PERIOD 1
#define PWM_SUSPEND_DUTY_RATIO 0
static const struct of_device_id cdi_pwm_of_match[] = {
{ .compatible = "nvidia,cdi-pwm", .data = NULL },
{},
};
static inline struct cdi_pwm_info *to_cdi_pwm_info(struct pwm_chip *chip)
{
return container_of(chip, struct cdi_pwm_info, chip);
}
#if KERNEL_VERSION(6, 0, 0) > LINUX_VERSION_CODE
static int cdi_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct cdi_pwm_info *info = to_cdi_pwm_info(chip);
int err = 0;
if (!chip || !pwm)
return -EINVAL;
if (info->force_on)
return err;
mutex_lock(&info->mutex);
if (atomic_inc_return(&info->in_use) == 1)
err = pwm_enable(info->pwm);
mutex_unlock(&info->mutex);
return err;
}
static void cdi_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct cdi_pwm_info *info = to_cdi_pwm_info(chip);
int atomic_val;
mutex_lock(&info->mutex);
atomic_val = atomic_read(&info->in_use);
if (atomic_val > 0) {
if (atomic_dec_and_test(&info->in_use))
pwm_disable(info->pwm);
}
mutex_unlock(&info->mutex);
}
static int cdi_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct cdi_pwm_info *info = to_cdi_pwm_info(chip);
int err = 0;
if (info->force_on)
return err;
mutex_lock(&info->mutex);
err = pwm_config(info->pwm, duty_ns, period_ns);
mutex_unlock(&info->mutex);
return err;
}
#endif
static struct pwm_device *of_cdi_pwm_xlate(struct pwm_chip *pc,
const struct of_phandle_args *args)
{
struct pwm_device *pwm;
struct cdi_pwm_info *info = to_cdi_pwm_info(pc);
int err = 0;
pwm = pwm_request_from_chip(pc, args->args[0], NULL);
if (!args->args[1]) {
dev_err(pc->dev, "Period should be larger than 0\n");
return NULL;
}
if (info->force_on) {
err = pwm_config(info->pwm, args->args[1]/4, args->args[1]);
if (err) {
dev_err(pc->dev, "can't config PWM: %d\n", err);
return NULL;
}
err = pwm_enable(info->pwm);
if (err) {
dev_err(pc->dev, "can't enable PWM: %d\n", err);
return NULL;
}
} else {
err = pwm_config(pwm, args->args[1]/4, args->args[1]);
if (err) {
dev_err(pc->dev, "can't config PWM: %d\n", err);
return NULL;
}
}
return pwm;
}
static const struct pwm_ops cdi_pwm_ops = {
#if KERNEL_VERSION(6, 0, 0) > LINUX_VERSION_CODE
.config = cdi_pwm_config,
.enable = cdi_pwm_enable,
.disable = cdi_pwm_disable,
#endif
#if !defined(NV_PWM_OPS_HAS_NO_OWNER)
.owner = THIS_MODULE,
#endif
};
static int cdi_pwm_probe(struct platform_device *pdev)
{
struct cdi_pwm_info *info = NULL;
int err = 0, npwm;
bool force_on = false;
dev_info(&pdev->dev, "%sing...\n", __func__);
info = devm_kzalloc(
&pdev->dev, sizeof(struct cdi_pwm_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
atomic_set(&info->in_use, 0);
mutex_init(&info->mutex);
err = of_property_read_u32(pdev->dev.of_node, "npwm", &npwm);
if (err < 0) {
dev_err(&pdev->dev, "npwm is not defined: %d\n", err);
return err;
}
force_on = of_property_read_bool(pdev->dev.of_node, "force_on");
info->chip.dev = &pdev->dev;
info->chip.ops = &cdi_pwm_ops;
info->chip.base = -1;
info->chip.npwm = npwm;
info->chip.of_xlate = of_cdi_pwm_xlate;
info->chip.of_pwm_n_cells = 2;
info->force_on = force_on;
err = pwmchip_add(&info->chip);
if (err < 0) {
dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", err);
return err;
}
platform_set_drvdata(pdev, info);
info->pwm = devm_pwm_get(&pdev->dev, NULL);
if (!IS_ERR(info->pwm)) {
pwm_disable(info->pwm);
dev_info(&pdev->dev, "%s success to get PWM\n", __func__);
} else {
pwmchip_remove(&info->chip);
err = PTR_ERR(info->pwm);
if (err != -EPROBE_DEFER)
dev_err(&pdev->dev,
"%s: fail to get PWM\n", __func__);
}
return err;
}
static int cdi_pwm_remove(struct platform_device *pdev)
{
struct cdi_pwm_info *info = platform_get_drvdata(pdev);
pwmchip_remove(&info->chip);
return 0;
}
static int cdi_pwm_suspend(struct device *dev)
{
int err = 0;
struct cdi_pwm_info *info = dev_get_drvdata(dev);
if (info == NULL) {
dev_err(dev, "%s: fail to get info\n", __func__);
} else {
if (!IS_ERR(info->pwm)) {
pwm_disable(info->pwm);
err = pwm_config(info->pwm, PWM_SUSPEND_DUTY_RATIO,
PWM_SUSPEND_PERIOD);
}
}
return 0;
}
static int cdi_pwm_resume(struct device *dev)
{
/* Do nothing */
return 0;
}
static const struct dev_pm_ops cdi_pwm_pm_ops = {
.suspend = cdi_pwm_suspend,
.resume = cdi_pwm_resume,
.runtime_suspend = cdi_pwm_suspend,
.runtime_resume = cdi_pwm_resume,
};
static struct platform_driver cdi_pwm_driver = {
.driver = {
.name = "cdi-pwm",
.owner = THIS_MODULE,
.of_match_table = cdi_pwm_of_match,
.pm = &cdi_pwm_pm_ops,
},
.probe = cdi_pwm_probe,
.remove = cdi_pwm_remove,
};
module_platform_driver(cdi_pwm_driver);
MODULE_AUTHOR("Junghyun Kim <juskim@nvidia.com>");
MODULE_DESCRIPTION("CDI PWM driver");
MODULE_LICENSE("GPL v2");
MODULE_DEVICE_TABLE(of, cdi_pwm_of_match);
MODULE_SOFTDEP("pre: cdi_dev");