diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index cc170fbdbdf7..66971f15b727 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -947,6 +947,15 @@ config MFD_MAX96755F help Say yes here to add support for Maxim Semiconductor MAX96755. +config MFD_MAX96776 + tristate "Maxim Semiconductor MAX96776 GMSL2 Deserializer Support" + depends on I2C + select MFD_CORE + select REGMAP_I2C + select I2C_MUX + help + Say yes here to add support for Maxim Semiconductor MAX96776. + config MFD_MT6360 tristate "Mediatek MT6360 SubPMIC" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 343958813edf..e369698ee720 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -173,6 +173,7 @@ obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o obj-$(CONFIG_MFD_MAX96745) += max96745.o obj-$(CONFIG_MFD_MAX96752F) += max96752f.o obj-$(CONFIG_MFD_MAX96755F) += max96755f.o +obj-$(CONFIG_MFD_MAX96776) += max96776.o obj-$(CONFIG_MFD_MP2629) += mp2629.o diff --git a/drivers/mfd/max96776.c b/drivers/mfd/max96776.c new file mode 100644 index 000000000000..abea0a25bfec --- /dev/null +++ b/drivers/mfd/max96776.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim MAX96776 MFD driver + * + * Copyright (C) 2022 Rockchip Electronics Co. Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct max96776 { + struct device *dev; + struct regmap *regmap; + struct i2c_client *client; + struct i2c_mux_core *muxc; + struct gpio_desc *enable_gpio; + u32 stream_id; +}; + +static const struct mfd_cell max96776_devs[] = { + { + .name = "max96776-bridge", + .of_compatible = "maxim,max96776-bridge", + }, +}; + +static int max96776_select(struct i2c_mux_core *muxc, u32 chan) +{ + return 0; +} + +static const struct regmap_range max96776_readable_ranges[] = { + regmap_reg_range(0x0000, 0x0026), + regmap_reg_range(0x0029, 0x002c), + regmap_reg_range(0x0050, 0x0050), + regmap_reg_range(0x0100, 0x0100), + regmap_reg_range(0x0103, 0x0103), + regmap_reg_range(0x0108, 0x0108), + regmap_reg_range(0x0600, 0x0600), + regmap_reg_range(0x07f0, 0x07f1), + regmap_reg_range(0x1700, 0x1700), + regmap_reg_range(0x4100, 0x4100), + regmap_reg_range(0x6230, 0x6230), + regmap_reg_range(0xe75e, 0xe75e), + regmap_reg_range(0xe776, 0xe7bf), +}; + +static const struct regmap_access_table max96776_readable_table = { + .yes_ranges = max96776_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(max96776_readable_ranges), +}; + +static const struct regmap_config max96776_regmap_config = { + .name = "max96776", + .reg_bits = 16, + .val_bits = 8, + .rd_table = &max96776_readable_table, + .max_register = 0xff02, +}; + +static void max96776_power_on(struct max96776 *max96776) +{ + if (max96776->enable_gpio) { + gpiod_direction_output(max96776->enable_gpio, 1); + msleep(500); + } +} + +static void max96776_power_off(struct max96776 *max96776) +{ + if (max96776->enable_gpio) + gpiod_direction_output(max96776->enable_gpio, 0); +} + +static int max96776_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device_node *child; + struct max96776 *max96776; + unsigned int nr = 0; + int ret; + + for_each_available_child_of_node(dev->of_node, child) { + if (!of_find_property(child, "reg", NULL)) + continue; + + nr++; + } + + max96776 = devm_kzalloc(dev, sizeof(*max96776), GFP_KERNEL); + if (!max96776) + return -ENOMEM; + + max96776->muxc = i2c_mux_alloc(client->adapter, dev, nr, 0, + I2C_MUX_LOCKED, max96776_select, NULL); + if (!max96776->muxc) + return -ENOMEM; + + max96776->dev = dev; + max96776->client = client; + + max96776->enable_gpio = devm_gpiod_get_optional(dev, "enable", + GPIOD_ASIS); + if (IS_ERR(max96776->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(max96776->enable_gpio), + "failed to get enable GPIO\n"); + + ret = device_property_read_u32(dev->parent, "reg", &max96776->stream_id); + if (ret) + return dev_err_probe(dev, ret, "failed to get gmsl id\n"); + + i2c_set_clientdata(client, max96776); + + max96776->regmap = devm_regmap_init_i2c(client, + &max96776_regmap_config); + if (IS_ERR(max96776->regmap)) + return dev_err_probe(dev, PTR_ERR(max96776->regmap), + "failed to initialize regmap\n"); + + max96776_power_on(max96776); + + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, max96776_devs, + ARRAY_SIZE(max96776_devs), NULL, 0, NULL); + if (ret) + return ret; + + for_each_available_child_of_node(dev->of_node, child) { + if (of_property_read_u32(child, "reg", &nr)) + continue; + + ret = i2c_mux_add_adapter(max96776->muxc, 0, nr, 0); + if (ret) { + i2c_mux_del_adapters(max96776->muxc); + return ret; + } + } + + return 0; +} + +static void max96776_i2c_shutdown(struct i2c_client *client) +{ + struct max96776 *max96776 = i2c_get_clientdata(client); + + max96776_power_off(max96776); +} + +static int __maybe_unused max96776_suspend(struct device *dev) +{ + struct max96776 *max96776 = dev_get_drvdata(dev); + + max96776_power_off(max96776); + + return 0; +} + +static int __maybe_unused max96776_resume(struct device *dev) +{ + struct max96776 *max96776 = dev_get_drvdata(dev); + + max96776_power_on(max96776); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(max96776_pm_ops, max96776_suspend, max96776_resume); + +static const struct of_device_id max96776_of_match[] = { + { .compatible = "maxim,max96776", }, + {} +}; +MODULE_DEVICE_TABLE(of, max96776_of_match); + +static struct i2c_driver max96776_i2c_driver = { + .driver = { + .name = "max96776", + .of_match_table = of_match_ptr(max96776_of_match), + .pm = &max96776_pm_ops, + }, + .probe_new = max96776_i2c_probe, + .shutdown = max96776_i2c_shutdown, +}; + +module_i2c_driver(max96776_i2c_driver); + +MODULE_AUTHOR("Guochun Huang "); +MODULE_DESCRIPTION("Maxim max96776 MFD driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max96776.h b/include/linux/mfd/max96776.h new file mode 100644 index 000000000000..fd445a7ac4b5 --- /dev/null +++ b/include/linux/mfd/max96776.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Defining registers address and its bit definitions of MAX96776 + * + * Copyright (c) 2022 Rockchip Electronics Co. Ltd. + */ + +#ifndef _MFD_MAX96776_H_ +#define _MFD_MAX96776_H_ + +#include + +/* 07f0h */ +#define TRAINING_SUCCESSFUL BIT(0) + +/* 1700h */ +#define CMD_RESET BIT(7) + +/* 6230h */ +#define HPD_PRESENT BIT(0) + +/* e776h */ +#define REBOOT_TRAINNING BIT(0) +#define RUN_LINK_TRAINING BIT(1) +#define AUX_READ BIT(4) +#define AUX_WRITE BIT(5) + +/* e777h */ +#define RUN_COMMAND BIT(7) + +/* e778h */ +#define USER_DATA1_B0 GENMASK(7, 0) + +/* e779h */ +#define USER_DATA1_B1 GENMASK(7, 0) + +/* e77ah */ +#define USER_DATA2_B0 GENMASK(7, 0) + +/* e77ch */ +#define USER_DATA3_B0 GENMASK(7, 0) + +/* e790h */ +#define LINK_RATE GENMASK(4, 0) + +/* e792h */ +#define LANE_COUNT GENMASK(2, 0) + +/* e794h */ +#define HRES_B0 GENMASK(7, 0) + +/* e795h */ +#define HRES_B1 GENMASK(7, 0) + +/* e796h */ +#define HFP_B0 GENMASK(7, 0) + +/* e797h */ +#define HFP_B1 GENMASK(7, 0) + +/* e798h */ +#define HSW_B0 GENMASK(7, 0) + +/* e799h */ +#define HSW_B1 GENMASK(7, 0) + +/* e79ah */ +#define HBP_B0 GENMASK(7, 0) + +/* e79bh */ +#define HBP_B1 GENMASK(7, 0) + +/* e79ch */ +#define VRES_B0 GENMASK(7, 0) + +/* e79dh */ +#define VRES_B1 GENMASK(7, 0) + +/* e79eh */ +#define VFP_B0 GENMASK(7, 0) + +/* e79fh */ +#define VFP_B1 GENMASK(7, 0) + +/* e7a0h */ +#define VSW_B0 GENMASK(7, 0) + +/* e7a1h */ +#define VSW_B1 GENMASK(7, 0) + +/* e7a2h */ +#define VBP_B0 GENMASK(7, 0) + +/* e7a3h */ +#define VBP_B1 GENMASK(7, 0) + +/* e7a4h */ +#define HWORDS_B0 GENMASK(7, 0) + +/* e7a5h */ +#define HWORDS_B1 GENMASK(7, 0) + +/* e7a6h */ +#define MVID_B0 GENMASK(7, 0) + +/* e7a7h */ +#define MVID_B1 GENMASK(7, 0) + +/* e7a8h */ +#define NVID_B0 GENMASK(7, 0) + +/* e7a9h */ +#define NVID_B1 GENMASK(7, 0) + +/* e7aah */ +#define TUC_VALUE_B0 GENMASK(7, 0) + +/* e7abh */ +#define TUC_VALUE_B1 GENMASK(7, 0) + +/* e7ach */ +#define HSYNC_POL BIT(0) +#define VSYNC_POL BIT(1) + +/* e7b0h */ +#define SS_ENABLE BIT(0) + +/* e7b1h */ +#define SSC_ENABLE BIT(4) + +#endif /* _MFD_MAX96776_H_ */