pwm: add support for r7f701

Change-Id: I3a3441c5c57aded98484c81f8e2b089faef98641
Signed-off-by: Zitong Cai <zitong.cai@rock-chips.com>
This commit is contained in:
Zitong Cai
2025-06-20 10:46:17 +08:00
committed by Tao Huang
parent 6c514e027d
commit fe4e4c8d8c
3 changed files with 288 additions and 0 deletions

View File

@@ -507,6 +507,13 @@ config PWM_ROCKCHIP_TEST
whether it is about the generic framework or the functions
supported by Rockchip PWM.
config PWM_R7F701
tristate "R7F701 PWM support"
help
This is a MCU for controlling brightness on the screen, which
adjusts brightness by writing corresponding registers through I2C.
If you don't have this MCU in your design, choose N.
config PWM_SAMSUNG
tristate "Samsung PWM support"
depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST

View File

@@ -46,6 +46,7 @@ obj-$(CONFIG_PWM_RCAR) += pwm-rcar.o
obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
obj-$(CONFIG_PWM_ROCKCHIP_TEST) += pwm-rockchip-test.o
obj-$(CONFIG_PWM_R7F701) += pwm-r7f701.o
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o

280
drivers/pwm/pwm-r7f701.c Normal file
View File

@@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* serdes-i2c.c -- Control screen brightness
*
* Copyright (c) 2025 Rockchip Electronics Co., Ltd.
*
* Author: ZITONG CAI <zitong.cai@rock-chips.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/pwm.h>
#include <linux/of_device.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h>
#define PWM_MAX_LEVEL 0x64
#define DISPLAY_STATUS 0x40
#define LVDS_LOCK_STATUS 0x41
#define CUR_BRIGHTNESS_LEVEL 0x42
#define OLED_FAULT_RECORD 0x43
#define PCB_TEMP_STATUS 0x44
#define OLED_TEMP_STATUS 0x45
#define CID_POWER_STATUS 0x46
#define CID_HARDWARE_VERSION 0x47
#define CID_SOFT_APP_VERSION 0x48
#define CID_BOOTLOADER_VERSION 0x49
#define CID_FUALT_RECORD 0x4a
#define CID_VOLTAGE_VALUE 0x4b
#define CID_CURRENT_MODE_STATUS 0x4c
#define CID_ENTER_AUTO_CAUSE 0x4d
#define CID_CAN_STATUS 0x4e
#define REQUEST_DISPLAY_STATUS 0x80
#define REQUEST_LVDS_LOCK_STATUS 0x81
#define REQUEST_BRIGHTNESS_LEVEL 0x82
#define REQUEST_OLED_FAULT_RECORD 0x83
#define REQUEST_PCB_TEMP_STATUS 0x84
#define REQUEST_OLED_TEMP_STATUS 0x85
#define REQUEST_CID_POWER_STATE 0x86
#define REQUEST_CID_HARDWARE_VERSION 0x87
#define REQUEST_CID_SOFT_APP_VERSION 0x88
#define REQUEST_CID_BOOTLOADER_VERSION 0x89
#define REQUEST_CID_FUALT_RECORD 0x8a
#define REQUEST_CID_VOLTAGE_VALUE 0x8b
#define REQUEST_CID_CURRENT_MODE_STATUS 0x8c
#define REQUEST_CID_ENTER_AUTO_CAUSE 0x8d
#define REQUEST_DISPLAY_STATUS_SET 0x8e
#define REQUEST_CID_BRIGHTNESS_SET 0x8f
#define REQUEST_IDCM_WRITE_HEART 0x90
#define REQUEST_CID_CAN_STATUS 0x91
#define REQUEST_IDCM_SEND_CRC 0x92
enum {
DISPLAY_OFF,
DISPLAY_ON
};
struct r7f701_pwm_chip {
struct pwm_chip chip;
struct device *dev;
struct regmap *regmap;
};
static bool r7f701_is_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case 0x80 ... 0x92:
return true;
}
return false;
}
static bool r7f701_is_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case 0x40 ... 0x4e:
return true;
}
return false;
}
static bool r7f701_is_volatile_reg(struct device *dev, unsigned int reg)
{
return true;
}
static const struct regmap_config r7f701_regmap_config = {
.name = "r7f701",
.reg_bits = 8,
.val_bits = 8,
.writeable_reg = r7f701_is_writeable_reg,
.readable_reg = r7f701_is_readable_reg,
.volatile_reg = r7f701_is_volatile_reg,
.cache_type = REGCACHE_RBTREE,
.max_register = 0x92,
};
static inline struct r7f701_pwm_chip *to_r7f701_pwm_chip(struct pwm_chip *chip)
{
return container_of(chip, struct r7f701_pwm_chip, chip);
}
static int r7f701_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
u64 duty_ns, u64 period_ns)
{
u8 reg = 0;
u64 div = 0;
u8 level = 0;
int ret = 0;
u8 data[7] = {0};
struct r7f701_pwm_chip *r7f701 = to_r7f701_pwm_chip(chip);
div = duty_ns * PWM_MAX_LEVEL;
level = DIV_ROUND_CLOSEST_ULL(div, period_ns);
reg = REQUEST_DISPLAY_STATUS_SET;
data[0] = DISPLAY_ON;
data[1] = level;
data[6] = reg ^ data[0] ^ data[1];
ret |= regmap_bulk_write(r7f701->regmap, reg, data, ARRAY_SIZE(data));
memset(data, 0, sizeof(data));
reg = REQUEST_CID_BRIGHTNESS_SET;
data[0] = level;
data[6] = reg ^ data[0];
ret |= regmap_bulk_write(r7f701->regmap, reg, data, ARRAY_SIZE(data));
dev_dbg(chip->dev, "%s: pwm chip BRIGHTNESS_SET level 0x%x ret=%d\n", __func__, level, ret);
return 0;
}
static int r7f701_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
dev_dbg(chip->dev, "%s: pwm chip\n", __func__);
return 0;
}
static void r7f701_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct r7f701_pwm_chip *r7f701 = to_r7f701_pwm_chip(chip);
int ret = 0;
u8 reg = 0;
u8 data[7] = {0};
reg = REQUEST_DISPLAY_STATUS_SET;
data[0] = DISPLAY_OFF;
data[6] = reg ^ data[0];
ret = regmap_bulk_write(r7f701->regmap, reg, data, ARRAY_SIZE(data));
dev_dbg(chip->dev, "%s: pwm chip ret=%d\n", __func__, ret);
}
static int r7f701_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
int err;
if (state->polarity != PWM_POLARITY_NORMAL)
return -EINVAL;
if (!state->enabled) {
if (pwm->state.enabled)
r7f701_pwm_disable(chip, pwm);
return 0;
}
err = r7f701_pwm_config(chip, pwm, state->duty_cycle, state->period);
if (err)
return err;
if (!pwm->state.enabled)
return r7f701_pwm_enable(chip, pwm);
return 0;
}
static int r7f701_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
{
state->enabled = true;
state->polarity = PWM_POLARITY_NORMAL;
dev_dbg(chip->dev, "%s: pwm chip\n", __func__);
return 0;
}
static const struct pwm_ops r7f701_pwm_ops = {
.apply = r7f701_pwm_apply,
.get_state = r7f701_pwm_get_state,
.owner = THIS_MODULE,
};
static const struct of_device_id pwm_of_match[] = {
{ .compatible = "r7f701-pwm", .data = 0},
{ }
};
static int pwm_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct r7f701_pwm_chip *r7f701;
int ret = 0;
r7f701 = devm_kzalloc(dev, sizeof(*r7f701), GFP_KERNEL);
if (!r7f701)
return -ENOMEM;
r7f701->dev = dev;
r7f701->chip.dev = dev;
r7f701->chip.ops = &r7f701_pwm_ops;
r7f701->chip.npwm = 1;
i2c_set_clientdata(client, r7f701);
dev_set_drvdata(dev, r7f701);
r7f701->regmap = devm_regmap_init_i2c(client, &r7f701_regmap_config);
if (IS_ERR(r7f701->regmap)) {
dev_err(dev, "%s: Failed to allocate r7f701 register map\n", __func__);
return PTR_ERR(r7f701->regmap);
}
ret = devm_pwmchip_add(dev, &r7f701->chip);
if (ret < 0) {
dev_err(dev, "%s: pwmchip_add() failed: %d\n", __func__, ret);
return ret;
}
dev_dbg(dev, "%s successful\n", __func__);
return 0;
}
static struct i2c_driver r7f701_i2c_driver = {
.driver = {
.name = "r7f701-pwm",
.of_match_table = of_match_ptr(pwm_of_match),
},
.probe = pwm_probe,
};
static int __init r7f701_i2c_init(void)
{
int ret;
ret = i2c_add_driver(&r7f701_i2c_driver);
if (ret != 0)
pr_err("Failed to register r7f701 I2C driver: %d\n", ret);
return ret;
}
static void __exit r7f701_i2c_exit(void)
{
i2c_del_driver(&r7f701_i2c_driver);
}
subsys_initcall(r7f701_i2c_init);
module_exit(r7f701_i2c_exit);
MODULE_AUTHOR("ZITONG CAI <zitong.cai@rock-chips.com>");
MODULE_DESCRIPTION("display pwm interface");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:r7f701-PWM");