mfd: Add support for Maxim MAX96745

The MAX96745 and MAX96747 convert DP1.4 and
eDP1.4a to single or dual GMSL2 serial. They also send
and receive control channel and peripheral control data,
enabling bidirectional transmission of video and data over
cables in excess of 15 meters in length.

Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
Change-Id: I59a338b0ef092bde1d1a444600acd92348cc9443
This commit is contained in:
Wyon Bi 2022-05-02 01:43:09 +00:00 committed by Tao Huang
parent 24af5158d9
commit 83451397e8
4 changed files with 321 additions and 0 deletions

View File

@ -921,6 +921,15 @@ config MFD_MAX8998
additional drivers must be enabled in order to use the functionality
of the device.
config MFD_MAX96745
tristate "Maxim Semiconductor MAX96745 GMSL2 Serializer Support"
depends on I2C
select MFD_CORE
select REGMAP_I2C
select I2C_MUX
help
Say yes here to add support for Maxim Semiconductor MAX96745.
config MFD_MAX96752F
tristate "Maxim Semiconductor MAX96752F GMSL2 Deserializer Support"
depends on I2C

View File

@ -170,6 +170,7 @@ max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o
obj-$(CONFIG_MFD_MAX96745) += max96745.o
obj-$(CONFIG_MFD_MAX96752F) += max96752f.o
obj-$(CONFIG_MFD_MP2629) += mp2629.o

200
drivers/mfd/max96745.c Normal file
View File

@ -0,0 +1,200 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Maxim MAX96745 MFD driver
*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max96745.h>
struct max96745 {
struct device *dev;
struct regmap *regmap;
struct i2c_mux_core *muxc;
struct gpio_desc *enable_gpio;
struct gpio_desc *lock_gpio;
};
static const struct mfd_cell max96745_devs[] = {
{
.name = "max96745-pinctrl",
.of_compatible = "maxim,max96745-pinctrl",
}, {
.name = "max96745-bridge",
.of_compatible = "maxim,max96745-bridge",
},
};
static const struct regmap_config max96745_regmap_config = {
.name = "max96745",
.reg_bits = 16,
.val_bits = 8,
.max_register = 0x8000,
};
static int max96745_select(struct i2c_mux_core *muxc, u32 chan)
{
struct max96745 *max96745 = dev_get_drvdata(muxc->dev);
if (chan == 0) {
regmap_update_bits(max96745->regmap, 0x0028, LINK_EN,
FIELD_PREP(LINK_EN, 1));
} else if (chan == 1) {
regmap_update_bits(max96745->regmap, 0x0032, LINK_EN,
FIELD_PREP(LINK_EN, 1));
} else {
regmap_update_bits(max96745->regmap, 0x0028, LINK_EN,
FIELD_PREP(LINK_EN, 1));
regmap_update_bits(max96745->regmap, 0x0032, LINK_EN,
FIELD_PREP(LINK_EN, 1));
}
return 0;
}
static void max96745_power_off(void *data)
{
struct max96745 *max96745 = data;
if (max96745->enable_gpio)
gpiod_direction_output(max96745->enable_gpio, 0);
}
static int max96745_power_on(struct max96745 *max96745)
{
u32 val;
int ret;
ret = regmap_read(max96745->regmap, 0x0107, &val);
if (!ret && FIELD_GET(VID_TX_ACTIVE_A | VID_TX_ACTIVE_B, val))
return 0;
if (max96745->enable_gpio)
gpiod_direction_output(max96745->enable_gpio, 1);
msleep(100);
ret = regmap_update_bits(max96745->regmap, 0x0010, RESET_ALL,
FIELD_PREP(RESET_ALL, 1));
if (ret < 0)
return ret;
msleep(100);
return 0;
}
static int max96745_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct device_node *child;
struct max96745 *max96745;
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++;
}
max96745 = devm_kzalloc(dev, sizeof(*max96745), GFP_KERNEL);
if (!max96745)
return -ENOMEM;
max96745->muxc = i2c_mux_alloc(client->adapter, dev, nr, 0,
I2C_MUX_LOCKED, max96745_select, NULL);
if (!max96745->muxc)
return -ENOMEM;
max96745->dev = dev;
i2c_set_clientdata(client, max96745);
max96745->regmap = devm_regmap_init_i2c(client, &max96745_regmap_config);
if (IS_ERR(max96745->regmap))
return dev_err_probe(dev, PTR_ERR(max96745->regmap),
"failed to initialize regmap");
max96745->enable_gpio = devm_gpiod_get_optional(dev, "enable",
GPIOD_ASIS);
if (IS_ERR(max96745->enable_gpio))
return dev_err_probe(dev, PTR_ERR(max96745->enable_gpio),
"failed to get enable GPIO\n");
max96745->lock_gpio = devm_gpiod_get_optional(dev, "lock", GPIOD_IN);
if (IS_ERR(max96745->lock_gpio))
return dev_err_probe(dev, PTR_ERR(max96745->lock_gpio),
"failed to get lock GPIO\n");
ret = max96745_power_on(max96745);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, max96745_power_off, max96745);
if (ret)
return ret;
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, max96745_devs,
ARRAY_SIZE(max96745_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(max96745->muxc, 0, nr, 0);
if (ret) {
i2c_mux_del_adapters(max96745->muxc);
return ret;
}
}
return 0;
}
static int max96745_i2c_remove(struct i2c_client *client)
{
struct max96745 *max96745 = i2c_get_clientdata(client);
i2c_mux_del_adapters(max96745->muxc);
return 0;
}
static void max96745_i2c_shutdown(struct i2c_client *client)
{
struct max96745 *max96745 = i2c_get_clientdata(client);
max96745_power_off(max96745);
}
static const struct of_device_id max96745_of_match[] = {
{ .compatible = "maxim,max96745", },
{}
};
MODULE_DEVICE_TABLE(of, max96745_of_match);
static struct i2c_driver max96745_i2c_driver = {
.driver = {
.name = "max96745",
.of_match_table = max96745_of_match,
},
.probe_new = max96745_i2c_probe,
.remove = max96745_i2c_remove,
.shutdown = max96745_i2c_shutdown,
};
module_i2c_driver(max96745_i2c_driver);
MODULE_AUTHOR("Wyon Bi <bivvy.bi@rock-chips.com>");
MODULE_DESCRIPTION("Maxim MAX96745 MFD driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,111 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Defining registers address and its bit definitions of MAX96745
*
* Copyright (c) 2022 Rockchip Electronics Co. Ltd.
*/
#ifndef _MFD_MAX96745_H_
#define _MFD_MAX96745_H_
#include <linux/bitfield.h>
#define GPIO_A_REG(gpio) (0x0200 + ((gpio) * 8))
#define GPIO_B_REG(gpio) (0x0201 + ((gpio) * 8))
#define GPIO_C_REG(gpio) (0x0202 + ((gpio) * 8))
#define GPIO_D_REG(gpio) (0x0203 + ((gpio) * 8))
/* 0010h */
#define RESET_ALL BIT(7)
#define SLEEP BIT(3)
/* 0013h */
#define LOCKED BIT(3)
#define ERROR BIT(2)
/* 0028h, 0032h */
#define LINK_EN BIT(7)
/* 0029h, 0033h */
#define RESET_LINK BIT(0)
#define RESET_ONESHOT BIT(1)
/* 002Ah, 0034h */
#define LINK_LOCKED BIT(0)
/* 0100h */
#define VID_LINK_SEL GENMASK(2, 1)
#define VID_TX_EN BIT(0)
/* 0101h */
#define BPP GENMASK(5, 0)
/* 0102h */
#define PCLKDET_A BIT(7)
#define DRIFT_ERR_A BIT(6)
#define OVERFLOW_A BIT(5)
#define FIFO_WARN_A BIT(4)
#define LIM_HEART BIT(2)
/* 0107h */
#define VID_TX_ACTIVE_B BIT(7)
#define VID_TX_ACTIVE_A BIT(6)
/* 0108h */
#define PCLKDET_B BIT(7)
#define DRIFT_ERR_B BIT(6)
#define OVERFLOW_B BIT(5)
#define FIFO_WARN_B BIT(4)
/* 0200h */
#define RES_CFG BIT(7)
#define TX_COM_EN BIT(5)
#define GPIO_OUT BIT(4)
#define GPIO_IN BIT(3)
#define GPIO_OUT_DIS BIT(0)
/* 0201h */
#define PULL_UPDN_SEL GENMASK(7, 6)
#define OUT_TYPEC BIT(5)
#define GPIO_TX_ID GENMASK(4, 0)
/* 0202h */
#define OVR_RES_CFG BIT(7)
#define IO_EDGE_RATE GENMASK(6, 5)
#define GPIO_RX_ID GENMASK(4, 0)
/* 0203h */
#define GPIO_IO_RX_EN BIT(5)
#define GPIO_OUT_LGC BIT(4)
#define GPIO_RX_EN_B BIT(3)
#define GPIO_TX_EN_B BIT(2)
#define GPIO_RX_EN_A BIT(1)
#define GPIO_TX_EN_A BIT(0)
/* 0750h */
#define FRCZEROPAD GENMASK(7, 6)
#define FRCZPEN BIT(5)
#define FRCSDGAIN BIT(4)
#define FRCSDEN BIT(3)
#define FRCGAIN GENMASK(2, 1)
#define FRCEN BIT(0)
/* 0751h */
#define FRCDATAWIDTH BIT(3)
#define FRCASYNCEN BIT(2)
#define FRCHSPOL BIT(1)
#define FRCVSPOL BIT(0)
/* 0752h */
#define FRCDCMODE GENMASK(1, 0)
/* 7000h */
#define LINK_ENABLE BIT(0)
/* 7070h */
#define MAX_LANE_COUNT GENMASK(7, 0)
/* 7074h */
#define MAX_LINK_RATE GENMASK(7, 0)
#endif /* _MFD_MAX96745_H_ */