From baea684a65feb33524acd1995160d40323059342 Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Thu, 25 Nov 2021 13:51:53 +0000 Subject: [PATCH 01/22] dc: bridge: add maxim display serializer driver Add driver for maxim display serializer. -Enable SST mode. Change-Id: I717b3ed053a97e98deb3a5547e03dec085bf1e21 Signed-off-by: Vishwaroop A Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2632453 Tested-by: mobile promotions Reviewed-by: Shu Zhong Reviewed-by: Bitan Biswas Reviewed-by: svc_kernel_abi Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- .../dc/bridge/maxim_gmsl_dp_serializer.c | 474 ++++++++++++++++++ 1 file changed, 474 insertions(+) create mode 100644 drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c new file mode 100644 index 00000000..fa40a65d --- /dev/null +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MAXIM DP Serializer driver for MAXIM GMSL Serializers + * + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_GMSL_DP_SER_CTRL3 0x13 +#define MAX_GMSL_DP_SER_CTRL3_LOCK_MASK (1 << 3) +#define MAX_GMSL_DP_SER_CTRL3_LOCK_VAL (1 << 3) + +#define MAX_GMSL_DP_SER_INTR8 0x20 +#define MAX_GMSL_DP_SER_INTR8_MASK 0x1 +#define MAX_GMSL_DP_SER_INTR8_VAL 0x1 + +#define MAX_GMSL_DP_SER_LINK_CTRL_PHY_A 0x29 +#define MAX_GMSL_DP_SER_LINK_CTRL_A_MASK 0x1 + +#define MAX_GMSL_DP_SER_LCTRL2_A 0x2A +#define MAX_GMSL_DP_SER_LCTRL2_LOCK_MASK (1 << 0) +#define MAX_GMSL_DP_SER_LCTRL2_LOCK_VAL (1 << 0) + +#define MAX_GMSL_DP_SER_LINK_CTRL_PHY_B 0x33 +#define MAX_GMSL_DP_SER_LINK_CTRL_B_MASK 0x1 + +#define MAX_GMSL_DP_SER_LCTRL2_B 0x34 +#define MAX_GMSL_DP_SER_LCTRL2_LOCK_B_MASK (1 << 0) +#define MAX_GMSL_DP_SER_LCTRL2_LOCK_B_VAL (1 << 0) + +#define MAX_GMSL_DP_SER_VID_TX_X 0x100 +#define MAX_GMSL_DP_SER_VID_TX_X_MASK 0x1 +#define MAX_GMSL_DP_SER_VID_TX_Y 0x110 +#define MAX_GMSL_DP_SER_VID_TX_Y_MASK 0x1 +#define MAX_GMSL_DP_SER_VID_TX_Z 0x120 +#define MAX_GMSL_DP_SER_VID_TX_Z_MASK 0x1 +#define MAX_GMSL_DP_SER_VID_TX_U 0x130 +#define MAX_GMSL_DP_SER_VID_TX_U_MASK 0x1 + +#define MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B0 0x6064 +#define MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B1 0x6065 +#define MAX_GMSL_DP_SER_PHY_EDP_1_CTRL0_B0 0x6164 +#define MAX_GMSL_DP_SER_PHY_EDP_1_CTRL0_B1 0x6165 +#define MAX_GMSL_DP_SER_PHY_EDP_2_CTRL0_B0 0x6264 +#define MAX_GMSL_DP_SER_PHY_EDP_2_CTRL0_B1 0x6265 +#define MAX_GMSL_DP_SER_PHY_EDP_3_CTRL0_B0 0x6364 +#define MAX_GMSL_DP_SER_PHY_EDP_3_CTRL0_B1 0x6365 + +#define MAX_GMSL_DP_SER_DPRX_TRAIN 0x641A +#define MAX_GMSL_DP_SER_DPRX_TRAIN_STATE_MASK 0xF0 +#define MAX_GMSL_DP_SER_DPRX_TRAIN_STATE_VAL 0xF0 + +#define MAX_GMSL_DP_SER_LINK_ENABLE 0x7000 +#define MAX_GMSL_DP_SER_LINK_ENABLE_MASK 0x1 + +#define MAX_GMSL_DP_SER_MISC_CONFIG_B1 0x7019 +#define MAX_GMSL_DP_SER_MAX_LINK_COUNT 0x7070 +#define MAX_GMSL_DP_SER_MAX_LINK_RATE 0x7074 + +#define MAX_GMSL_DP_SER_LOCAL_EDID 0x7084 + +#define MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY 0x70A4 +#define MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_MASK 0x3F +#define MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_100KBPS 0x8 + +struct max_gmsl_dp_ser_data { + bool has_link_a; + bool has_link_b; +}; + +struct max_gmsl_dp_ser_source { + struct fwnode_handle *fwnode; +}; + +static const struct regmap_config max_gmsl_dp_ser_i2c_regmap = { + .reg_bits = 16, + .val_bits = 8, +}; + +struct max_gmsl_dp_ser_priv { + struct i2c_client *client; + struct gpio_desc *gpiod_pwrdn; + u8 dprx_lane_count; + u8 dprx_link_rate; + struct mutex mutex; + struct regmap *regmap; + struct work_struct work; + struct delayed_work delay_work; + struct workqueue_struct *wq; + int ser_errb; + unsigned int ser_irq; + const struct max_gmsl_dp_ser_data *ser_data; +}; + +static int max_gmsl_dp_ser_read(struct max_gmsl_dp_ser_priv *priv, int reg) +{ + int ret, val = 0; + + ret = regmap_read(priv->regmap, reg, &val); + if (ret < 0) + dev_err(&priv->client->dev, + "%s: register 0x%02x read failed (%d)\n", + __func__, reg, ret); + return val; +} + +static int max_gmsl_dp_ser_write(struct max_gmsl_dp_ser_priv *priv, u32 reg, u8 val) +{ + int ret; + + ret = regmap_write(priv->regmap, reg, val); + if (ret < 0) + dev_err(&priv->client->dev, + "%s: register 0x%02x write failed (%d)\n", + __func__, reg, ret); + return ret; +} + +/* static api to update given value */ +static inline void max_gmsl_dp_ser_update(struct max_gmsl_dp_ser_priv *priv, + u32 reg, u32 mask, u8 val) +{ + u8 update_val; + + update_val = max_gmsl_dp_ser_read(priv, reg); + update_val = ((update_val & (~mask)) | (val & mask)); + max_gmsl_dp_ser_write(priv, reg, update_val); +} + +static void max_gmsl_dp_ser_sst_setup(struct max_gmsl_dp_ser_priv *priv) +{ + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B0, 0x0f); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B1, 0x0f); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_1_CTRL0_B0, 0x0f); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_1_CTRL0_B1, 0x0f); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_2_CTRL0_B0, 0x0f); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_2_CTRL0_B1, 0x0f); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_3_CTRL0_B0, 0x0f); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_3_CTRL0_B1, 0x0f); + + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_LOCAL_EDID, 0x1); + + /* Disable MST Mode */ + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_MISC_CONFIG_B1, 0x0); + + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_MAX_LINK_RATE, + priv->dprx_link_rate); + + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_MAX_LINK_COUNT, + priv->dprx_lane_count); + + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY, + MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_MASK, + MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_100KBPS); +} + +static int max_gmsl_read_lock(struct max_gmsl_dp_ser_priv *priv, + u32 reg_addr, u32 mask, + u32 expected_value) +{ + u8 reg_data; + + reg_data = max_gmsl_dp_ser_read(priv, reg_addr); + if ((reg_data & mask) == expected_value) + return 0; + + return -1; +} + +static irqreturn_t max_gsml_dp_ser_irq_handler(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +static void tegra_poll_gmsl_training_lock(struct work_struct *work) +{ + struct delayed_work *dwork = container_of(work, + struct delayed_work, work); + struct max_gmsl_dp_ser_priv *priv = container_of(dwork, + struct max_gmsl_dp_ser_priv, delay_work); + int ret = 0; + + ret = max_gmsl_read_lock(priv, MAX_GMSL_DP_SER_CTRL3, + MAX_GMSL_DP_SER_CTRL3_LOCK_MASK, + MAX_GMSL_DP_SER_CTRL3_LOCK_VAL); + if (ret < 0) { + dev_dbg(&priv->client->dev, "GMSL Lock is not set\n"); + goto reschedule; + } + + if (priv->ser_data->has_link_a) { + ret = max_gmsl_read_lock(priv, + MAX_GMSL_DP_SER_LCTRL2_A, + MAX_GMSL_DP_SER_LCTRL2_LOCK_MASK, + MAX_GMSL_DP_SER_LCTRL2_LOCK_VAL); + + if (ret < 0) { + dev_dbg(&priv->client->dev, + "GMSL Lock is not set for LINK A\n"); + goto reschedule; + } + } + + if (priv->ser_data->has_link_b) { + ret = max_gmsl_read_lock(priv, + MAX_GMSL_DP_SER_LCTRL2_B, + MAX_GMSL_DP_SER_LCTRL2_LOCK_B_MASK, + MAX_GMSL_DP_SER_LCTRL2_LOCK_B_VAL); + if (ret < 0) { + dev_dbg(&priv->client->dev, + "GSM Lock is not set for LINK B\n"); + goto reschedule; + } + } + + ret = max_gmsl_read_lock(priv, MAX_GMSL_DP_SER_DPRX_TRAIN, + MAX_GMSL_DP_SER_DPRX_TRAIN_STATE_MASK, + MAX_GMSL_DP_SER_DPRX_TRAIN_STATE_VAL); + if (ret < 0) { + dev_dbg(&priv->client->dev, + "DP Link tranining hasn't completed\n"); + goto reschedule; + } + + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_X, + MAX_GMSL_DP_SER_VID_TX_X_MASK, 0x1); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Y, + MAX_GMSL_DP_SER_VID_TX_Y_MASK, 0x1); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Z, + MAX_GMSL_DP_SER_VID_TX_Z_MASK, 0x1); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_U, + MAX_GMSL_DP_SER_VID_TX_U_MASK, 0x1); + return; + +reschedule: + queue_delayed_work(priv->wq, + &priv->delay_work, msecs_to_jiffies(500)); +} + +static int max_gmsl_dp_ser_init(struct device *dev) +{ + struct max_gmsl_dp_ser_priv *priv; + struct i2c_client *client; + int ret = 0; + + client = to_i2c_client(dev); + priv = i2c_get_clientdata(client); + + priv->gpiod_pwrdn = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpiod_pwrdn)) { + dev_err(dev, "%s: gpiopwrdn is not enabled\n", __func__); + return PTR_ERR(priv->gpiod_pwrdn); + } + gpiod_set_consumer_name(priv->gpiod_pwrdn, "max_gmsl_dp_ser-pwrdn"); + + /* Drive PWRDNB pin high to power up the serializer */ + gpiod_set_value_cansleep(priv->gpiod_pwrdn, 1); + + /* Wait ~2ms for powerup to complete */ + usleep_range(2000, 2200); + + /* + * Write RESET_LINK = 1 (for both Phy A, 0x29, and Phy B, 0x33) + * within 10ms + */ + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_CTRL_PHY_A, + MAX_GMSL_DP_SER_LINK_CTRL_A_MASK, 0x1); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_CTRL_PHY_B, + MAX_GMSL_DP_SER_LINK_CTRL_B_MASK, 0x1); + + /* + * Disable video output on the GMSL link by setting VID_TX_EN = 0 + * for Pipe X, Y, Z and U + */ + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_X, + MAX_GMSL_DP_SER_VID_TX_X_MASK, 0x0); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Y, + MAX_GMSL_DP_SER_VID_TX_Y_MASK, 0x0); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Z, + MAX_GMSL_DP_SER_VID_TX_Z_MASK, 0x0); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_U, + MAX_GMSL_DP_SER_VID_TX_U_MASK, 0x0); + + /* + * Set LINK_ENABLE=0 (0x7000) to force the DP HPD + * pin low to hold off DP link training and + * SOC video + */ + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_ENABLE, + MAX_GMSL_DP_SER_LINK_ENABLE_MASK, 0x0); + + max_gmsl_dp_ser_sst_setup(priv); + + /* + * Write RESET_LINK = 0 (for both Phy A, 0x29, and Phy B, 0x33) + * to initiate the GMSL link lock process. + */ + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_CTRL_PHY_A, + MAX_GMSL_DP_SER_LINK_CTRL_A_MASK, 0x0); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_CTRL_PHY_B, + MAX_GMSL_DP_SER_LINK_CTRL_B_MASK, 0x0); + + /* + * Set LINK_ENABLE = 1 (0x7000) to enable SOC DP link training, + * enable SOC video output to the serializer. + */ + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_ENABLE, + MAX_GMSL_DP_SER_LINK_ENABLE_MASK, 0x1); + + queue_delayed_work(priv->wq, &priv->delay_work, + msecs_to_jiffies(500)); + + return ret; +} + +static int max_gmsl_dp_ser_parse_dt(struct i2c_client *client, + struct max_gmsl_dp_ser_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct device_node *ser = dev->of_node; + int err = 0; + u32 val = 0; + + dev_info(dev, "%s: parsing serializer device tree:\n", __func__); + + err = of_property_read_u32(ser, "dprx-lane-count", &val); + if (err) { + if (err == -EINVAL) { + dev_info(dev, "%s: - dprx-lane-count property not found\n", + __func__); + /* default value: 4 */ + priv->dprx_lane_count = 4; + dev_info(dev, "%s: dprx-lane-count set to default val: 4\n", + __func__); + } else { + return err; + } + } else { + /* set dprx-lane-count */ + priv->dprx_lane_count = val; + dev_info(dev, "%s: - dprx-lane-count %i\n", __func__, val); + } + + err = of_property_read_u32(ser, "dprx-link-rate", &val); + if (err) { + if (err == -EINVAL) { + dev_info(dev, "%s: - dprx-link-rate property not found\n", + __func__); + /* default value: 0x1E */ + priv->dprx_link_rate = 0x1E; + dev_info(dev, "%s: dprx-link-rate set to default val: 0x1E\n", + __func__); + } else { + return err; + } + } else { + /* set dprx-link-rate*/ + priv->dprx_link_rate = val; + dev_info(dev, "%s: - dprx-link-rate %i\n", __func__, val); + } + + return 0; +} + +static int max_gmsl_dp_ser_probe(struct i2c_client *client) +{ + struct max_gmsl_dp_ser_priv *priv; + struct device *dev; + struct device_node *ser = client->dev.of_node; + int ret; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + priv->ser_data = of_device_get_match_data(&client->dev); + if (!priv->ser_data) { + dev_err(&client->dev, "unsupported tegra\n"); + return -ENODEV; + } + mutex_init(&priv->mutex); + + priv->client = client; + i2c_set_clientdata(client, priv); + + priv->regmap = devm_regmap_init_i2c(client, &max_gmsl_dp_ser_i2c_regmap); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + ret = max_gmsl_dp_ser_parse_dt(client, priv); + if (ret < 0) { + dev_err(dev, "%s: error parsing device tree\n", __func__); + return -EFAULT; + } + + priv->wq = alloc_workqueue("tegra_poll_gmsl_training_lock", WQ_HIGHPRI, 0); + INIT_DELAYED_WORK(&priv->delay_work, tegra_poll_gmsl_training_lock); + + ret = max_gmsl_dp_ser_init(&client->dev); + if (ret < 0) { + dev_err(dev, "%s: dp serializer init failed\n", __func__); + return -EFAULT; + } + + /* enable INTR8.LOSS_OF_LOCK_OEN */ + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_INTR8, + MAX_GMSL_DP_SER_INTR8_MASK, + MAX_GMSL_DP_SER_INTR8_VAL); + + priv->ser_errb = of_get_named_gpio(ser, "ser-errb", 0); + if (gpio_is_valid(priv->ser_errb)) { + priv->ser_irq = gpio_to_irq(priv->ser_errb); + ret = request_threaded_irq(priv->ser_irq, + max_gsml_dp_ser_irq_handler, + NULL, IRQF_TRIGGER_RISING + | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "SER", priv); + } + return ret; +} + +static int max_gmsl_dp_ser_remove(struct i2c_client *client) +{ + struct max_gmsl_dp_ser_priv *priv = i2c_get_clientdata(client); + + i2c_unregister_device(client); + gpiod_set_value_cansleep(priv->gpiod_pwrdn, 0); + + return 0; +} + +static struct max_gmsl_dp_ser_data tegra234_max_gmsl_ser_data = { + .has_link_a = true, + .has_link_b = false, +}; + +static const struct of_device_id max_gmsl_dp_ser_dt_ids[] = { + { .compatible = "maxim,max_gmsl_dp_ser", + .data = &tegra234_max_gmsl_ser_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, max_gmsl_dp_ser_dt_ids); + +static struct i2c_driver max_gmsl_dp_ser_i2c_driver = { + .driver = { + .name = "max_gmsl_dp_ser", + .of_match_table = of_match_ptr(max_gmsl_dp_ser_dt_ids), + }, + .probe_new = max_gmsl_dp_ser_probe, + .remove = max_gmsl_dp_ser_remove, +}; + +module_i2c_driver(max_gmsl_dp_ser_i2c_driver); + +MODULE_DESCRIPTION("Maxim DP GMSL Serializer Driver"); +MODULE_AUTHOR("Vishwaroop"); +MODULE_LICENSE("GPL"); From fd94f1e4e4505d8dd62f89ee0bc129e149fdbcdc Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Mon, 6 Dec 2021 19:33:21 +0000 Subject: [PATCH 02/22] dc: bridge: add link select option for serializer Fetch the details of the VID_LINK_SEL from device tree and program the VIDEO_TX0 registers based on the specified link in the device tree. Change-Id: I875ec719bc0fe3ab47772346290938ec79a9ce82 Signed-off-by: Vishwaroop A Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2637509 Tested-by: mobile promotions Reviewed-by: svcacv Reviewed-by: Shu Zhong Reviewed-by: svc_kernel_abi Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- .../dc/bridge/maxim_gmsl_dp_serializer.c | 140 ++++++++++-------- 1 file changed, 77 insertions(+), 63 deletions(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index fa40a65d..0fd1dbe3 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -26,31 +26,30 @@ #define MAX_GMSL_DP_SER_CTRL3_LOCK_VAL (1 << 3) #define MAX_GMSL_DP_SER_INTR8 0x20 -#define MAX_GMSL_DP_SER_INTR8_MASK 0x1 +#define MAX_GMSL_DP_SER_INTR8_MASK (1 << 0) #define MAX_GMSL_DP_SER_INTR8_VAL 0x1 #define MAX_GMSL_DP_SER_LINK_CTRL_PHY_A 0x29 -#define MAX_GMSL_DP_SER_LINK_CTRL_A_MASK 0x1 +#define MAX_GMSL_DP_SER_LINK_CTRL_A_MASK (1 << 0) #define MAX_GMSL_DP_SER_LCTRL2_A 0x2A +#define MAX_GMSL_DP_SER_LCTRL2_B 0x34 #define MAX_GMSL_DP_SER_LCTRL2_LOCK_MASK (1 << 0) -#define MAX_GMSL_DP_SER_LCTRL2_LOCK_VAL (1 << 0) +#define MAX_GMSL_DP_SER_LCTRL2_LOCK_VAL 0x1 #define MAX_GMSL_DP_SER_LINK_CTRL_PHY_B 0x33 -#define MAX_GMSL_DP_SER_LINK_CTRL_B_MASK 0x1 - -#define MAX_GMSL_DP_SER_LCTRL2_B 0x34 -#define MAX_GMSL_DP_SER_LCTRL2_LOCK_B_MASK (1 << 0) -#define MAX_GMSL_DP_SER_LCTRL2_LOCK_B_VAL (1 << 0) +#define MAX_GMSL_DP_SER_LINK_CTRL_B_MASK (1 << 0) #define MAX_GMSL_DP_SER_VID_TX_X 0x100 -#define MAX_GMSL_DP_SER_VID_TX_X_MASK 0x1 #define MAX_GMSL_DP_SER_VID_TX_Y 0x110 -#define MAX_GMSL_DP_SER_VID_TX_Y_MASK 0x1 #define MAX_GMSL_DP_SER_VID_TX_Z 0x120 -#define MAX_GMSL_DP_SER_VID_TX_Z_MASK 0x1 #define MAX_GMSL_DP_SER_VID_TX_U 0x130 -#define MAX_GMSL_DP_SER_VID_TX_U_MASK 0x1 +#define MAX_GMSL_DP_SER_ENABLE_LINK_A 0x0 +#define MAX_GMSL_DP_SER_ENABLE_LINK_B 0x1 +#define MAX_GMSL_DP_SER_ENABLE_LINK_AB 0x2 + +#define MAX_GMSL_DP_SER_VID_TX_MASK (1 << 0) +#define MAX_GMSL_DP_SER_VID_TX_LINK_MASK (3 << 1) #define MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B0 0x6064 #define MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B1 0x6065 @@ -62,11 +61,11 @@ #define MAX_GMSL_DP_SER_PHY_EDP_3_CTRL0_B1 0x6365 #define MAX_GMSL_DP_SER_DPRX_TRAIN 0x641A -#define MAX_GMSL_DP_SER_DPRX_TRAIN_STATE_MASK 0xF0 +#define MAX_GMSL_DP_SER_DPRX_TRAIN_STATE_MASK (0xF << 4) #define MAX_GMSL_DP_SER_DPRX_TRAIN_STATE_VAL 0xF0 #define MAX_GMSL_DP_SER_LINK_ENABLE 0x7000 -#define MAX_GMSL_DP_SER_LINK_ENABLE_MASK 0x1 +#define MAX_GMSL_DP_SER_LINK_ENABLE_MASK (1 << 0) #define MAX_GMSL_DP_SER_MISC_CONFIG_B1 0x7019 #define MAX_GMSL_DP_SER_MAX_LINK_COUNT 0x7070 @@ -75,14 +74,9 @@ #define MAX_GMSL_DP_SER_LOCAL_EDID 0x7084 #define MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY 0x70A4 -#define MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_MASK 0x3F +#define MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_MASK (0x3F << 0) #define MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_100KBPS 0x8 -struct max_gmsl_dp_ser_data { - bool has_link_a; - bool has_link_b; -}; - struct max_gmsl_dp_ser_source { struct fwnode_handle *fwnode; }; @@ -97,6 +91,7 @@ struct max_gmsl_dp_ser_priv { struct gpio_desc *gpiod_pwrdn; u8 dprx_lane_count; u8 dprx_link_rate; + u8 gmsl_link_select; struct mutex mutex; struct regmap *regmap; struct work_struct work; @@ -104,7 +99,6 @@ struct max_gmsl_dp_ser_priv { struct workqueue_struct *wq; int ser_errb; unsigned int ser_irq; - const struct max_gmsl_dp_ser_data *ser_data; }; static int max_gmsl_dp_ser_read(struct max_gmsl_dp_ser_priv *priv, int reg) @@ -164,6 +158,19 @@ static void max_gmsl_dp_ser_sst_setup(struct max_gmsl_dp_ser_priv *priv) max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_MAX_LINK_COUNT, priv->dprx_lane_count); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_X, + MAX_GMSL_DP_SER_VID_TX_LINK_MASK, + priv->gmsl_link_select); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Y, + MAX_GMSL_DP_SER_VID_TX_LINK_MASK, + priv->gmsl_link_select); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Z, + MAX_GMSL_DP_SER_VID_TX_LINK_MASK, + priv->gmsl_link_select); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_U, + MAX_GMSL_DP_SER_VID_TX_LINK_MASK, + priv->gmsl_link_select); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY, MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_MASK, MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_100KBPS); @@ -194,6 +201,7 @@ static void tegra_poll_gmsl_training_lock(struct work_struct *work) struct max_gmsl_dp_ser_priv *priv = container_of(dwork, struct max_gmsl_dp_ser_priv, delay_work); int ret = 0; + u32 lctrl_reg = 0; ret = max_gmsl_read_lock(priv, MAX_GMSL_DP_SER_CTRL3, MAX_GMSL_DP_SER_CTRL3_LOCK_MASK, @@ -203,29 +211,23 @@ static void tegra_poll_gmsl_training_lock(struct work_struct *work) goto reschedule; } - if (priv->ser_data->has_link_a) { - ret = max_gmsl_read_lock(priv, - MAX_GMSL_DP_SER_LCTRL2_A, - MAX_GMSL_DP_SER_LCTRL2_LOCK_MASK, - MAX_GMSL_DP_SER_LCTRL2_LOCK_VAL); - - if (ret < 0) { - dev_dbg(&priv->client->dev, - "GMSL Lock is not set for LINK A\n"); - goto reschedule; - } + if (priv->gmsl_link_select == MAX_GMSL_DP_SER_ENABLE_LINK_A) { + lctrl_reg = MAX_GMSL_DP_SER_LCTRL2_A; + dev_dbg(&priv->client->dev, + "GMSL Lock is being set for Link A\n"); + } else { + lctrl_reg = MAX_GMSL_DP_SER_LCTRL2_B; + dev_dbg(&priv->client->dev, + "GMSL Lock is being set for Link B\n"); } - if (priv->ser_data->has_link_b) { - ret = max_gmsl_read_lock(priv, - MAX_GMSL_DP_SER_LCTRL2_B, - MAX_GMSL_DP_SER_LCTRL2_LOCK_B_MASK, - MAX_GMSL_DP_SER_LCTRL2_LOCK_B_VAL); - if (ret < 0) { - dev_dbg(&priv->client->dev, - "GSM Lock is not set for LINK B\n"); - goto reschedule; - } + ret = max_gmsl_read_lock(priv, lctrl_reg, + MAX_GMSL_DP_SER_LCTRL2_LOCK_MASK, + MAX_GMSL_DP_SER_LCTRL2_LOCK_VAL); + + if (ret < 0) { + dev_dbg(&priv->client->dev, "GMSL Lock set failed\n"); + goto reschedule; } ret = max_gmsl_read_lock(priv, MAX_GMSL_DP_SER_DPRX_TRAIN, @@ -238,13 +240,14 @@ static void tegra_poll_gmsl_training_lock(struct work_struct *work) } max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_X, - MAX_GMSL_DP_SER_VID_TX_X_MASK, 0x1); + MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Y, - MAX_GMSL_DP_SER_VID_TX_Y_MASK, 0x1); + MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Z, - MAX_GMSL_DP_SER_VID_TX_Z_MASK, 0x1); + MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_U, - MAX_GMSL_DP_SER_VID_TX_U_MASK, 0x1); + MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); + return; reschedule: @@ -289,13 +292,13 @@ static int max_gmsl_dp_ser_init(struct device *dev) * for Pipe X, Y, Z and U */ max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_X, - MAX_GMSL_DP_SER_VID_TX_X_MASK, 0x0); + MAX_GMSL_DP_SER_VID_TX_MASK, 0x0); max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Y, - MAX_GMSL_DP_SER_VID_TX_Y_MASK, 0x0); + MAX_GMSL_DP_SER_VID_TX_MASK, 0x0); max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Z, - MAX_GMSL_DP_SER_VID_TX_Z_MASK, 0x0); + MAX_GMSL_DP_SER_VID_TX_MASK, 0x0); max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_U, - MAX_GMSL_DP_SER_VID_TX_U_MASK, 0x0); + MAX_GMSL_DP_SER_VID_TX_MASK, 0x0); /* * Set LINK_ENABLE=0 (0x7000) to force the DP HPD @@ -375,6 +378,29 @@ static int max_gmsl_dp_ser_parse_dt(struct i2c_client *client, dev_info(dev, "%s: - dprx-link-rate %i\n", __func__, val); } + err = of_property_read_u32(ser, "gmsl-link-select", &val); + if (err) { + if (err == -EINVAL) { + dev_info(dev, "%s: - link-select property not found\n", + __func__); + priv->gmsl_link_select = 0x0; + dev_info(dev, "%s: link-select set to LINK A: 0x0\n", + __func__); + } else { + return err; + } + } else { + if ((val != MAX_GMSL_DP_SER_ENABLE_LINK_A) && + (val != MAX_GMSL_DP_SER_ENABLE_LINK_B)) { + dev_err(dev, "%s: Invalid gmsl-link-select %i\n", + __func__, val); + return -EINVAL; + } + /* set link-select*/ + priv->gmsl_link_select = val; + dev_info(dev, "%s: - link-select %i\n", + __func__, val); + } return 0; } @@ -389,11 +415,6 @@ static int max_gmsl_dp_ser_probe(struct i2c_client *client) if (priv == NULL) return -ENOMEM; - priv->ser_data = of_device_get_match_data(&client->dev); - if (!priv->ser_data) { - dev_err(&client->dev, "unsupported tegra\n"); - return -ENODEV; - } mutex_init(&priv->mutex); priv->client = client; @@ -445,15 +466,8 @@ static int max_gmsl_dp_ser_remove(struct i2c_client *client) return 0; } -static struct max_gmsl_dp_ser_data tegra234_max_gmsl_ser_data = { - .has_link_a = true, - .has_link_b = false, -}; - static const struct of_device_id max_gmsl_dp_ser_dt_ids[] = { - { .compatible = "maxim,max_gmsl_dp_ser", - .data = &tegra234_max_gmsl_ser_data, - }, + { .compatible = "maxim,max_gmsl_dp_ser" }, {}, }; MODULE_DEVICE_TABLE(of, max_gmsl_dp_ser_dt_ids); From 9cf42b295fb379883deaee09a3333c192794eb4c Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Thu, 9 Dec 2021 02:12:42 +0000 Subject: [PATCH 03/22] dc: bridge: update link_select shift value Add a macro to handle the shift value for link select. Change-Id: Id43090c3d9b3b72cae22286f2d1c85548e8b5a63 Signed-off-by: Vishwaroop A Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2639102 Tested-by: mobile promotions Reviewed-by: svcacv Reviewed-by: Shu Zhong Reviewed-by: svc_kernel_abi Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- .../tegra/dc/bridge/maxim_gmsl_dp_serializer.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index 0fd1dbe3..56a386d7 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -50,6 +50,7 @@ #define MAX_GMSL_DP_SER_VID_TX_MASK (1 << 0) #define MAX_GMSL_DP_SER_VID_TX_LINK_MASK (3 << 1) +#define MAX_GMSL_DP_SER_LINK_SEL_SHIFT_VAL 0x1 #define MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B0 0x6064 #define MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B1 0x6065 @@ -138,6 +139,8 @@ static inline void max_gmsl_dp_ser_update(struct max_gmsl_dp_ser_priv *priv, static void max_gmsl_dp_ser_sst_setup(struct max_gmsl_dp_ser_priv *priv) { + u8 gmsl_link_select_value = 0; + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B0, 0x0f); max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B1, 0x0f); max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_1_CTRL0_B0, 0x0f); @@ -158,18 +161,21 @@ static void max_gmsl_dp_ser_sst_setup(struct max_gmsl_dp_ser_priv *priv) max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_MAX_LINK_COUNT, priv->dprx_lane_count); + gmsl_link_select_value = (priv->gmsl_link_select << + MAX_GMSL_DP_SER_LINK_SEL_SHIFT_VAL); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_X, MAX_GMSL_DP_SER_VID_TX_LINK_MASK, - priv->gmsl_link_select); + gmsl_link_select_value); max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Y, MAX_GMSL_DP_SER_VID_TX_LINK_MASK, - priv->gmsl_link_select); + gmsl_link_select_value); max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Z, MAX_GMSL_DP_SER_VID_TX_LINK_MASK, - priv->gmsl_link_select); + gmsl_link_select_value); max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_U, MAX_GMSL_DP_SER_VID_TX_LINK_MASK, - priv->gmsl_link_select); + gmsl_link_select_value); max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY, MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_MASK, From 3b68f46c898d07f5ec2365c489770d29b6af7520 Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Thu, 9 Dec 2021 18:40:01 +0000 Subject: [PATCH 04/22] dc: bridge: add errb support for serializer Enable the errb support by enabling the gpio based interrupts. Change-Id: Ifbe995df44211fe38cb15fa6e4df225e25e34156 Signed-off-by: Vishwaroop A Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2639566 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Shu Zhong Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- .../dc/bridge/maxim_gmsl_dp_serializer.c | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index 56a386d7..402d3f81 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -29,6 +29,9 @@ #define MAX_GMSL_DP_SER_INTR8_MASK (1 << 0) #define MAX_GMSL_DP_SER_INTR8_VAL 0x1 +#define MAX_GMSL_DP_SER_INTR9 0x21 +#define MAX_GMSL_DP_SER_LOSS_OF_LOCK_FLAG (1 << 0) + #define MAX_GMSL_DP_SER_LINK_CTRL_PHY_A 0x29 #define MAX_GMSL_DP_SER_LINK_CTRL_A_MASK (1 << 0) @@ -197,6 +200,16 @@ static int max_gmsl_read_lock(struct max_gmsl_dp_ser_priv *priv, static irqreturn_t max_gsml_dp_ser_irq_handler(int irq, void *dev_id) { + struct max_gmsl_dp_ser_priv *priv = dev_id; + int ret = 0; + struct device *dev = &priv->client->dev; + + ret = max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_INTR9); + if (ret & MAX_GMSL_DP_SER_LOSS_OF_LOCK_FLAG) + dev_dbg(dev, "%s: Fault due to GMSL Link Loss\n", __func__); + + dev_dbg(dev, "%s: Sticky bit LOSS_OF_LOCK_FLAG cleared\n", __func__); + return IRQ_HANDLED; } @@ -320,10 +333,14 @@ static int max_gmsl_dp_ser_init(struct device *dev) * Write RESET_LINK = 0 (for both Phy A, 0x29, and Phy B, 0x33) * to initiate the GMSL link lock process. */ - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_CTRL_PHY_A, - MAX_GMSL_DP_SER_LINK_CTRL_A_MASK, 0x0); - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_CTRL_PHY_B, - MAX_GMSL_DP_SER_LINK_CTRL_B_MASK, 0x0); + if (priv->gmsl_link_select == MAX_GMSL_DP_SER_ENABLE_LINK_A) + max_gmsl_dp_ser_update(priv, + MAX_GMSL_DP_SER_LINK_CTRL_PHY_A, + MAX_GMSL_DP_SER_LINK_CTRL_A_MASK, 0x0); + else + max_gmsl_dp_ser_update(priv, + MAX_GMSL_DP_SER_LINK_CTRL_PHY_B, + MAX_GMSL_DP_SER_LINK_CTRL_B_MASK, 0x0); /* * Set LINK_ENABLE = 1 (0x7000) to enable SOC DP link training, @@ -445,19 +462,38 @@ static int max_gmsl_dp_ser_probe(struct i2c_client *client) return -EFAULT; } + ret = max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_INTR9); + if (ret < 0) { + dev_err(dev, "%s: INTR9 register read failed\n", __func__); + return -EFAULT; + } /* enable INTR8.LOSS_OF_LOCK_OEN */ max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_INTR8, MAX_GMSL_DP_SER_INTR8_MASK, MAX_GMSL_DP_SER_INTR8_VAL); priv->ser_errb = of_get_named_gpio(ser, "ser-errb", 0); + + ret = devm_gpio_request_one(&client->dev, priv->ser_errb, + GPIOF_DIR_IN, "GPIO_MAXIM_SER"); + if (ret < 0) { + dev_err(dev, "%s: GPIO request failed\n ret: %d", + __func__, ret); + return ret; + } + if (gpio_is_valid(priv->ser_errb)) { priv->ser_irq = gpio_to_irq(priv->ser_errb); - ret = request_threaded_irq(priv->ser_irq, + ret = request_threaded_irq(priv->ser_irq, NULL, max_gsml_dp_ser_irq_handler, - NULL, IRQF_TRIGGER_RISING - | IRQF_TRIGGER_FALLING + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "SER", priv); + if (ret < 0) { + dev_err(dev, "%s: Unable to register IRQ handler ret: %d\n", + __func__, ret); + return ret; + } + } return ret; } From 229e044bfed26ce2093c260a02a8332d43418d67 Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Mon, 20 Dec 2021 07:41:09 +0000 Subject: [PATCH 05/22] dc: bridge: Fail the probe if I2C read fails Fail the probe in case if I2C read /write fails to avoid skew of warnings whichs get generated during boot. Bug 3482867 Change-Id: I0a671bb911c7d0241bce430558c55b88a06ca521 Signed-off-by: Vishwaroop A Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2644625 Tested-by: mobile promotions Reviewed-by: mobile promotions --- .../video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index 402d3f81..278b8efa 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -21,6 +21,8 @@ #include #include +#define MAX_GMSL_DP_SER_REG_13 0xD + #define MAX_GMSL_DP_SER_CTRL3 0x13 #define MAX_GMSL_DP_SER_CTRL3_LOCK_MASK (1 << 3) #define MAX_GMSL_DP_SER_CTRL3_LOCK_VAL (1 << 3) @@ -447,6 +449,14 @@ static int max_gmsl_dp_ser_probe(struct i2c_client *client) if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); + ret = max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_REG_13); + if (ret != 0) { + dev_info(dev, "%s: MAXIM Serializer detected\n", __func__); + } else { + dev_err(dev, "%s: MAXIM Serializer Not detected\n", __func__); + return -ENODEV; + } + ret = max_gmsl_dp_ser_parse_dt(client, priv); if (ret < 0) { dev_err(dev, "%s: error parsing device tree\n", __func__); From dd4ada9c908ef3c835aee66dcbbbe9074893befb Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Fri, 28 Jan 2022 05:47:58 +0000 Subject: [PATCH 06/22] drivers: dc: fix Coverity defect Initialize value dev before calling dev_info and initialized value fake_panel_mode. CID 10129386 Bug 3461002 Change-Id: I9d49bbbe39d23deb609005a091d7c1ebb9e7113f Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2660625 Reviewed-by: svcacv Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi Reviewed-by: Sachin Nikam GVS: Gerrit_Virtual_Submit --- drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index 278b8efa..b5f6a2e3 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -2,7 +2,7 @@ /* * MAXIM DP Serializer driver for MAXIM GMSL Serializers * - * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved. */ #include @@ -449,6 +449,8 @@ static int max_gmsl_dp_ser_probe(struct i2c_client *client) if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); + dev = &priv->client->dev; + ret = max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_REG_13); if (ret != 0) { dev_info(dev, "%s: MAXIM Serializer detected\n", __func__); From 2ddc0b4649a47edf1701f76703db6018645a6886 Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Mon, 17 Jan 2022 19:35:09 +0000 Subject: [PATCH 07/22] dc: bridge: Add MST support of serializer ADD MST support for serializer driver Bug 3491734 Change-Id: Id616f58ba4f18bcb341c4ad48ba29562e09739ab Signed-off-by: Vishwaroop A Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2655657 Reviewed-by: Shu Zhong Reviewed-by: svcacv Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- .../dc/bridge/maxim_gmsl_dp_serializer.c | 257 ++++++++++++++---- 1 file changed, 199 insertions(+), 58 deletions(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index b5f6a2e3..45bf42ba 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -20,6 +20,7 @@ #include #include #include +#include #define MAX_GMSL_DP_SER_REG_13 0xD @@ -74,6 +75,8 @@ #define MAX_GMSL_DP_SER_LINK_ENABLE_MASK (1 << 0) #define MAX_GMSL_DP_SER_MISC_CONFIG_B1 0x7019 +#define MAX_GMSL_DP_SER_MISC_CONFIG_B1_MASK (1 << 0) +#define MAX_GMSL_DP_SER_MISC_CONFIG_B1_VAL 0x1 #define MAX_GMSL_DP_SER_MAX_LINK_COUNT 0x7070 #define MAX_GMSL_DP_SER_MAX_LINK_RATE 0x7074 @@ -83,6 +86,19 @@ #define MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_MASK (0x3F << 0) #define MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_100KBPS 0x8 +#define MAX_GMSL_DP_SER_MST_PAYLOAD_ID_0 0x7904 +#define MAX_GMSL_DP_SER_MST_PAYLOAD_ID_1 0x7908 +#define MAX_GMSL_DP_SER_MST_PAYLOAD_ID_2 0x790C +#define MAX_GMSL_DP_SER_MST_PAYLOAD_ID_3 0x7910 + +#define MAX_GMSL_DP_SER_TX3_0 0xA3 +#define MAX_GMSL_DP_SER_TX3_1 0xA7 +#define MAX_GMSL_DP_SER_TX3_2 0xAB +#define MAX_GMSL_DP_SER_TX3_3 0xAF + +#define MAX_GMSL_ARRAY_SIZE 4 + + struct max_gmsl_dp_ser_source { struct fwnode_handle *fwnode; }; @@ -97,7 +113,6 @@ struct max_gmsl_dp_ser_priv { struct gpio_desc *gpiod_pwrdn; u8 dprx_lane_count; u8 dprx_link_rate; - u8 gmsl_link_select; struct mutex mutex; struct regmap *regmap; struct work_struct work; @@ -105,6 +120,12 @@ struct max_gmsl_dp_ser_priv { struct workqueue_struct *wq; int ser_errb; unsigned int ser_irq; + bool enable_mst; + u8 mst_payload_ids[MAX_GMSL_ARRAY_SIZE]; + u8 gmsl_stream_ids[MAX_GMSL_ARRAY_SIZE]; + u8 gmsl_link_select[MAX_GMSL_ARRAY_SIZE]; + bool link_a_is_enabled; + bool link_b_is_enabled; }; static int max_gmsl_dp_ser_read(struct max_gmsl_dp_ser_priv *priv, int reg) @@ -142,9 +163,50 @@ static inline void max_gmsl_dp_ser_update(struct max_gmsl_dp_ser_priv *priv, max_gmsl_dp_ser_write(priv, reg, update_val); } -static void max_gmsl_dp_ser_sst_setup(struct max_gmsl_dp_ser_priv *priv) +static void max_gmsl_dp_ser_mst_setup(struct max_gmsl_dp_ser_priv *priv) { + int i; + static const int max_mst_payload_id_reg[] = { + MAX_GMSL_DP_SER_MST_PAYLOAD_ID_0, + MAX_GMSL_DP_SER_MST_PAYLOAD_ID_1, + MAX_GMSL_DP_SER_MST_PAYLOAD_ID_2, + MAX_GMSL_DP_SER_MST_PAYLOAD_ID_3, + }; + static const int max_gmsl_stream_id_regs[] = { + MAX_GMSL_DP_SER_TX3_0, + MAX_GMSL_DP_SER_TX3_1, + MAX_GMSL_DP_SER_TX3_2, + MAX_GMSL_DP_SER_TX3_3, + }; + + /* enable MST by programming MISC_CONFIG_B1 reg */ + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_MISC_CONFIG_B1, + MAX_GMSL_DP_SER_MISC_CONFIG_B1_MASK, + MAX_GMSL_DP_SER_MISC_CONFIG_B1_VAL); + + /* program MST payload IDs */ + for (i = 0; i < ARRAY_SIZE(priv->mst_payload_ids); i++) { + max_gmsl_dp_ser_write(priv, max_mst_payload_id_reg[i], + priv->mst_payload_ids[i]); + } + + /* Program stream IDs */ + for (i = 0; i < ARRAY_SIZE(priv->gmsl_stream_ids); i++) { + max_gmsl_dp_ser_write(priv, max_gmsl_stream_id_regs[i], + priv->gmsl_stream_ids[i]); + } +} + +static void max_gmsl_dp_ser_setup(struct max_gmsl_dp_ser_priv *priv) +{ + int i; u8 gmsl_link_select_value = 0; + static const int max_gmsl_ser_vid_tx_regs[] = { + MAX_GMSL_DP_SER_VID_TX_X, + MAX_GMSL_DP_SER_VID_TX_Y, + MAX_GMSL_DP_SER_VID_TX_Z, + MAX_GMSL_DP_SER_VID_TX_U, + }; max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B0, 0x0f); max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B1, 0x0f); @@ -166,25 +228,35 @@ static void max_gmsl_dp_ser_sst_setup(struct max_gmsl_dp_ser_priv *priv) max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_MAX_LINK_COUNT, priv->dprx_lane_count); - gmsl_link_select_value = (priv->gmsl_link_select << - MAX_GMSL_DP_SER_LINK_SEL_SHIFT_VAL); - - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_X, - MAX_GMSL_DP_SER_VID_TX_LINK_MASK, - gmsl_link_select_value); - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Y, - MAX_GMSL_DP_SER_VID_TX_LINK_MASK, - gmsl_link_select_value); - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Z, - MAX_GMSL_DP_SER_VID_TX_LINK_MASK, - gmsl_link_select_value); - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_U, - MAX_GMSL_DP_SER_VID_TX_LINK_MASK, - gmsl_link_select_value); + for (i = 0; i < MAX_GMSL_ARRAY_SIZE; i++) { + gmsl_link_select_value = (priv->gmsl_link_select[i] << + MAX_GMSL_DP_SER_LINK_SEL_SHIFT_VAL); + max_gmsl_dp_ser_update(priv, max_gmsl_ser_vid_tx_regs[i], + MAX_GMSL_DP_SER_VID_TX_LINK_MASK, + gmsl_link_select_value); + } max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY, MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_MASK, MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_100KBPS); + + if (priv->enable_mst) + max_gmsl_dp_ser_mst_setup(priv); +} + +static bool max_gmsl_dp_ser_check_dups(u8 *ids) +{ + int i = 0, j = 0; + + /* check if IDs are unique */ + for (i = 0; i < MAX_GMSL_ARRAY_SIZE; i++) { + for (j = i + 1; j < MAX_GMSL_ARRAY_SIZE; j++) { + if (ids[i] == ids[j]) + return false; + } + } + + return true; } static int max_gmsl_read_lock(struct max_gmsl_dp_ser_priv *priv, @@ -222,7 +294,6 @@ static void tegra_poll_gmsl_training_lock(struct work_struct *work) struct max_gmsl_dp_ser_priv *priv = container_of(dwork, struct max_gmsl_dp_ser_priv, delay_work); int ret = 0; - u32 lctrl_reg = 0; ret = max_gmsl_read_lock(priv, MAX_GMSL_DP_SER_CTRL3, MAX_GMSL_DP_SER_CTRL3_LOCK_MASK, @@ -232,23 +303,24 @@ static void tegra_poll_gmsl_training_lock(struct work_struct *work) goto reschedule; } - if (priv->gmsl_link_select == MAX_GMSL_DP_SER_ENABLE_LINK_A) { - lctrl_reg = MAX_GMSL_DP_SER_LCTRL2_A; - dev_dbg(&priv->client->dev, - "GMSL Lock is being set for Link A\n"); - } else { - lctrl_reg = MAX_GMSL_DP_SER_LCTRL2_B; - dev_dbg(&priv->client->dev, - "GMSL Lock is being set for Link B\n"); + if (priv->link_a_is_enabled) { + ret = max_gmsl_read_lock(priv, MAX_GMSL_DP_SER_LCTRL2_A, + MAX_GMSL_DP_SER_LCTRL2_LOCK_MASK, + MAX_GMSL_DP_SER_LCTRL2_LOCK_VAL); + if (ret < 0) { + dev_dbg(&priv->client->dev, "GMSL Lock set failed for Link A\n"); + goto reschedule; + } } - ret = max_gmsl_read_lock(priv, lctrl_reg, - MAX_GMSL_DP_SER_LCTRL2_LOCK_MASK, - MAX_GMSL_DP_SER_LCTRL2_LOCK_VAL); - - if (ret < 0) { - dev_dbg(&priv->client->dev, "GMSL Lock set failed\n"); - goto reschedule; + if (priv->link_b_is_enabled) { + ret = max_gmsl_read_lock(priv, MAX_GMSL_DP_SER_LCTRL2_B, + MAX_GMSL_DP_SER_LCTRL2_LOCK_MASK, + MAX_GMSL_DP_SER_LCTRL2_LOCK_VAL); + if (ret < 0) { + dev_dbg(&priv->client->dev, "GMSL Lock set failed for Link B\n"); + goto reschedule; + } } ret = max_gmsl_read_lock(priv, MAX_GMSL_DP_SER_DPRX_TRAIN, @@ -329,20 +401,22 @@ static int max_gmsl_dp_ser_init(struct device *dev) max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_ENABLE, MAX_GMSL_DP_SER_LINK_ENABLE_MASK, 0x0); - max_gmsl_dp_ser_sst_setup(priv); + max_gmsl_dp_ser_setup(priv); /* * Write RESET_LINK = 0 (for both Phy A, 0x29, and Phy B, 0x33) * to initiate the GMSL link lock process. */ - if (priv->gmsl_link_select == MAX_GMSL_DP_SER_ENABLE_LINK_A) + if (priv->link_a_is_enabled) max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_CTRL_PHY_A, - MAX_GMSL_DP_SER_LINK_CTRL_A_MASK, 0x0); - else + MAX_GMSL_DP_SER_LINK_CTRL_A_MASK, + 0x0); + if (priv->link_b_is_enabled) max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_CTRL_PHY_B, - MAX_GMSL_DP_SER_LINK_CTRL_B_MASK, 0x0); + MAX_GMSL_DP_SER_LINK_CTRL_B_MASK, + 0x0); /* * Set LINK_ENABLE = 1 (0x7000) to enable SOC DP link training, @@ -357,12 +431,69 @@ static int max_gmsl_dp_ser_init(struct device *dev) return ret; } +static int max_gmsl_dp_ser_parse_mst_props(struct i2c_client *client, + struct max_gmsl_dp_ser_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct device_node *ser = dev->of_node; + int err = 0; + bool ret; + + priv->enable_mst = of_property_read_bool(ser, + "enable-mst"); + if (priv->enable_mst) + dev_info(dev, "%s: MST mode enabled:\n", __func__); + else + dev_info(dev, "%s: MST mode not enabled:\n", __func__); + + if (priv->enable_mst) { + err = of_property_read_variable_u8_array(ser, + "mst-payload-ids", + priv->mst_payload_ids, 1, + ARRAY_SIZE(priv->mst_payload_ids)); + + if (err < 0) { + dev_info(dev, + "%s: MST Payload prop not found or invalid\n", + __func__); + return -EINVAL; + } + + ret = max_gmsl_dp_ser_check_dups(priv->mst_payload_ids); + if (!ret) { + dev_err(dev, "%s: payload IDs are not unique\n", + __func__); + return -EINVAL; + } + + err = of_property_read_variable_u8_array(ser, + "gmsl-stream-ids", + priv->gmsl_stream_ids, 1, + ARRAY_SIZE(priv->gmsl_stream_ids)); + if (err < 0) { + dev_info(dev, + "%s: GMSL Stream ID property not found or invalid\n", + __func__); + return -EINVAL; + } + + ret = max_gmsl_dp_ser_check_dups(priv->gmsl_stream_ids); + if (!ret) { + dev_err(dev, "%s: stream IDs are not unique\n", + __func__); + return -EINVAL; + } + } + + return 0; +} + static int max_gmsl_dp_ser_parse_dt(struct i2c_client *client, struct max_gmsl_dp_ser_priv *priv) { struct device *dev = &priv->client->dev; struct device_node *ser = dev->of_node; - int err = 0; + int err = 0, i; u32 val = 0; dev_info(dev, "%s: parsing serializer device tree:\n", __func__); @@ -403,29 +534,39 @@ static int max_gmsl_dp_ser_parse_dt(struct i2c_client *client, dev_info(dev, "%s: - dprx-link-rate %i\n", __func__, val); } - err = of_property_read_u32(ser, "gmsl-link-select", &val); - if (err) { - if (err == -EINVAL) { - dev_info(dev, "%s: - link-select property not found\n", - __func__); - priv->gmsl_link_select = 0x0; - dev_info(dev, "%s: link-select set to LINK A: 0x0\n", - __func__); + err = of_property_read_variable_u8_array(ser, "gmsl-link-select", + priv->gmsl_link_select, 1, + ARRAY_SIZE(priv->gmsl_link_select)); + if (err < 0) { + dev_info(dev, + "%s: GMSL Link select property not found or invalid\n", + __func__); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(priv->gmsl_link_select); i++) { + if ((priv->gmsl_link_select[i] == MAX_GMSL_DP_SER_ENABLE_LINK_A) + || (priv->gmsl_link_select[i] == + MAX_GMSL_DP_SER_ENABLE_LINK_AB)) { + priv->link_a_is_enabled = true; + } else if ((priv->gmsl_link_select[i] == MAX_GMSL_DP_SER_ENABLE_LINK_B) + || (priv->gmsl_link_select[i] == + MAX_GMSL_DP_SER_ENABLE_LINK_AB)) { + priv->link_b_is_enabled = true; } else { - return err; - } - } else { - if ((val != MAX_GMSL_DP_SER_ENABLE_LINK_A) && - (val != MAX_GMSL_DP_SER_ENABLE_LINK_B)) { - dev_err(dev, "%s: Invalid gmsl-link-select %i\n", - __func__, val); + dev_info(dev, + "%s: GMSL Link select values are invalid\n", + __func__); return -EINVAL; } - /* set link-select*/ - priv->gmsl_link_select = val; - dev_info(dev, "%s: - link-select %i\n", - __func__, val); } + + err = max_gmsl_dp_ser_parse_mst_props(client, priv); + if (err != 0) { + dev_err(dev, "%s: error parsing MST props\n", __func__); + return -EFAULT; + } + return 0; } From 4088261539038bbfd5d5f4ba0d3ff76858c7c3df Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Mon, 16 May 2022 11:16:55 +0000 Subject: [PATCH 08/22] dc: serializer: fix dt progamming in driver Device tree has been updated to use <0x0 0x0 0x1 0x1> instead of /bits/ 8 <0x0 0x0 0x1 0x1>. Fix the device tree parsing logic to fetch the data correctly. Bug 3645731 Change-Id: I51ce1194ffb4c71526d1d74a3a5dbbcfe2f18a51 Signed-off-by: Vishwaroop A Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2713103 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Shu Zhong GVS: Gerrit_Virtual_Submit --- .../tegra/dc/bridge/maxim_gmsl_dp_serializer.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index 45bf42ba..68f2beb4 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -121,9 +121,9 @@ struct max_gmsl_dp_ser_priv { int ser_errb; unsigned int ser_irq; bool enable_mst; - u8 mst_payload_ids[MAX_GMSL_ARRAY_SIZE]; - u8 gmsl_stream_ids[MAX_GMSL_ARRAY_SIZE]; - u8 gmsl_link_select[MAX_GMSL_ARRAY_SIZE]; + u32 mst_payload_ids[MAX_GMSL_ARRAY_SIZE]; + u32 gmsl_stream_ids[MAX_GMSL_ARRAY_SIZE]; + u32 gmsl_link_select[MAX_GMSL_ARRAY_SIZE]; bool link_a_is_enabled; bool link_b_is_enabled; }; @@ -200,7 +200,7 @@ static void max_gmsl_dp_ser_mst_setup(struct max_gmsl_dp_ser_priv *priv) static void max_gmsl_dp_ser_setup(struct max_gmsl_dp_ser_priv *priv) { int i; - u8 gmsl_link_select_value = 0; + u32 gmsl_link_select_value = 0; static const int max_gmsl_ser_vid_tx_regs[] = { MAX_GMSL_DP_SER_VID_TX_X, MAX_GMSL_DP_SER_VID_TX_Y, @@ -244,7 +244,7 @@ static void max_gmsl_dp_ser_setup(struct max_gmsl_dp_ser_priv *priv) max_gmsl_dp_ser_mst_setup(priv); } -static bool max_gmsl_dp_ser_check_dups(u8 *ids) +static bool max_gmsl_dp_ser_check_dups(u32 *ids) { int i = 0, j = 0; @@ -447,7 +447,7 @@ static int max_gmsl_dp_ser_parse_mst_props(struct i2c_client *client, dev_info(dev, "%s: MST mode not enabled:\n", __func__); if (priv->enable_mst) { - err = of_property_read_variable_u8_array(ser, + err = of_property_read_variable_u32_array(ser, "mst-payload-ids", priv->mst_payload_ids, 1, ARRAY_SIZE(priv->mst_payload_ids)); @@ -466,7 +466,7 @@ static int max_gmsl_dp_ser_parse_mst_props(struct i2c_client *client, return -EINVAL; } - err = of_property_read_variable_u8_array(ser, + err = of_property_read_variable_u32_array(ser, "gmsl-stream-ids", priv->gmsl_stream_ids, 1, ARRAY_SIZE(priv->gmsl_stream_ids)); @@ -534,7 +534,7 @@ static int max_gmsl_dp_ser_parse_dt(struct i2c_client *client, dev_info(dev, "%s: - dprx-link-rate %i\n", __func__, val); } - err = of_property_read_variable_u8_array(ser, "gmsl-link-select", + err = of_property_read_variable_u32_array(ser, "gmsl-link-select", priv->gmsl_link_select, 1, ARRAY_SIZE(priv->gmsl_link_select)); if (err < 0) { From 3cffc5689cacbe6405e915b6a5688e6d98b58504 Mon Sep 17 00:00:00 2001 From: Prafull Suryawanshi Date: Wed, 29 Jun 2022 05:43:28 -0400 Subject: [PATCH 09/22] dc: bridge: maxim: add suspend-resume support. This change adds support of suspend and resume functionality to Maxim DP Serializer driver. bug 3685062 JIRA TDS-10310 Change-Id: Idb010948a2af1c7912a177eabb56441fa9d340f6 Signed-off-by: Prafull Suryawanshi Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2741394 Reviewed-by: Shu Zhong GVS: Gerrit_Virtual_Submit (cherry picked from commit f1d3579fd695fb1b78e7a42447355d50906c7269) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2737003 Reviewed-by: svc_kernel_abi --- .../dc/bridge/maxim_gmsl_dp_serializer.c | 63 +++++++++++++++---- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index 68f2beb4..ebcafc1f 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -368,8 +368,8 @@ static int max_gmsl_dp_ser_init(struct device *dev) /* Drive PWRDNB pin high to power up the serializer */ gpiod_set_value_cansleep(priv->gpiod_pwrdn, 1); - /* Wait ~2ms for powerup to complete */ - usleep_range(2000, 2200); + /* Wait ~4ms for powerup to complete */ + usleep_range(4000, 4200); /* * Write RESET_LINK = 1 (for both Phy A, 0x29, and Phy B, 0x33) @@ -428,6 +428,17 @@ static int max_gmsl_dp_ser_init(struct device *dev) queue_delayed_work(priv->wq, &priv->delay_work, msecs_to_jiffies(500)); + + ret = max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_INTR9); + if (ret < 0) { + dev_err(dev, "%s: INTR9 register read failed\n", __func__); + return -EFAULT; + } + /* enable INTR8.LOSS_OF_LOCK_OEN */ + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_INTR8, + MAX_GMSL_DP_SER_INTR8_MASK, + MAX_GMSL_DP_SER_INTR8_VAL); + return ret; } @@ -615,16 +626,6 @@ static int max_gmsl_dp_ser_probe(struct i2c_client *client) return -EFAULT; } - ret = max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_INTR9); - if (ret < 0) { - dev_err(dev, "%s: INTR9 register read failed\n", __func__); - return -EFAULT; - } - /* enable INTR8.LOSS_OF_LOCK_OEN */ - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_INTR8, - MAX_GMSL_DP_SER_INTR8_MASK, - MAX_GMSL_DP_SER_INTR8_VAL); - priv->ser_errb = of_get_named_gpio(ser, "ser-errb", 0); ret = devm_gpio_request_one(&client->dev, priv->ser_errb, @@ -661,6 +662,41 @@ static int max_gmsl_dp_ser_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM +static int max_gmsl_dp_ser_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max_gmsl_dp_ser_priv *priv = i2c_get_clientdata(client); + + /* Drive PWRDNB pin low to power down the serializer */ + gpiod_set_value_cansleep(priv->gpiod_pwrdn, 0); + return 0; +} + +static int max_gmsl_dp_ser_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max_gmsl_dp_ser_priv *priv = i2c_get_clientdata(client); + int ret = 0; + + /* + Drive PWRDNB pin high to power up the serializer + and initialize all registes + */ + ret = max_gmsl_dp_ser_init(&client->dev); + if (ret < 0) { + dev_err(&priv->client->dev, "%s: dp serializer init failed\n", __func__); + } + return ret; +} + +const struct dev_pm_ops max_gmsl_dp_ser_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS( + max_gmsl_dp_ser_suspend, max_gmsl_dp_ser_resume) +}; + +#endif + static const struct of_device_id max_gmsl_dp_ser_dt_ids[] = { { .compatible = "maxim,max_gmsl_dp_ser" }, {}, @@ -671,6 +707,9 @@ static struct i2c_driver max_gmsl_dp_ser_i2c_driver = { .driver = { .name = "max_gmsl_dp_ser", .of_match_table = of_match_ptr(max_gmsl_dp_ser_dt_ids), +#ifdef CONFIG_PM + .pm = &max_gmsl_dp_ser_pm_ops, +#endif }, .probe_new = max_gmsl_dp_ser_probe, .remove = max_gmsl_dp_ser_remove, From 02a15c114a10f6dd06767319c3818876c8b86697 Mon Sep 17 00:00:00 2001 From: prafulls Date: Mon, 25 Jul 2022 15:50:04 +0000 Subject: [PATCH 10/22] video: tegra: bridge: Enable CRC check for MAX Ser This change enables internal video CRC check for Maxim DP Serializer. 1. This sets the bit to enable CRC functionality and then add handlers for ERRB CRC errors. 2. Reading register clears the error. 3. Error can be injected using i2c tool to write error inject bit in register. Below is flow to verify CRC functionality 1. Run GFX application like bubble 2. Read VTX41 register and confirm VID_ASIL_CRC_ERR bit is not set 3. Inject error by setting bit VID_ASIL_INJ_ERR. 4. Verify dmesg errors. bug 3463178 Change-Id: I25c07413eb81bb4b40f35e15b53a4102ec68fa9e Signed-off-by: prafulls Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2751057 Reviewed-by: svc_kernel_abi Reviewed-by: Shu Zhong GVS: Gerrit_Virtual_Submit --- .../dc/bridge/maxim_gmsl_dp_serializer.c | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index ebcafc1f..f26e494a 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -96,6 +96,15 @@ #define MAX_GMSL_DP_SER_TX3_2 0xAB #define MAX_GMSL_DP_SER_TX3_3 0xAF +#define MAX_GMSL_DP_SER_INTERNAL_CRC_X 0x449 +#define MAX_GMSL_DP_SER_INTERNAL_CRC_Y 0x549 +#define MAX_GMSL_DP_SER_INTERNAL_CRC_Z 0x649 +#define MAX_GMSL_DP_SER_INTERNAL_CRC_U 0x749 + +#define MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE 0x9 +#define MAX_GMSL_DP_SER_INTERNAL_CRC_ERR_DET 0x4 +#define MAX_GMSL_DP_SER_INTERNAL_CRC_ERR_INJ 0x10 + #define MAX_GMSL_ARRAY_SIZE 4 @@ -272,6 +281,32 @@ static int max_gmsl_read_lock(struct max_gmsl_dp_ser_priv *priv, return -1; } +static void max_gmsl_detect_internal_crc_error(struct max_gmsl_dp_ser_priv *priv, + struct device *dev) +{ + int i, ret = 0; + + static const int max_gmsl_internal_crc_regs[] = { + MAX_GMSL_DP_SER_INTERNAL_CRC_X, + MAX_GMSL_DP_SER_INTERNAL_CRC_Y, + MAX_GMSL_DP_SER_INTERNAL_CRC_Z, + MAX_GMSL_DP_SER_INTERNAL_CRC_U, + }; + + for (i = 0; i < MAX_GMSL_ARRAY_SIZE; i++) { + ret = max_gmsl_dp_ser_read(priv, max_gmsl_internal_crc_regs[i]); + /* Reading register will clear the detect bit */ + if ((ret & MAX_GMSL_DP_SER_INTERNAL_CRC_ERR_DET) != 0U) { + dev_err(dev, "%s: INTERNAL CRC video error detected at pipe %d\n", __func__, i); + if ((ret & MAX_GMSL_DP_SER_INTERNAL_CRC_ERR_INJ) != 0U) { + /* CRC error is forcefuly injected, disable it */ + ret = ret & (~MAX_GMSL_DP_SER_INTERNAL_CRC_ERR_INJ); + max_gmsl_dp_ser_write(priv, max_gmsl_internal_crc_regs[i], ret); + } + } + } +} + static irqreturn_t max_gsml_dp_ser_irq_handler(int irq, void *dev_id) { struct max_gmsl_dp_ser_priv *priv = dev_id; @@ -282,6 +317,9 @@ static irqreturn_t max_gsml_dp_ser_irq_handler(int irq, void *dev_id) if (ret & MAX_GMSL_DP_SER_LOSS_OF_LOCK_FLAG) dev_dbg(dev, "%s: Fault due to GMSL Link Loss\n", __func__); + /* Detect error for CRC */ + max_gmsl_detect_internal_crc_error(priv, dev); + dev_dbg(dev, "%s: Sticky bit LOSS_OF_LOCK_FLAG cleared\n", __func__); return IRQ_HANDLED; @@ -332,6 +370,16 @@ static void tegra_poll_gmsl_training_lock(struct work_struct *work) goto reschedule; } + /* enable internal CRC after link training */ + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_X, + MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_Y, + MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_Z, + MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_U, + MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_X, MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Y, From 90579e82d5f79eb858954a9a97f067f18ab19a5c Mon Sep 17 00:00:00 2001 From: prafulls Date: Fri, 29 Jul 2022 07:50:01 +0000 Subject: [PATCH 11/22] video: tegra: bridge: Enable CRC check for MAX Ser This change enables external video CRC check for Maxim DP Serializer. This change - 1. Enable video line CRC functionality by setting specific register bit. 2. Add interrupt handler check for remote CRC check failure. 3. Reverse GPIO tunnel and remote error check is already set. 4. If there are CRC error across GMSL link then dmesg spew will indicate it. Verification - 1. Play some GFX application and view on monitor. 2. At DES side, using GUI, For register INTR8, set bit ERR_TX_EN to 1. 3. Clear exiting dmesg using "dmesg --clear" 4. On SER, using i2ccmd tool, Set bit LINE_CRC_EN to 0 at register 0x100. 5. Confirm on dmesg for error message like - "Remote deserializer error detected" bug 3463178 Signed-off-by: prafulls Change-Id: Ieba2b19b7ce1a71173f6d34e61b1607f237cb1a5 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2753202 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi Reviewed-by: Shu Zhong GVS: Gerrit_Virtual_Submit --- .../dc/bridge/maxim_gmsl_dp_serializer.c | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index f26e494a..4f2c49ea 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -28,6 +28,16 @@ #define MAX_GMSL_DP_SER_CTRL3_LOCK_MASK (1 << 3) #define MAX_GMSL_DP_SER_CTRL3_LOCK_VAL (1 << 3) +#define MAX_GMSL_DP_SER_INTR2 0x1A +#define MAX_GMSL_DP_SER_REM_ERR_OEN_A_MASK (1 << 4) +#define MAX_GMSL_DP_SER_REM_ERR_OEN_A_VAL (1 << 4) +#define MAX_GMSL_DP_SER_REM_ERR_OEN_B_MASK (1 << 5) +#define MAX_GMSL_DP_SER_REM_ERR_OEN_B_VAL (1 << 5) + +#define MAX_GMSL_DP_SER_INTR3 0x1B +#define MAX_GMSL_DP_SER_REM_ERR_FLAG_A (1 << 4) +#define MAX_GMSL_DP_SER_REM_ERR_FLAG_B (1 << 5) + #define MAX_GMSL_DP_SER_INTR8 0x20 #define MAX_GMSL_DP_SER_INTR8_MASK (1 << 0) #define MAX_GMSL_DP_SER_INTR8_VAL 0x1 @@ -307,6 +317,29 @@ static void max_gmsl_detect_internal_crc_error(struct max_gmsl_dp_ser_priv *priv } } +/* + * This function is responsible for detecting ANY remote deserializer + * errors. Note that the main error that we're interested in today is + * any video line CRC error reported by the deserializer. + */ +static void max_gmsl_detect_remote_error(struct max_gmsl_dp_ser_priv *priv, + struct device *dev) +{ + int ret = 0; + + ret = max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_INTR3); + + if (priv->link_a_is_enabled) { + if ((ret & MAX_GMSL_DP_SER_REM_ERR_FLAG_A) != 0) + dev_err(dev, "%s: Remote deserializer error detected on Link A\n", __func__); + } + + if (priv->link_b_is_enabled) { + if ((ret & MAX_GMSL_DP_SER_REM_ERR_FLAG_B) != 0) + dev_err(dev, "%s: Remote deserializer error detected on Link B\n", __func__); + } +} + static irqreturn_t max_gsml_dp_ser_irq_handler(int irq, void *dev_id) { struct max_gmsl_dp_ser_priv *priv = dev_id; @@ -317,10 +350,11 @@ static irqreturn_t max_gsml_dp_ser_irq_handler(int irq, void *dev_id) if (ret & MAX_GMSL_DP_SER_LOSS_OF_LOCK_FLAG) dev_dbg(dev, "%s: Fault due to GMSL Link Loss\n", __func__); - /* Detect error for CRC */ + /* Detect internal CRC errors inside serializer */ max_gmsl_detect_internal_crc_error(priv, dev); - dev_dbg(dev, "%s: Sticky bit LOSS_OF_LOCK_FLAG cleared\n", __func__); + /* Detect remote error across GMSL link */ + max_gmsl_detect_remote_error(priv, dev); return IRQ_HANDLED; } @@ -476,12 +510,21 @@ static int max_gmsl_dp_ser_init(struct device *dev) queue_delayed_work(priv->wq, &priv->delay_work, msecs_to_jiffies(500)); - ret = max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_INTR9); if (ret < 0) { dev_err(dev, "%s: INTR9 register read failed\n", __func__); return -EFAULT; } + + if (priv->link_a_is_enabled) + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_INTR2, + MAX_GMSL_DP_SER_REM_ERR_OEN_A_MASK, + MAX_GMSL_DP_SER_REM_ERR_OEN_A_VAL); + if (priv->link_b_is_enabled) + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_INTR2, + MAX_GMSL_DP_SER_REM_ERR_OEN_B_MASK, + MAX_GMSL_DP_SER_REM_ERR_OEN_B_VAL); + /* enable INTR8.LOSS_OF_LOCK_OEN */ max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_INTR8, MAX_GMSL_DP_SER_INTR8_MASK, From 9d71b422fcf2ae63f446d98a53e59ee37bb7c8ea Mon Sep 17 00:00:00 2001 From: Yogish Kulkarni Date: Tue, 9 Aug 2022 18:17:23 +0530 Subject: [PATCH 12/22] serializer: mask branch sink count change events When ruining a few hundred loops of link training between the SOC and the serializer, we are seeing unexpected HPD_IRQ being triggered by the MAX96745/96851 serializers due to "Branch sink count change" event. Till we figure out why this is happening, disable this interrupt source. Bug 3676822 Change-Id: Id56ff7d324b9a51f5468afb2d74df7856040056d Signed-off-by: Yogish Kulkarni Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2758859 Reviewed-by: Shu Zhong Reviewed-by: svcacv Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- .../tegra/dc/bridge/maxim_gmsl_dp_serializer.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index 4f2c49ea..f5710290 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -87,6 +87,10 @@ #define MAX_GMSL_DP_SER_MISC_CONFIG_B1 0x7019 #define MAX_GMSL_DP_SER_MISC_CONFIG_B1_MASK (1 << 0) #define MAX_GMSL_DP_SER_MISC_CONFIG_B1_VAL 0x1 + +#define MAX_GMSL_DP_SER_HPD_INTERRUPT_MASK 0x702D +#define MAX_GMSL_DP_SER_HPD_BRANCH_SINK_COUNT_CHANGE_INTERRUPT_DISABLE_VAL 0x20 + #define MAX_GMSL_DP_SER_MAX_LINK_COUNT 0x7070 #define MAX_GMSL_DP_SER_MAX_LINK_RATE 0x7074 @@ -227,6 +231,16 @@ static void max_gmsl_dp_ser_setup(struct max_gmsl_dp_ser_priv *priv) MAX_GMSL_DP_SER_VID_TX_U, }; + /* + * WAR: When ruining a few hundred loops of link training between the + * SOC and the serializer, we are seeing unexpected HPD_IRQ being + * triggered by the MAX96745/96851 serializers due to "Branch sink count + * change" event. Till we figure out why this is happening, disable this + * interrupt source. + */ + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_HPD_INTERRUPT_MASK, + MAX_GMSL_DP_SER_HPD_BRANCH_SINK_COUNT_CHANGE_INTERRUPT_DISABLE_VAL); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B0, 0x0f); max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B1, 0x0f); max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_1_CTRL0_B0, 0x0f); From 36a425b6f7e91f031e4cc5a8bbdcdf0482f53c27 Mon Sep 17 00:00:00 2001 From: Prafull Suryawanshi Date: Mon, 12 Sep 2022 16:04:43 +0530 Subject: [PATCH 13/22] dc: bridge: maxim: remove GMSL lock worker thread This change removes worker thread from serializer driver which waits on both links to lock. If both links are not set via DTS entry then it does not proceed further which causes no video output. As it is possible that one link can be connected to deserializer, this does not require the worker to wait on link locks. So removing worker thread and enabling video immediately fixes this issue. bug 3727875 Change-Id: Ie427103a7d455201a4f783d690c6250b38a9113c Signed-off-by: Prafull Suryawanshi Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2775207 Reviewed-by: svcacv Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Shu Zhong GVS: Gerrit_Virtual_Submit --- .../dc/bridge/maxim_gmsl_dp_serializer.c | 113 ++++-------------- 1 file changed, 20 insertions(+), 93 deletions(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index f5710290..15cd9c8c 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -138,9 +138,6 @@ struct max_gmsl_dp_ser_priv { u8 dprx_link_rate; struct mutex mutex; struct regmap *regmap; - struct work_struct work; - struct delayed_work delay_work; - struct workqueue_struct *wq; int ser_errb; unsigned int ser_irq; bool enable_mst; @@ -292,19 +289,6 @@ static bool max_gmsl_dp_ser_check_dups(u32 *ids) return true; } -static int max_gmsl_read_lock(struct max_gmsl_dp_ser_priv *priv, - u32 reg_addr, u32 mask, - u32 expected_value) -{ - u8 reg_data; - - reg_data = max_gmsl_dp_ser_read(priv, reg_addr); - if ((reg_data & mask) == expected_value) - return 0; - - return -1; -} - static void max_gmsl_detect_internal_crc_error(struct max_gmsl_dp_ser_priv *priv, struct device *dev) { @@ -373,77 +357,6 @@ static irqreturn_t max_gsml_dp_ser_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static void tegra_poll_gmsl_training_lock(struct work_struct *work) -{ - struct delayed_work *dwork = container_of(work, - struct delayed_work, work); - struct max_gmsl_dp_ser_priv *priv = container_of(dwork, - struct max_gmsl_dp_ser_priv, delay_work); - int ret = 0; - - ret = max_gmsl_read_lock(priv, MAX_GMSL_DP_SER_CTRL3, - MAX_GMSL_DP_SER_CTRL3_LOCK_MASK, - MAX_GMSL_DP_SER_CTRL3_LOCK_VAL); - if (ret < 0) { - dev_dbg(&priv->client->dev, "GMSL Lock is not set\n"); - goto reschedule; - } - - if (priv->link_a_is_enabled) { - ret = max_gmsl_read_lock(priv, MAX_GMSL_DP_SER_LCTRL2_A, - MAX_GMSL_DP_SER_LCTRL2_LOCK_MASK, - MAX_GMSL_DP_SER_LCTRL2_LOCK_VAL); - if (ret < 0) { - dev_dbg(&priv->client->dev, "GMSL Lock set failed for Link A\n"); - goto reschedule; - } - } - - if (priv->link_b_is_enabled) { - ret = max_gmsl_read_lock(priv, MAX_GMSL_DP_SER_LCTRL2_B, - MAX_GMSL_DP_SER_LCTRL2_LOCK_MASK, - MAX_GMSL_DP_SER_LCTRL2_LOCK_VAL); - if (ret < 0) { - dev_dbg(&priv->client->dev, "GMSL Lock set failed for Link B\n"); - goto reschedule; - } - } - - ret = max_gmsl_read_lock(priv, MAX_GMSL_DP_SER_DPRX_TRAIN, - MAX_GMSL_DP_SER_DPRX_TRAIN_STATE_MASK, - MAX_GMSL_DP_SER_DPRX_TRAIN_STATE_VAL); - if (ret < 0) { - dev_dbg(&priv->client->dev, - "DP Link tranining hasn't completed\n"); - goto reschedule; - } - - /* enable internal CRC after link training */ - max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_X, - MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); - max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_Y, - MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); - max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_Z, - MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); - max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_U, - MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); - - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_X, - MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Y, - MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Z, - MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_U, - MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); - - return; - -reschedule: - queue_delayed_work(priv->wq, - &priv->delay_work, msecs_to_jiffies(500)); -} - static int max_gmsl_dp_ser_init(struct device *dev) { struct max_gmsl_dp_ser_priv *priv; @@ -521,9 +434,6 @@ static int max_gmsl_dp_ser_init(struct device *dev) max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_ENABLE, MAX_GMSL_DP_SER_LINK_ENABLE_MASK, 0x1); - queue_delayed_work(priv->wq, &priv->delay_work, - msecs_to_jiffies(500)); - ret = max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_INTR9); if (ret < 0) { dev_err(dev, "%s: INTR9 register read failed\n", __func__); @@ -544,6 +454,26 @@ static int max_gmsl_dp_ser_init(struct device *dev) MAX_GMSL_DP_SER_INTR8_MASK, MAX_GMSL_DP_SER_INTR8_VAL); + /* enable internal CRC after link training */ + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_X, + MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_Y, + MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_Z, + MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_U, + MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); + + /* enable video output */ + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_X, + MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Y, + MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_Z, + MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_U, + MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); + return ret; } @@ -722,9 +652,6 @@ static int max_gmsl_dp_ser_probe(struct i2c_client *client) return -EFAULT; } - priv->wq = alloc_workqueue("tegra_poll_gmsl_training_lock", WQ_HIGHPRI, 0); - INIT_DELAYED_WORK(&priv->delay_work, tegra_poll_gmsl_training_lock); - ret = max_gmsl_dp_ser_init(&client->dev); if (ret < 0) { dev_err(dev, "%s: dp serializer init failed\n", __func__); From 1510420e8cc824d8344008de809ab4b454ee82e8 Mon Sep 17 00:00:00 2001 From: Mihir Pradeep Garude Date: Mon, 21 Nov 2022 10:51:00 -0800 Subject: [PATCH 14/22] nvidia: remove regulator header from maxim Remove regulator header file from maxim display port serializer driver. JIRA TDS-11357 Change-Id: I31148a458a92adf31f00bec6386ade3b34b1b412 Signed-off-by: Mihir Pradeep Garude Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2813426 Reviewed-by: svcacv Reviewed-by: Shu Zhong Reviewed-by: svc_kernel_abi Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert GVS: Gerrit_Virtual_Submit --- drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index 15cd9c8c..c40033da 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include From 98f2ffc37e6897d3c60b9ba5898adaf83c69e974 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 2 Dec 2022 14:05:23 +0000 Subject: [PATCH 15/22] video: tegra: bridge: Fix build for Linux v6.1 Upstream Linux commit ed5c2f5fd10d ("i2c: Make remove callback return void") in Linux v6.1, updates the i2c remove callback to prototye to return void instead of int. This is causes the Maxim DP Serialize driver build to fail for Linux v6.1. Update this driver to fix the build for Linux v6.1. Bug 3820317 Bug 3835208 Change-Id: I61f4f68e67a4adf9f1744d32f7686ea962876c15 Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2820718 Reviewed-by: svc_kernel_abi Reviewed-by: Bibek Basu GVS: Gerrit_Virtual_Submit --- drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index c40033da..17623179 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -20,6 +20,7 @@ #include #include #include +#include #define MAX_GMSL_DP_SER_REG_13 0xD @@ -683,14 +684,20 @@ static int max_gmsl_dp_ser_probe(struct i2c_client *client) return ret; } +#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE +static void max_gmsl_dp_ser_remove(struct i2c_client *client) +#else static int max_gmsl_dp_ser_remove(struct i2c_client *client) +#endif { struct max_gmsl_dp_ser_priv *priv = i2c_get_clientdata(client); i2c_unregister_device(client); gpiod_set_value_cansleep(priv->gpiod_pwrdn, 0); +#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE return 0; +#endif } #ifdef CONFIG_PM From b46348e227de3140b57c7a05dc426724688b2546 Mon Sep 17 00:00:00 2001 From: prafulls Date: Tue, 9 Aug 2022 13:42:01 +0000 Subject: [PATCH 16/22] nvidia: add driver for TI FPDLink DP Serializer This change adds kernel driver for TI FPDlink DP Serializers. The dp lane rate, dp link rate and viewport resolution can be set from device tree. Rest of registers are set using values given in reference i2c script. JIRA TDS-11129 Signed-off-by: prafulls Change-Id: I6dfc48794c9b2517fe6ed4e91552e46583f6288c Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2822429 Reviewed-by: Shu Zhong Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- .../dc/bridge/ti_fpdlink_dp_serializer.c | 1052 +++++++++++++++++ 1 file changed, 1052 insertions(+) create mode 100644 drivers/video/tegra/dc/bridge/ti_fpdlink_dp_serializer.c diff --git a/drivers/video/tegra/dc/bridge/ti_fpdlink_dp_serializer.c b/drivers/video/tegra/dc/bridge/ti_fpdlink_dp_serializer.c new file mode 100644 index 00000000..486e6d96 --- /dev/null +++ b/drivers/video/tegra/dc/bridge/ti_fpdlink_dp_serializer.c @@ -0,0 +1,1052 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * DS90UH983-Q1 DP to FPD-Link Serializer driver + * + * Copyright (C) 2022, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Target I2C register address +#define TI_TARGET_ID_0 0x70 +#define TI_TARGET_ID_VAL 0x58 +#define TI_TARGET_ALIAS_ID_0 0x78 +#define TI_TARGET_ALIAS_ID_VAL 0x2C +#define TI_TARGET_DEST_0 0x88 +#define TI_TARGET_DEST_VAL 0x0 +// Reset for SER and PLL +#define TI_RESET_CTL 0x1 +#define TI_RESET_CTL_VAL_SOFT_RESET_SER 0x1 +#define TI_RESET_CTL_VAL_SOFT_RESET_PLL 0x30 +// Indirect Page Access +#define TI_IND_ACC_CTL 0x40 +#define TI_IND_ACC_CTL_SEL_MASK (0x7 << 2) +#define TI_IND_ACC_CTL_SEL_SHIFT 2 +#define TI_IND_ACC_CTL_AUTO_INC_MASK (0x1 << 1) +#define TI_IND_ACC_CTL_READ_MASK (0x0 << 1) +// FPD Port page +#define TI_IND_ACC_PAGE_1 0x1 +// FPD PLL page +#define TI_IND_ACC_PAGE_2 0x2 +#define TI_IND_ACC_PAGE_4 0x4 +#define TI_IND_ACC_PAGE_5 0x5 +#define TI_IND_ACC_PAGE_9 0x9 +#define TI_IND_ACC_PAGE_11 0xb +#define TI_IND_ACC_PAGE_12 0xc +#define TI_IND_ACC_PAGE_14 0xe +#define TI_IND_ACC_ADDR 0x41 +#define TI_IND_ACC_DATA 0x42 +// Page 0 - General +#define TI_GENERAL_CFG 0x7 +#define TI_GENERAL_CFG_SST_MST_MODE_STRP_MASK (0x1 << 1) +#define TI_GENERAL_CFG_I2C_PASS_THROUGH_MASK (0x1 << 3) +#define TI_GENERAL_CFG2 0x2 +#define TI_GENERAL_CFG2_VAL 0xD1 +#define TI_GENERAL_CFG2_DEFAULT 0xDF +#define TI_GENERAL_CFG2_DPRX_EN (0x1 << 4) +#define TI_GENERAL_CFG2_CRC_ERROR_RESET_CLEAR (0x1 << 5) +#define TI_FPD4_CFG 0x5 +#define TI_FPD4_CFG_FPD4_TX_MODE_DUAL 0x28 +#define TI_FPD4_CFG_FPD4_TX_MODE_SINGLE 0x2C +#define TI_FPD4_CFG_FPD4_TX_MODE_IND 0x3C +#define TI_FPD4_PGCFG_VP0 0x29 +#define TI_FPD4_PGCFG_DEFAULT 0x8 +#define TI_FPD3_FIFO_CFG 0x5B +#define TI_FPD3_FIFO_CFG_VAL 0x23 +#define TI_TX_PORT_SEL 0x2D +#define TI_TX_PORT_SEL_VAL_PORT_0 0x1 +#define TI_TX_PORT_SEL_VAL_PORT_1 0x12 +// PAGE 2 PLL +#define TI_PLL_0 0x1B +#define TI_PLL_1 0x5B +#define TI_PLL_DISABLE_VAL 0x1 +#define TI_PLL_DISABLE_SHIFT 0x3 +#define TI_NDIV_7_0_CH0 0x5 +#define TI_NDIV_7_0_CH0_VAL 0x7D +#define TI_NDIV_15_8_CH0 0x6 +#define TI_PDIV_CH0 0x13 +#define TI_PDIV_CH0_VAL 0x80 +#define TI_NDIV_7_0_CH1 0x45 +#define TI_NDIV_7_0_CH1_VAL 0x7D +#define TI_NDIV_15_8_CH1 0x46 +#define TI_PDIV_CH1 0x53 +#define TI_PDIV_CH1_VAL 0x80 +#define TI_MASH_ORDER_CH0 0x4 +#define TI_MASH_ORDER_CH0_VAL 0x1 +#define TI_NUM_7_0_CH0 0x1E +#define TI_NUM_7_0_CH0_VAL 0x0 +#define TI_NUM_15_8_CH0 0x1F +#define TI_NUM_15_8_CH0_VAL 0x0 +#define TI_NUM_23_16_CH0 0x20 +#define TI_NUM_23_16_CH0_VAL 0x0 +#define TI_MASH_ORDER_CH1 0x44 +#define TI_MASH_ORDER_CH1_VAL 0x1 +#define TI_NUM_7_0_CH1 0x5E +#define TI_NUM_7_0_CH1_VAL 0x0 +#define TI_NUM_15_8_CH1 0x5F +#define TI_NUM_15_8_CH1_VAL 0x0 +#define TI_NUM_23_16_CH1 0x60 +#define TI_NUM_23_16_CH1_VAL 0x0 +#define TI_VCO_CH0 0xE +#define TI_VCO_CH1 0x4E +#define TI_VCO_CH_SEL_VCO_1 0xC3 +#define TI_VCO_CH_SEL_VCO_2 0xC7 +#define TI_VCO_CH_SEL_VCO_3 0xCB +#define TI_VCO_CH_SEL_VCO_4 0xCF +// APB bus registers +#define TI_APB_CTL 0x48 +#define TI_APB_CTL_EN 0x1 +#define TI_APB_ADDR0 0x49 +#define TI_APB_ADDR1 0x4a +#define TI_APB_DATA0 0x4b +#define TI_APB_DATA1 0x4c +#define TI_APB_DATA2 0x4d +#define TI_APB_DATA3 0x4e +#define TI_APB_LINK_ENABLE 0x0 +#define TI_APB_EMPTY_FILL 0x0 +#define TI_APB_LINK_ENABLE_LOW 0x0 +#define TI_APB_LINK_ENABLE_HIGH 0x1 +#define TI_APB_MAX_LINK_RATE 0x74 +#define TI_APB_MAX_LINK_RATE_MASK 0xFF +#define TI_APB_MAX_LINK_RATE_162 0x06 // 1.62 Gbps +#define TI_APB_MAX_LINK_RATE_270 0x0A // 2.7 Gbps +#define TI_APB_MAX_LINK_RATE_540 0x14 // 5.4 Gbps +#define TI_APB_MAX_LINK_RATE_810 0x1E // 8.1 Gbps +#define TI_APB_MAX_LANE_COUNT 0x70 +#define TI_APB_MAX_LANE_COUNT_1 0x1 +#define TI_APB_MAX_LANE_COUNT_2 0x2 +#define TI_APB_MAX_LANE_COUNT_4 0x4 +#define TI_APB_MISC_CONFIG 0x18 +#define TI_APB_MISC_CONFIG_MAX_DOWNSPREAD_CONFIG (1 << 4) +#define TI_APB_MISC_CONFIG_DISABLE_INACTIVE_COUNT (1 << 2) + +#define TI_APB_VIDEO_INPUT_RESET 0x54 +#define TI_APB_VIDEO_INPUT_RESET_TRIGGER 0x1 + +#define TI_LINK_LAYER_CTL 0x0 +#define TI_LINK_LAYER_CTL_EN_NEW_TSLOT1 0x8 +#define TI_LINK_LAYER_CTL_LINK_LAYER_1_EN 0x4 +#define TI_LINK_LAYER_CTL_EN_NEW_TSLOT0 0x2 +#define TI_LINK_LAYER_CTL_LINK_LAYER_0_EN 0x1 + +#define TI_LINK0_STREAM_EN 0x1 +#define TI_LINK1_STREAM_EN 0x11 +#define TI_LINK_STREAM_EN0 0x1 +#define TI_LINK_STREAM_EN1 0x2 +#define TI_LINK_STREAM_EN2 0x4 +#define TI_LINK_STREAM_EN3 0x8 +#define TI_LINK_STREAM_EN4 0x10 +#define TI_LINK0_SLOT_REQ0 0x6 +#define TI_LINK1_SLOT_REQ0 0x16 +#define TI_VP_WIDTH0 0x20 +#define TI_VID_PROC_CFG_VP0 0x1 +#define TI_VID_PROC_CFG_VP0_DEFAULT 0xA8 +#define TI_DP_H_ACTIVE0_VP0 0x2 +#define TI_DP_H_ACTIVE1_VP0 0x3 +#define TI_VID_H_ACTIVE0_VP0 0x10 +#define TI_VID_H_ACTIVE1_VP0 0x11 +#define TI_VID_H_BACK0_VP0 0x12 +#define TI_VID_H_BACK1_VP0 0x13 +#define TI_VID_H_WIDTH0_VP0 0x14 +#define TI_VID_H_WIDTH1_VP0 0x15 +#define TI_VID_H_TOTAL0_VP0 0x16 +#define TI_VID_H_TOTAL1_VP0 0x17 +#define TI_VID_V_ACTIVE0_VP0 0x18 +#define TI_VID_V_ACTIVE1_VP0 0x19 +#define TI_VID_V_BACK0_VP0 0x1A +#define TI_VID_V_BACK1_VP0 0x1B +#define TI_VID_V_WIDTH0_VP0 0x1C +#define TI_VID_V_WIDTH1_VP0 0x1D +#define TI_VID_V_FRONT0_VP0 0x1F +#define TI_VID_V_FRONT1_VP0 0x1E +#define TI_VID_PROC_CFG2_VP0 0x27 +#define TI_PCLK_GEN_M_0_VP0 0x23 +#define TI_PCLK_GEN_M_1_VP0 0x24 +#define TI_PCLK_GEN_N_VP0 0x25 + +#define TI_VP_CONFIG_REG 0x43 +#define TI_VP_CONFIG_REG_VID_STREAM_1 0x0 +#define TI_VP_CONFIG_REG_VID_STREAM_2 0x1 +#define TI_VP_CONFIG_REG_VID_STREAM_3 0x2 +#define TI_VP_CONFIG_REG_VID_STREAM_4 0x3 +#define TI_VP_ENABLE_REG 0x44 +#define TI_VP_ENABLE_REG_VP0_ENABLE 0x1 +#define TI_VP_ENABLE_REG_VP1_ENABLE 0x2 +#define TI_VP_ENABLE_REG_VP2_ENABLE 0x4 +#define TI_VP_ENABLE_REG_VP3_ENABLE 0x8 + +#define TI_HDCP_TX_ID0 0xF0 +#define TI_HDCP_TX_ID0_VAL 0x5F +#define TI_HDCP_TX_ID1 0xF1 +#define TI_HDCP_TX_ID1_VAL 0x55 +#define TI_HDCP_TX_ID2 0xF2 +#define TI_HDCP_TX_ID2_VAL_1 0x42 +#define TI_HDCP_TX_ID2_VAL_2 0x48 +#define TI_HDCP_TX_ID3 0xF3 +#define TI_HDCP_TX_ID3_VAL 0x39 +#define TI_HDCP_TX_ID4 0xF4 +#define TI_HDCP_TX_ID4_VAL 0x38 +#define TI_HDCP_TX_ID5 0xF5 +#define TI_HDCP_TX_ID5_VAL 0x33 +#define TI_HDCP_TX_ID6 0xF6 +#define TI_HDCP_TX_ID6_VAL 0x30 + +// Custom Macros +#define TI_FPD_LINK_COUNT 0x2 + +struct ti_fpdlink_dp_ser_source { + struct fwnode_handle *fwnode; +}; + +static const struct regmap_config ti_fpdlink_dp_ser_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, +}; + +struct view_port { + u16 h_active; + u16 h_back_porch; + u16 h_width; + u16 h_total; + u16 v_active; + u16 v_back_porch; + u16 v_width; + u16 v_front_porch; +}; + +struct ti_fpdlink_dp_ser_priv { + struct i2c_client *client; + struct gpio_desc *gpiod_pwrdn; + struct regmap *regmap; + u8 dprx_lane_count; + u8 dprx_link_rate; + struct view_port vp; + bool link_0_select; + bool link_1_select; +}; + +static int ti_i2c_read(struct ti_fpdlink_dp_ser_priv *priv, u8 reg) +{ + int ret = 0; + int val = 0; + + ret = regmap_read(priv->regmap, reg, &val); + if (ret < 0) + dev_err(&priv->client->dev, + "%s: register 0x%02x read failed (%d)\n", + __func__, reg, ret); + + return val; +} + +static int ti_i2c_write(struct ti_fpdlink_dp_ser_priv *priv, u8 reg, int val) +{ + int ret = 0; + + ret = regmap_write(priv->regmap, reg, val); + if (ret < 0) + dev_err(&priv->client->dev, + "%s: register 0x%02x write failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int ti_page_indirect_i2c_write(struct ti_fpdlink_dp_ser_priv *priv, + u32 page, u32 buffer[][2], u32 length) +{ + int i, ret = 0; + + // set page at i2c indirect register 0x40 + ret = ti_i2c_write(priv, TI_IND_ACC_CTL, page); + if (ret < 0) + goto fail; + + for (i = 0; i < length; i++) { + // write address at i2c indirect register 0x41 + ret = ti_i2c_write(priv, TI_IND_ACC_ADDR, buffer[i][0]); + if (ret < 0) + goto fail; + + // write data at i2c indirect register 0x42 + ret = ti_i2c_write(priv, TI_IND_ACC_DATA, buffer[i][1]); + if (ret < 0) + goto fail; + } + +fail: + if (ret < 0) + dev_err(&priv->client->dev, "%s: failed (%d)\n", __func__, ret); + + return ret; +} + +static void ti_fpdlink_dp_apb_write(struct ti_fpdlink_dp_ser_priv *priv, + u8 apb_addr[2], u8 apb_data[4]) +{ + // addr 0 + ti_i2c_write(priv, TI_APB_ADDR0, apb_addr[0]); + + // addr 1 + ti_i2c_write(priv, TI_APB_ADDR1, apb_addr[1]); + + // data 0 + ti_i2c_write(priv, TI_APB_DATA0, apb_data[0]); + + // data 1 + ti_i2c_write(priv, TI_APB_DATA1, apb_data[1]); + + // data 2 + ti_i2c_write(priv, TI_APB_DATA2, apb_data[2]); + + // data 3 + ti_i2c_write(priv, TI_APB_DATA3, apb_data[3]); +} + +static void ti_program_pll_port_0(struct ti_fpdlink_dp_ser_priv *priv) +{ + u32 page = 0; + u32 buf[20][2]; + + ti_i2c_write(priv, TI_TX_PORT_SEL, TI_TX_PORT_SEL_VAL_PORT_0); + + // Set FPD4 on port 0 + { + page = TI_IND_ACC_PAGE_9 << TI_IND_ACC_CTL_SEL_SHIFT; + // Register offset not present in datasheet but present in reference script + buf[0][0] = 0x84; + buf[0][1] = 0x2; + ti_page_indirect_i2c_write(priv, page, buf, 1); + } + + // Program PLL page - port0 + { + page = TI_IND_ACC_PAGE_2 << TI_IND_ACC_CTL_SEL_SHIFT; + buf[0][0] = TI_NDIV_7_0_CH0; + buf[0][1] = TI_NDIV_7_0_CH0_VAL; + buf[1][0] = TI_PDIV_CH0; + buf[1][1] = TI_PDIV_CH0_VAL; + ti_page_indirect_i2c_write(priv, page, buf, 2); + } + + // Select port 0 and set sampling rate + { + ti_i2c_write(priv, TI_TX_PORT_SEL, TI_TX_PORT_SEL_VAL_PORT_0); + // Register offset not present in datasheet but present in reference script + ti_i2c_write(priv, 0x6a, 0x4a); + ti_i2c_write(priv, 0x6e, 0x80); + } + + // Set halfrate mode + { + ti_i2c_write(priv, TI_GENERAL_CFG2, TI_GENERAL_CFG2_VAL); + } + + // PLLs + { + page = TI_IND_ACC_PAGE_2 << TI_IND_ACC_CTL_SEL_SHIFT; + // Zero Out PLL + { + buf[0][0] = TI_MASH_ORDER_CH0; + buf[0][1] = TI_MASH_ORDER_CH0_VAL; + buf[1][0] = TI_NUM_7_0_CH0; + buf[1][1] = TI_NUM_7_0_CH0_VAL; + buf[2][0] = TI_NUM_15_8_CH0; + buf[2][1] = TI_NUM_15_8_CH0_VAL; + buf[3][0] = TI_NUM_23_16_CH0; + buf[3][1] = TI_NUM_23_16_CH0_VAL; + ti_page_indirect_i2c_write(priv, page, buf, 4); + } + // Configure VCO + { + buf[0][0] = TI_VCO_CH0; + buf[0][1] = TI_VCO_CH_SEL_VCO_2; + ti_page_indirect_i2c_write(priv, page, buf, 1); + // soft reset PLL + ti_i2c_write(priv, TI_RESET_CTL, TI_RESET_CTL_VAL_SOFT_RESET_PLL); + } + // Enable PLL + { + // Below registers not given in data sheet. + buf[0][0] = 0x1b; + buf[0][1] = 0x0; + ti_page_indirect_i2c_write(priv, page, buf, 1); + } + } + +} + +static void ti_program_pll_port_1(struct ti_fpdlink_dp_ser_priv *priv) +{ + u32 page = 0; + u32 buf[20][2]; + + ti_i2c_write(priv, TI_TX_PORT_SEL, TI_TX_PORT_SEL_VAL_PORT_1); + + // Set FPD4 on port 1 + { + page = TI_IND_ACC_PAGE_9 << TI_IND_ACC_CTL_SEL_SHIFT; + // Register offset not present in datasheet but present in reference script + buf[0][0] = 0x94; + buf[0][1] = 0x2; + ti_page_indirect_i2c_write(priv, page, buf, 1); + } + + // Program PLL page - port1 + { + page = TI_IND_ACC_PAGE_2 << TI_IND_ACC_CTL_SEL_SHIFT; + buf[0][0] = TI_NDIV_7_0_CH1; + buf[0][1] = TI_NDIV_7_0_CH1_VAL; + buf[1][0] = TI_PDIV_CH1; + buf[1][1] = TI_PDIV_CH1_VAL; + ti_page_indirect_i2c_write(priv, page, buf, 2); + } + + // Select port 1 and set sampling rate + { + ti_i2c_write(priv, TI_TX_PORT_SEL, TI_TX_PORT_SEL_VAL_PORT_1); + // Register offset not present in datasheet but present in reference script + ti_i2c_write(priv, 0x6a, 0x4a); + ti_i2c_write(priv, 0x6e, 0x80); + } + + // Set halfrate mode + { + ti_i2c_write(priv, TI_GENERAL_CFG2, TI_GENERAL_CFG2_VAL); + } + + { + page = TI_IND_ACC_PAGE_2 << TI_IND_ACC_CTL_SEL_SHIFT; + // Zero Out PLL + { + buf[0][0] = TI_MASH_ORDER_CH1; + buf[0][1] = TI_MASH_ORDER_CH1_VAL; + buf[1][0] = TI_NUM_7_0_CH1; + buf[1][1] = TI_NUM_7_0_CH1_VAL; + buf[2][0] = TI_NUM_15_8_CH1; + buf[2][1] = TI_NUM_15_8_CH1_VAL; + buf[3][0] = TI_NUM_23_16_CH1; + buf[3][1] = TI_NUM_23_16_CH1_VAL; + ti_page_indirect_i2c_write(priv, page, buf, 4); + } + // Configure VCO + { + buf[0][0] = TI_VCO_CH1; + buf[0][1] = TI_VCO_CH_SEL_VCO_2; + ti_page_indirect_i2c_write(priv, page, buf, 1); + // soft reset PLL + ti_i2c_write(priv, TI_RESET_CTL, TI_RESET_CTL_VAL_SOFT_RESET_PLL); + } + // Enable PLL + { + // Below registers not given in data sheet. + buf[0][0] = 0x5b; + buf[0][1] = 0x0; + ti_page_indirect_i2c_write(priv, page, buf, 2); + } + } + +} +/* Set FPD4 and program PLL for both ports */ +static void ti_program_pll(struct ti_fpdlink_dp_ser_priv *priv) +{ + u32 page = 0; + u32 buf[20][2]; + + // Disable PLL - PLL0 and PLL1 + { + page = TI_IND_ACC_PAGE_2 << TI_IND_ACC_CTL_SEL_SHIFT; + buf[0][0] = TI_PLL_0; + buf[0][1] = TI_PLL_DISABLE_VAL << TI_PLL_DISABLE_SHIFT; + buf[1][0] = TI_PLL_1; + buf[1][1] = TI_PLL_DISABLE_VAL << TI_PLL_DISABLE_SHIFT; + ti_page_indirect_i2c_write(priv, page, buf, 2); + } + + // DISABLE FPD3 FIFO pass through, Enable FPD4 independent mode. + { + // These are default values from data sheet. + ti_i2c_write(priv, TI_FPD3_FIFO_CFG, TI_FPD3_FIFO_CFG_VAL); + ti_i2c_write(priv, TI_FPD4_CFG, TI_FPD4_CFG_FPD4_TX_MODE_IND); + ti_i2c_write(priv, TI_GENERAL_CFG2, TI_GENERAL_CFG2_VAL); + } + + if (priv->link_0_select) + ti_program_pll_port_0(priv); + + if (priv->link_1_select) + ti_program_pll_port_1(priv); + + // soft reset Ser + ti_i2c_write(priv, TI_RESET_CTL, TI_RESET_CTL_VAL_SOFT_RESET_SER); + + // Enable I2C Passthrough + { + int pass_thr_val = ti_i2c_read(priv, TI_GENERAL_CFG); + int pass_thr_reg = pass_thr_val | TI_GENERAL_CFG_I2C_PASS_THROUGH_MASK; + + ti_i2c_write(priv, 0x7, pass_thr_reg); + } +} + +static void ti_program_dp_config(struct ti_fpdlink_dp_ser_priv *priv) +{ + u32 page = 0; + u32 buf[20][2]; + u8 apb_addr[2]; + u8 apb_data[4]; + + // Select ser link layer + if (priv->link_0_select) { + page = TI_IND_ACC_PAGE_11 << TI_IND_ACC_CTL_SEL_SHIFT; + buf[0][0] = TI_LINK_LAYER_CTL; + buf[0][1] = TI_LINK_LAYER_CTL_EN_NEW_TSLOT0 | TI_LINK_LAYER_CTL_LINK_LAYER_0_EN; + ti_page_indirect_i2c_write(priv, page, buf, 1); + // select write to port 0 + ti_i2c_write(priv, TI_TX_PORT_SEL, TI_TX_PORT_SEL_VAL_PORT_0); + } + + if (priv->link_1_select) { + page = TI_IND_ACC_PAGE_11 << TI_IND_ACC_CTL_SEL_SHIFT; + buf[0][0] = TI_LINK_LAYER_CTL; + buf[0][1] = TI_LINK_LAYER_CTL_EN_NEW_TSLOT0 | TI_LINK_LAYER_CTL_LINK_LAYER_1_EN; + ti_page_indirect_i2c_write(priv, page, buf, 1); + // select write to port 1 + ti_i2c_write(priv, TI_TX_PORT_SEL, TI_TX_PORT_SEL_VAL_PORT_1); + } + + // Set DP config + { + // Enable APB interface + ti_i2c_write(priv, TI_APB_CTL, TI_APB_CTL_EN); + + // Force HPD low + { + memset(apb_addr, 0, 2); + memset(apb_data, 0, 4); + apb_addr[0] = TI_APB_LINK_ENABLE; + apb_data[0] = TI_APB_LINK_ENABLE_LOW; + ti_fpdlink_dp_apb_write(priv, apb_addr, apb_data); + } + + // Set max advertised link rate. + { + memset(apb_addr, 0, 2); + memset(apb_data, 0, 4); + apb_addr[0] = TI_APB_MAX_LINK_RATE; + apb_data[0] = priv->dprx_link_rate; + ti_fpdlink_dp_apb_write(priv, apb_addr, apb_data); + } + + // Set max advertised lane count. + { + memset(apb_addr, 0, 2); + memset(apb_data, 0, 4); + apb_addr[0] = TI_APB_MAX_LANE_COUNT; + apb_data[0] = priv->dprx_lane_count; + ti_fpdlink_dp_apb_write(priv, apb_addr, apb_data); + } + + // Request min VOD swing of 0x02 + { + memset(apb_addr, 0, 2); + memset(apb_data, 0, 4); + // below register not given in data sheet + apb_addr[0] = 0x14; + apb_addr[1] = 2; + apb_data[0] = 2; + ti_fpdlink_dp_apb_write(priv, apb_addr, apb_data); + } + + // Set SST/MST mode and DP/eDP Mode. Add support for MST in future. + { + // This is set to default value + memset(apb_addr, 0, 2); + memset(apb_data, 0, 4); + apb_addr[0] = TI_APB_MISC_CONFIG; + apb_data[0] = TI_APB_MISC_CONFIG_MAX_DOWNSPREAD_CONFIG + | TI_APB_MISC_CONFIG_DISABLE_INACTIVE_COUNT; + ti_fpdlink_dp_apb_write(priv, apb_addr, apb_data); + } + + // Disable line reset for VS0 + { + // below register not given in data sheet + memset(apb_addr, 0, 2); + memset(apb_data, 0, 4); + apb_addr[0] = 0xc; + apb_addr[1] = 0xa; + apb_data[0] = 1; + ti_fpdlink_dp_apb_write(priv, apb_addr, apb_data); + } + + // Force HPD high to trigger link training + { + memset(apb_addr, 0, 2); + memset(apb_data, 0, 4); + apb_addr[0] = TI_APB_LINK_ENABLE; + apb_data[0] = TI_APB_LINK_ENABLE_HIGH; + ti_fpdlink_dp_apb_write(priv, apb_addr, apb_data); + } + } +} + +static void ti_program_viewport_timing(struct ti_fpdlink_dp_ser_priv *priv) +{ + u32 page = 0; + u32 buf[20][2]; + u8 apb_addr[2]; + u8 apb_data[4]; + + // Program VP config + { + //Set VP_SRC_SELECT to Stream 0 for SST Mode and program timings + page = TI_IND_ACC_PAGE_12 << TI_IND_ACC_CTL_SEL_SHIFT; + page = page | TI_IND_ACC_CTL_AUTO_INC_MASK; + // set page address with autoincrement writes. + // ie. If first register is set to 0x2 (using 0x41) then first write will be to 0x2, + // next write will 0x3 and so on. + ti_i2c_write(priv, TI_IND_ACC_CTL, page); + ti_i2c_write(priv, TI_IND_ACC_ADDR, TI_VID_PROC_CFG_VP0); + ti_i2c_write(priv, TI_IND_ACC_DATA, TI_VID_PROC_CFG_VP0_DEFAULT); + // VID H Active + ti_i2c_write(priv, TI_IND_ACC_ADDR, TI_DP_H_ACTIVE0_VP0); + ti_i2c_write(priv, TI_IND_ACC_DATA, priv->vp.h_active & 0xFF); + ti_i2c_write(priv, TI_IND_ACC_DATA, (priv->vp.h_active & 0xFF00) >> 8); + //Horizontal Active - VID_H_ACTIVE0_VP0 + ti_i2c_write(priv, TI_IND_ACC_ADDR, TI_VID_H_ACTIVE0_VP0); + ti_i2c_write(priv, TI_IND_ACC_DATA, priv->vp.h_active & 0xFF); + ti_i2c_write(priv, TI_IND_ACC_DATA, (priv->vp.h_active & 0xFF00) >> 8); + //Horizontal Back Porch - VID_H_BACK0_VP0 + ti_i2c_write(priv, TI_IND_ACC_DATA, priv->vp.h_back_porch & 0xFF); + ti_i2c_write(priv, TI_IND_ACC_DATA, (priv->vp.h_back_porch & 0xFF00) >> 8); + //Horizontal Sync - VID_H_WIDTH0_VP0 + ti_i2c_write(priv, TI_IND_ACC_DATA, priv->vp.h_width & 0xFF); + ti_i2c_write(priv, TI_IND_ACC_DATA, (priv->vp.h_width & 0xFF00) >> 8); + //Horizontal Total - VID_H_TOTAL0_VP0 + ti_i2c_write(priv, TI_IND_ACC_DATA, priv->vp.h_total & 0xFF); + ti_i2c_write(priv, TI_IND_ACC_DATA, (priv->vp.h_total & 0xFF00) >> 8); + //Vertical Active - VID_V_ACTIVE0_VP0 + ti_i2c_write(priv, TI_IND_ACC_DATA, priv->vp.v_active & 0xFF); + ti_i2c_write(priv, TI_IND_ACC_DATA, (priv->vp.v_active & 0xFF00) >> 8); + //Vertical Back Porch - VID_V_BACK0_VP0 + ti_i2c_write(priv, TI_IND_ACC_DATA, priv->vp.v_back_porch & 0xFF); + ti_i2c_write(priv, TI_IND_ACC_DATA, (priv->vp.v_back_porch & 0xFF00) >> 8); + //Vertical Sync - VID_V_WIDTH0_VP0 + ti_i2c_write(priv, TI_IND_ACC_DATA, priv->vp.v_width & 0xFF); + ti_i2c_write(priv, TI_IND_ACC_DATA, (priv->vp.v_width & 0xFF00) >> 8); + //Vertical Front Porch - VID_V_FRONT0_VP0 + ti_i2c_write(priv, TI_IND_ACC_DATA, priv->vp.v_front_porch & 0xFF); + ti_i2c_write(priv, TI_IND_ACC_DATA, (priv->vp.v_front_porch & 0xFF00) >> 8); + //HSYNC Polarity = +, VSYNC Polarity = +, - VID_PROC_CFG2_VP0 + ti_i2c_write(priv, TI_IND_ACC_ADDR, TI_VID_PROC_CFG2_VP0); + ti_i2c_write(priv, TI_IND_ACC_DATA, 0x0); + //M/N Register - M,M and N value + ti_i2c_write(priv, TI_IND_ACC_ADDR, TI_PCLK_GEN_M_0_VP0); + ti_i2c_write(priv, TI_IND_ACC_DATA, 0x14); + ti_i2c_write(priv, TI_IND_ACC_DATA, 0xe); + ti_i2c_write(priv, TI_IND_ACC_DATA, 0xf); + } + + // Enable VP + { + //Set number of VPs used = 1 + ti_i2c_write(priv, TI_VP_CONFIG_REG, TI_VP_CONFIG_REG_VID_STREAM_1); + //Enable video processors + ti_i2c_write(priv, TI_VP_ENABLE_REG, TI_VP_ENABLE_REG_VP0_ENABLE); + } + + // Video Input Reset + { + memset(apb_addr, 0, 2); + memset(apb_data, 0, 4); + apb_addr[0] = TI_APB_VIDEO_INPUT_RESET; + apb_data[0] = TI_APB_VIDEO_INPUT_RESET_TRIGGER; + ti_fpdlink_dp_apb_write(priv, apb_addr, apb_data); + } + + { + // Set color depth to 24 bpp for VP0 + page = TI_IND_ACC_PAGE_12 << TI_IND_ACC_CTL_SEL_SHIFT; + buf[0][0] = TI_FPD4_PGCFG_VP0; + buf[0][1] = TI_FPD4_PGCFG_DEFAULT; + ti_page_indirect_i2c_write(priv, page, buf, 1); + // Soft Reset PLL + ti_i2c_write(priv, TI_RESET_CTL, TI_RESET_CTL_VAL_SOFT_RESET_PLL); + } + + // Configure TX Link Layer 0 - stream 0 enable, time slot 0, vp bpp, enable + if (priv->link_0_select) { + page = TI_IND_ACC_PAGE_11 << TI_IND_ACC_CTL_SEL_SHIFT; + buf[0][0] = TI_LINK0_STREAM_EN; + buf[0][1] = TI_LINK_STREAM_EN0; + buf[1][0] = TI_LINK0_SLOT_REQ0; + buf[1][1] = 0x3c; // fixed value, find out why? + buf[2][0] = TI_VP_WIDTH0; + buf[2][1] = 0x55; // default set to 24 bit. Need to fetch from DTS? + buf[3][0] = TI_LINK_LAYER_CTL; + buf[3][1] = TI_LINK_LAYER_CTL_EN_NEW_TSLOT0 | TI_LINK_LAYER_CTL_LINK_LAYER_0_EN; + ti_page_indirect_i2c_write(priv, page, buf, 4); + } + + if (priv->link_1_select) { + page = TI_IND_ACC_PAGE_11 << TI_IND_ACC_CTL_SEL_SHIFT; + buf[0][0] = TI_LINK1_STREAM_EN; + buf[0][1] = TI_LINK_STREAM_EN0; + buf[1][0] = TI_LINK1_SLOT_REQ0; + buf[1][1] = 0x3c; // fixed value, find out why? + buf[2][0] = TI_VP_WIDTH0; + buf[2][1] = 0x55; // default set to 24 bit. Need to fetch from DTS? + buf[3][0] = TI_LINK_LAYER_CTL; + buf[3][1] = TI_LINK_LAYER_CTL_EN_NEW_TSLOT0 | TI_LINK_LAYER_CTL_LINK_LAYER_1_EN; + ti_page_indirect_i2c_write(priv, page, buf, 4); + } +} + +static int ti_fpdlink_dp_ser_init(struct device *dev) +{ + struct ti_fpdlink_dp_ser_priv *priv; + struct i2c_client *client; + int ret = 0; + + client = to_i2c_client(dev); + priv = i2c_get_clientdata(client); + + priv->gpiod_pwrdn = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpiod_pwrdn)) { + dev_err(dev, "%s: gpiopwrdn is not enabled\n", __func__); + return PTR_ERR(priv->gpiod_pwrdn); + } + gpiod_set_consumer_name(priv->gpiod_pwrdn, "ti_fpdlink_dp_ser-pwrdn"); + + /* Drive PWRDNB pin high to power up the serializer */ + gpiod_set_value_cansleep(priv->gpiod_pwrdn, 1); + + /* Wait ~20ms for link to establish after power up */ + usleep_range(20000, 21000); + + // Init addresses + ti_i2c_write(priv, TI_TARGET_ID_0, TI_TARGET_ID_VAL); + ti_i2c_write(priv, TI_TARGET_ALIAS_ID_0, TI_TARGET_ALIAS_ID_VAL); + ti_i2c_write(priv, TI_TARGET_DEST_0, TI_TARGET_DEST_VAL); + + ti_program_pll(priv); + + ti_program_dp_config(priv); + + ti_program_viewport_timing(priv); + + return ret; +} + +static int ti_fpdlink_dp_ser_parse_dt_link_config(struct i2c_client *client, + struct ti_fpdlink_dp_ser_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct device_node *ser = dev->of_node; + int err = 0; + u32 fpd_link_select[TI_FPD_LINK_COUNT] = { 0 }; + + err = of_property_read_variable_u32_array(ser, "fpd-link-select", + fpd_link_select, 1, + ARRAY_SIZE(fpd_link_select)); + if (err < 0) { + dev_err(dev, + "%s: fpd-link-select property not found or invalid\n", + __func__); + return -EINVAL; + } + + if (fpd_link_select[0] == 1) + priv->link_0_select = true; + + if (fpd_link_select[1] == 1) + priv->link_1_select = true; + + if (priv->link_0_select && priv->link_1_select) { + dev_err(dev, + "%s: fpd-link-select: both link cannot be selected\n", + __func__); + return -EINVAL; + } + + if (!(priv->link_0_select || priv->link_1_select)) { + dev_err(dev, + "%s: fpd-link-select: No link is selected, enabling link 0\n", + __func__); + priv->link_0_select = true; + } + + dev_info(dev, + "%s: fpd-link-select link_0_select = %d, link_1_select = %d\n", + __func__, priv->link_0_select, priv->link_1_select); + + return err; +} + +static int ti_fpdlink_dp_ser_parse_dt_dp_config(struct i2c_client *client, + struct ti_fpdlink_dp_ser_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct device_node *ser = dev->of_node; + int err = 0; + u32 val = 0; + + dev_info(dev, "%s: parsing serializer device tree:\n", __func__); + + err = of_property_read_u32(ser, "dprx-lane-count", &val); + if (err) { + if (err == -EINVAL) { + dev_err(dev, "%s: - dprx-lane-count property not found\n", + __func__); + /* default value: 4 */ + priv->dprx_lane_count = 4; + dev_err(dev, "%s: dprx-lane-count set to default val: 4\n", + __func__); + } else { + return err; + } + } else { + /* set dprx-lane-count */ + if ((val == TI_APB_MAX_LANE_COUNT_1) || + (val == TI_APB_MAX_LANE_COUNT_2) || + (val == TI_APB_MAX_LANE_COUNT_4)) { + priv->dprx_lane_count = val; + } else { + dev_err(dev, "%s: - invalid lane-count %i from DTS\n", + __func__, val); + return err; + } + } + + err = of_property_read_u32(ser, "dprx-link-rate", &val); + if (err) { + if (err == -EINVAL) { + dev_err(dev, "%s: - dprx-link-rate property not found\n", + __func__); + /* default value: 0x14 which is 5.4 Gbps */ + priv->dprx_link_rate = TI_APB_MAX_LINK_RATE_540; + dev_err(dev, "%s: dprx-link-rate set to default val: 0x14\n", + __func__); + } else { + return err; + } + } else { + /* set dprx-link-rate*/ + if ((val == TI_APB_MAX_LINK_RATE_162) || + (val == TI_APB_MAX_LINK_RATE_270) || + (val == TI_APB_MAX_LINK_RATE_540) || + (val == TI_APB_MAX_LINK_RATE_810)) { + priv->dprx_link_rate = val; + dev_info(dev, "%s: - dprx-link-rate %i\n", __func__, val); + } else { + dev_err(dev, "%s: - invalid link-count %i from DTS\n", + __func__, val); + return err; + } + } + + return 0; +} + +static int ti_fpdlink_dp_ser_parse_dt_viewport_config(struct i2c_client *client, + struct ti_fpdlink_dp_ser_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct device_node *ser = dev->of_node; + int err = 0; + u32 val = 0; + struct device_node *timings_handle; + + err = of_property_read_u32(ser, "timings-phandle", &val); + if (err) { + dev_err(dev, "%s: - timings-phandle property not found\n", + __func__); + return err; + } + + timings_handle = of_find_node_by_phandle(val); + if (timings_handle == NULL) { + dev_err(dev, "%s: - can not get timings node\n", + __func__); + return -1; + } + + err = of_property_read_u32(timings_handle, "hactive", &val); + if (err) { + dev_err(dev, "%s: - hactive property not found\n", + __func__); + return err; + } + priv->vp.h_active = val; + dev_info(dev, "%s: - hactive - %i\n", __func__, val); + + err = of_property_read_u32(timings_handle, "hback-porch", &val); + if (err) { + dev_err(dev, "%s: - hback-porch property not found\n", + __func__); + return err; + } + priv->vp.h_back_porch = val; + dev_info(dev, "%s: - hback-porch - %i\n", __func__, val); + + err = of_property_read_u32(timings_handle, "hsync-len", &val); + if (err) { + dev_err(dev, "%s: - hsync-len property not found\n", + __func__); + return err; + } + priv->vp.h_width = val; + dev_info(dev, "%s: - hsync-len - %i\n", __func__, val); + + err = of_property_read_u32(timings_handle, "hfront-porch", &val); + if (err) { + dev_err(dev, "%s: - hfront-porch property not found\n", + __func__); + return err; + } + priv->vp.h_total = val + priv->vp.h_width + priv->vp.h_back_porch + priv->vp.h_active; + dev_info(dev, "%s: - htotal - %i\n", __func__, priv->vp.h_total); + + err = of_property_read_u32(timings_handle, "vactive", &val); + if (err) { + dev_err(dev, "%s: - vactive property not found\n", + __func__); + return err; + } + priv->vp.v_active = val; + dev_info(dev, "%s: - vactive - %i\n", __func__, val); + + err = of_property_read_u32(timings_handle, "vback-porch", &val); + if (err) { + dev_err(dev, "%s: - vback-porch property not found\n", + __func__); + return err; + } + priv->vp.v_back_porch = val; + dev_info(dev, "%s: - vback-porch - %i\n", __func__, val); + + err = of_property_read_u32(timings_handle, "vsync-len", &val); + if (err) { + dev_err(dev, "%s: - vsync-len property not found\n", + __func__); + return err; + } + priv->vp.v_width = val; + dev_info(dev, "%s: - v-width - %i\n", __func__, val); + + err = of_property_read_u32(timings_handle, "vfront-porch", &val); + if (err) { + dev_err(dev, "%s: - v-front-porch property not found\n", + __func__); + return err; + } + priv->vp.v_front_porch = val; + dev_info(dev, "%s: - v-front-porch - %i\n", __func__, val); + + return 0; +} + +static int ti_fpdlink_dp_ser_probe(struct i2c_client *client) +{ + struct ti_fpdlink_dp_ser_priv *priv; + struct device *dev; + int ret = 0; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + priv->client = client; + i2c_set_clientdata(client, priv); + + priv->regmap = devm_regmap_init_i2c(client, &ti_fpdlink_dp_ser_i2c_regmap); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + dev = &priv->client->dev; + + ret = ti_fpdlink_dp_ser_parse_dt_link_config(client, priv); + if (ret < 0) { + dev_err(dev, "%s: error parsing device tree for link config\n", __func__); + return -EFAULT; + } + + ret = ti_fpdlink_dp_ser_parse_dt_dp_config(client, priv); + if (ret < 0) { + dev_err(dev, "%s: error parsing device tree for dp config\n", __func__); + return -EFAULT; + } + + ret = ti_fpdlink_dp_ser_parse_dt_viewport_config(client, priv); + if (ret < 0) { + dev_err(dev, "%s: error parsing device tree for vp config\n", __func__); + return -EFAULT; + } + + dev_err(dev, "%s: TI Serializer initialization started\n", __func__); + ret = ti_fpdlink_dp_ser_init(&client->dev); + if (ret < 0) { + dev_err(dev, "%s: TI Serializer init failed\n", __func__); + return -EFAULT; + } + dev_err(dev, "%s: TI Serializer initialization completed\n", __func__); + + return ret; +} + +static int ti_fpdlink_dp_ser_remove(struct i2c_client *client) +{ + struct ti_fpdlink_dp_ser_priv *priv = i2c_get_clientdata(client); + + i2c_unregister_device(client); + gpiod_set_value_cansleep(priv->gpiod_pwrdn, 0); + + return 0; +} + +static const struct of_device_id ti_fpdlink_dp_ser_dt_ids[] = { + { .compatible = "ti,ti_fpdlink_dp_ser" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ti_fpdlink_dp_ser_dt_ids); + +static struct i2c_driver ti_fpdlink_dp_ser_i2c_driver = { + .driver = { + .name = "ti_fpdlink_dp_ser", + .of_match_table = of_match_ptr(ti_fpdlink_dp_ser_dt_ids), + }, + .probe_new = ti_fpdlink_dp_ser_probe, + .remove = ti_fpdlink_dp_ser_remove, +}; + +module_i2c_driver(ti_fpdlink_dp_ser_i2c_driver); + +MODULE_DESCRIPTION("TI DP FPDLINK Serializer Driver"); +MODULE_AUTHOR("NVIDIA CORP"); +MODULE_LICENSE("GPL"); From f452a0d7c6aaa2304ac4f43c9425b1af980e2dfb Mon Sep 17 00:00:00 2001 From: Prafull Suryawanshi Date: Fri, 16 Dec 2022 16:41:05 +0530 Subject: [PATCH 17/22] tegra: dc: max_ser: configure HPD_IRQ This change enables HPD_IRQ event of maxim serializer only for "Loss of Training" and "Register control" events. It masks other events to avoid unexpected HPD_IRQ events. JIRA TDS-10786 Change-Id: I1c52d99f476bcf5dd9f31d00ab32f8953aec0333 Signed-off-by: Prafull Suryawanshi Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2829291 Reviewed-by: svcacv Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi Reviewed-by: Shu Zhong GVS: Gerrit_Virtual_Submit --- .../tegra/dc/bridge/maxim_gmsl_dp_serializer.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index 17623179..399e3de0 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -88,8 +88,8 @@ #define MAX_GMSL_DP_SER_MISC_CONFIG_B1_MASK (1 << 0) #define MAX_GMSL_DP_SER_MISC_CONFIG_B1_VAL 0x1 -#define MAX_GMSL_DP_SER_HPD_INTERRUPT_MASK 0x702D -#define MAX_GMSL_DP_SER_HPD_BRANCH_SINK_COUNT_CHANGE_INTERRUPT_DISABLE_VAL 0x20 +#define MAX_GMSL_DP_SER_HPD_INTERRUPT_MASK 0x702D +#define MAX_GMSL_DP_SER_HPD_INTERRUPT_VAL 0x7C #define MAX_GMSL_DP_SER_MAX_LINK_COUNT 0x7070 #define MAX_GMSL_DP_SER_MAX_LINK_RATE 0x7074 @@ -229,14 +229,11 @@ static void max_gmsl_dp_ser_setup(struct max_gmsl_dp_ser_priv *priv) }; /* - * WAR: When ruining a few hundred loops of link training between the - * SOC and the serializer, we are seeing unexpected HPD_IRQ being - * triggered by the MAX96745/96851 serializers due to "Branch sink count - * change" event. Till we figure out why this is happening, disable this - * interrupt source. + * Just enable "Loss of Training" and "Register control" events. + * Mask rest of event which can trigger HPD_IRQ. */ max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_HPD_INTERRUPT_MASK, - MAX_GMSL_DP_SER_HPD_BRANCH_SINK_COUNT_CHANGE_INTERRUPT_DISABLE_VAL); + MAX_GMSL_DP_SER_HPD_INTERRUPT_VAL); max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B0, 0x0f); max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_PHY_EDP_0_CTRL0_B1, 0x0f); From 2332bac0d75abcab85bda2fe8dbf136f4266ac54 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 20 Dec 2022 12:43:05 +0000 Subject: [PATCH 18/22] video: tegra: Fix TI serializer build for Linux v6.1 Upstream Linux commit ed5c2f5fd10d ("i2c: Make remove callback return void") in Linux v6.1, updates the i2c remove callback to prototype to return void instead of int. This is causes the TI FPDLink DP Serializer driver build to fail for Linux v6.1. Update this driver to fix the build for Linux v6.1. Bug 3820317 Bug 3835208 Change-Id: I35c3ac90070b7324330c31dc69d06f53cecd5b67 Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2831358 Reviewed-by: svc_kernel_abi Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Laxman Dewangan GVS: Gerrit_Virtual_Submit --- drivers/video/tegra/dc/bridge/ti_fpdlink_dp_serializer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/video/tegra/dc/bridge/ti_fpdlink_dp_serializer.c b/drivers/video/tegra/dc/bridge/ti_fpdlink_dp_serializer.c index 486e6d96..c0874814 100644 --- a/drivers/video/tegra/dc/bridge/ti_fpdlink_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/ti_fpdlink_dp_serializer.c @@ -30,6 +30,7 @@ #include #include #include +#include // Target I2C register address #define TI_TARGET_ID_0 0x70 @@ -1020,14 +1021,20 @@ static int ti_fpdlink_dp_ser_probe(struct i2c_client *client) return ret; } +#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE +static void ti_fpdlink_dp_ser_remove(struct i2c_client *client) +#else static int ti_fpdlink_dp_ser_remove(struct i2c_client *client) +#endif { struct ti_fpdlink_dp_ser_priv *priv = i2c_get_clientdata(client); i2c_unregister_device(client); gpiod_set_value_cansleep(priv->gpiod_pwrdn, 0); +#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE return 0; +#endif } static const struct of_device_id ti_fpdlink_dp_ser_dt_ids[] = { From f466f3adb96f81a1e24db979845618762b5212a9 Mon Sep 17 00:00:00 2001 From: Prafull Suryawanshi Date: Wed, 21 Dec 2022 16:25:57 +0530 Subject: [PATCH 19/22] video: bridge: maxim: add GMSL3 and FEC support This change adds GMSL3 support to maxim dp serializer driver. The property is set in device tree which is if true, then PAM4 mode is enabled and 12 Gbps link rate is set. GMSL3 also needs to have FEC enabled. This change also adds that support. Device tree need to have "enable-gmsl3" property to be set to get GMSL3 enabled. When GMSL3 is enabled, it will enable GMSL-FEC by default. GMSL-FEC can only be enabled by setting propery "enable-gmsl-fec". JIRA TDS-10659 Signed-off-by: Prafull Suryawanshi Change-Id: I1757f71babbc5135f0ddf3d2a501de721e84da6d Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2831963 Reviewed-by: svcacv Reviewed-by: Shu Zhong Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- .../dc/bridge/maxim_gmsl_dp_serializer.c | 75 ++++++++++++++++++- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index 399e3de0..7fe5ca3f 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -2,7 +2,7 @@ /* * MAXIM DP Serializer driver for MAXIM GMSL Serializers * - * Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. */ #include @@ -22,6 +22,12 @@ #include #include +#define MAX_GMSL_DP_SER_REG_4 0x4 +#define MAX_GMSL_DP_SER_REG_4_GMSL_A (1 << 6) +#define MAX_GMSL_DP_SER_REG_4_GMSL_A_PAM4_VAL (1 << 6) +#define MAX_GMSL_DP_SER_REG_4_GMSL_B (1 << 7) +#define MAX_GMSL_DP_SER_REG_4_GMSL_B_PAM4_VAL (1 << 7) + #define MAX_GMSL_DP_SER_REG_13 0xD #define MAX_GMSL_DP_SER_CTRL3 0x13 @@ -48,6 +54,13 @@ #define MAX_GMSL_DP_SER_LINK_CTRL_PHY_A 0x29 #define MAX_GMSL_DP_SER_LINK_CTRL_A_MASK (1 << 0) +#define MAX_GMSL_DP_SER_LCTRL0_A 0x28 +#define MAX_GMSL_DP_SER_LCTRL0_B 0x32 +#define MAX_GMSL_DP_SER_LCTRL0_TX_RATE_MASK (3 << 2) +#define MAX_GMSL_DP_SER_LCTRL0_TX_RATE_VAL_3GBPS 0x4 +#define MAX_GMSL_DP_SER_LCTRL0_TX_RATE_VAL_6GBPS 0x8 +#define MAX_GMSL_DP_SER_LCTRL0_TX_RATE_VAL_12GBPS 0xC + #define MAX_GMSL_DP_SER_LCTRL2_A 0x2A #define MAX_GMSL_DP_SER_LCTRL2_B 0x34 #define MAX_GMSL_DP_SER_LCTRL2_LOCK_MASK (1 << 0) @@ -56,6 +69,11 @@ #define MAX_GMSL_DP_SER_LINK_CTRL_PHY_B 0x33 #define MAX_GMSL_DP_SER_LINK_CTRL_B_MASK (1 << 0) +#define MAX_GMSL_DP_SER_TX0_LINK_A 0x50 +#define MAX_GMSL_DP_SER_TX0_LINK_B 0x60 +#define MAX_GMSL_DP_SER_TX0_FEC_ENABLE_MASK (1 << 1) +#define MAX_GMSL_DP_SER_TX0_FEC_ENABLE_VAL 0x2 + #define MAX_GMSL_DP_SER_VID_TX_X 0x100 #define MAX_GMSL_DP_SER_VID_TX_Y 0x110 #define MAX_GMSL_DP_SER_VID_TX_Z 0x120 @@ -141,6 +159,8 @@ struct max_gmsl_dp_ser_priv { int ser_errb; unsigned int ser_irq; bool enable_mst; + bool enable_gmsl3; + bool enable_gmsl_fec; u32 mst_payload_ids[MAX_GMSL_ARRAY_SIZE]; u32 gmsl_stream_ids[MAX_GMSL_ARRAY_SIZE]; u32 gmsl_link_select[MAX_GMSL_ARRAY_SIZE]; @@ -267,6 +287,41 @@ static void max_gmsl_dp_ser_setup(struct max_gmsl_dp_ser_priv *priv) MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_MASK, MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_100KBPS); + if (priv->enable_gmsl_fec || priv->enable_gmsl3) { + if (priv->link_a_is_enabled) { + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_TX0_LINK_A, + MAX_GMSL_DP_SER_TX0_FEC_ENABLE_MASK, + MAX_GMSL_DP_SER_TX0_FEC_ENABLE_VAL); + } + if (priv->link_b_is_enabled) { + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_TX0_LINK_B, + MAX_GMSL_DP_SER_TX0_FEC_ENABLE_MASK, + MAX_GMSL_DP_SER_TX0_FEC_ENABLE_VAL); + } + } + + if (priv->enable_gmsl3) { + if (priv->link_a_is_enabled) { + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LCTRL0_A, + MAX_GMSL_DP_SER_LCTRL0_TX_RATE_MASK, + MAX_GMSL_DP_SER_LCTRL0_TX_RATE_VAL_12GBPS); + + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_REG_4, + MAX_GMSL_DP_SER_REG_4_GMSL_A, + MAX_GMSL_DP_SER_REG_4_GMSL_A_PAM4_VAL); + } + + if (priv->link_b_is_enabled) { + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LCTRL0_B, + MAX_GMSL_DP_SER_LCTRL0_TX_RATE_MASK, + MAX_GMSL_DP_SER_LCTRL0_TX_RATE_VAL_12GBPS); + + max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_REG_4, + MAX_GMSL_DP_SER_REG_4_GMSL_B, + MAX_GMSL_DP_SER_REG_4_GMSL_B_PAM4_VAL); + } + } + if (priv->enable_mst) max_gmsl_dp_ser_mst_setup(priv); } @@ -485,9 +540,23 @@ static int max_gmsl_dp_ser_parse_mst_props(struct i2c_client *client, priv->enable_mst = of_property_read_bool(ser, "enable-mst"); if (priv->enable_mst) - dev_info(dev, "%s: MST mode enabled:\n", __func__); + dev_info(dev, "%s: MST mode enabled\n", __func__); else - dev_info(dev, "%s: MST mode not enabled:\n", __func__); + dev_info(dev, "%s: MST mode not enabled\n", __func__); + + priv->enable_gmsl3 = of_property_read_bool(ser, + "enable-gmsl3"); + if (priv->enable_gmsl3) + dev_info(dev, "%s: GMSL3 mode enabled\n", __func__); + else + dev_info(dev, "%s: GMSL3 mode not enabled\n", __func__); + + priv->enable_gmsl_fec = of_property_read_bool(ser, + "enable-gmsl-fec"); + if (priv->enable_gmsl_fec) + dev_info(dev, "%s: GMSL FEC enabled\n", __func__); + else + dev_info(dev, "%s: GMSL FEC not enabled\n", __func__); if (priv->enable_mst) { err = of_property_read_variable_u32_array(ser, From 1760079334a5975d5b03b0b5b7629a2b552cedca Mon Sep 17 00:00:00 2001 From: prafulls Date: Tue, 31 Jan 2023 11:57:56 +0000 Subject: [PATCH 20/22] video: dc: bridge: max_ser: fix resume error The resume functionality of maxim gmsl dp serializer driver calls init routine. Function incorrectly treats register value as return value and shows error message in logs. Removing return value check from register read during init fixes this issue. bug 3955858 Change-Id: Ibe2058a49827bdd802ea4b5f24be986c911450e6 Signed-off-by: prafulls Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2851087 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index 7fe5ca3f..c83d0d6d 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -413,7 +413,6 @@ static int max_gmsl_dp_ser_init(struct device *dev) { struct max_gmsl_dp_ser_priv *priv; struct i2c_client *client; - int ret = 0; client = to_i2c_client(dev); priv = i2c_get_clientdata(client); @@ -486,11 +485,7 @@ static int max_gmsl_dp_ser_init(struct device *dev) max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_ENABLE, MAX_GMSL_DP_SER_LINK_ENABLE_MASK, 0x1); - ret = max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_INTR9); - if (ret < 0) { - dev_err(dev, "%s: INTR9 register read failed\n", __func__); - return -EFAULT; - } + max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_INTR9); if (priv->link_a_is_enabled) max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_INTR2, @@ -526,7 +521,7 @@ static int max_gmsl_dp_ser_init(struct device *dev) max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_U, MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); - return ret; + return 0; } static int max_gmsl_dp_ser_parse_mst_props(struct i2c_client *client, From 104ecb7e0983aeff1a2c96a1bd7508308e9261ce Mon Sep 17 00:00:00 2001 From: prafulls Date: Tue, 7 Feb 2023 12:22:10 +0000 Subject: [PATCH 21/22] video: bridge: maxim: enable GMSL3 and FEC This change reads device tree property and enables GMSL3 and GMSL FEC mode only for that corresponding link. Earlier change was enabling GMSL3 and FEC by default to both links. JIRA TDS-10659 Change-Id: I92a0cc757d3ded1c5aa13e14ffbdfaa22a2da89c Signed-off-by: prafulls Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2854454 Reviewed-by: Shu Zhong Reviewed-by: svc_kernel_abi Reviewed-by: svcacv GVS: Gerrit_Virtual_Submit --- .../dc/bridge/maxim_gmsl_dp_serializer.c | 171 ++++++++++++------ 1 file changed, 114 insertions(+), 57 deletions(-) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index c83d0d6d..bcc86b50 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -139,6 +139,9 @@ #define MAX_GMSL_ARRAY_SIZE 4 +#define MAX_GMSL_LINKS 2 +#define MAX_GMSL_LINK_A 0 +#define MAX_GMSL_LINK_B 1 struct max_gmsl_dp_ser_source { struct fwnode_handle *fwnode; @@ -159,13 +162,12 @@ struct max_gmsl_dp_ser_priv { int ser_errb; unsigned int ser_irq; bool enable_mst; - bool enable_gmsl3; - bool enable_gmsl_fec; u32 mst_payload_ids[MAX_GMSL_ARRAY_SIZE]; u32 gmsl_stream_ids[MAX_GMSL_ARRAY_SIZE]; u32 gmsl_link_select[MAX_GMSL_ARRAY_SIZE]; - bool link_a_is_enabled; - bool link_b_is_enabled; + bool link_is_enabled[MAX_GMSL_LINKS]; + u32 enable_gmsl3[MAX_GMSL_LINKS]; + u32 enable_gmsl_fec[MAX_GMSL_LINKS]; }; static int max_gmsl_dp_ser_read(struct max_gmsl_dp_ser_priv *priv, int reg) @@ -248,6 +250,26 @@ static void max_gmsl_dp_ser_setup(struct max_gmsl_dp_ser_priv *priv) MAX_GMSL_DP_SER_VID_TX_U, }; + static const int max_gmsl_ser_tx0_link_regs[] = { + MAX_GMSL_DP_SER_TX0_LINK_A, + MAX_GMSL_DP_SER_TX0_LINK_B, + }; + + static const int max_gmsl_ser_lctrl0_regs[] = { + MAX_GMSL_DP_SER_LCTRL0_A, + MAX_GMSL_DP_SER_LCTRL0_B, + }; + + static const int max_gmsl_ser_reg4_mask[] = { + MAX_GMSL_DP_SER_REG_4_GMSL_A, + MAX_GMSL_DP_SER_REG_4_GMSL_B, + }; + + static const int max_gmsl_ser_reg4_value[] = { + MAX_GMSL_DP_SER_REG_4_GMSL_A_PAM4_VAL, + MAX_GMSL_DP_SER_REG_4_GMSL_B_PAM4_VAL, + }; + /* * Just enable "Loss of Training" and "Register control" events. * Mask rest of event which can trigger HPD_IRQ. @@ -287,38 +309,24 @@ static void max_gmsl_dp_ser_setup(struct max_gmsl_dp_ser_priv *priv) MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_MASK, MAX_GMSL_DP_SER_I2C_SPEED_CAPABILITY_100KBPS); - if (priv->enable_gmsl_fec || priv->enable_gmsl3) { - if (priv->link_a_is_enabled) { - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_TX0_LINK_A, - MAX_GMSL_DP_SER_TX0_FEC_ENABLE_MASK, - MAX_GMSL_DP_SER_TX0_FEC_ENABLE_VAL); - } - if (priv->link_b_is_enabled) { - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_TX0_LINK_B, - MAX_GMSL_DP_SER_TX0_FEC_ENABLE_MASK, - MAX_GMSL_DP_SER_TX0_FEC_ENABLE_VAL); - } - } + for (i = 0; i < MAX_GMSL_LINKS; i++) { + if (!priv->link_is_enabled[i]) + continue; - if (priv->enable_gmsl3) { - if (priv->link_a_is_enabled) { - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LCTRL0_A, - MAX_GMSL_DP_SER_LCTRL0_TX_RATE_MASK, - MAX_GMSL_DP_SER_LCTRL0_TX_RATE_VAL_12GBPS); + if (priv->enable_gmsl_fec[i] || priv->enable_gmsl3[i]) { + max_gmsl_dp_ser_update(priv, max_gmsl_ser_tx0_link_regs[i], + MAX_GMSL_DP_SER_TX0_FEC_ENABLE_MASK, + MAX_GMSL_DP_SER_TX0_FEC_ENABLE_VAL); + } + + if (priv->enable_gmsl3[i]) { + max_gmsl_dp_ser_update(priv, max_gmsl_ser_lctrl0_regs[i], + MAX_GMSL_DP_SER_LCTRL0_TX_RATE_MASK, + MAX_GMSL_DP_SER_LCTRL0_TX_RATE_VAL_12GBPS); max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_REG_4, - MAX_GMSL_DP_SER_REG_4_GMSL_A, - MAX_GMSL_DP_SER_REG_4_GMSL_A_PAM4_VAL); - } - - if (priv->link_b_is_enabled) { - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LCTRL0_B, - MAX_GMSL_DP_SER_LCTRL0_TX_RATE_MASK, - MAX_GMSL_DP_SER_LCTRL0_TX_RATE_VAL_12GBPS); - - max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_REG_4, - MAX_GMSL_DP_SER_REG_4_GMSL_B, - MAX_GMSL_DP_SER_REG_4_GMSL_B_PAM4_VAL); + max_gmsl_ser_reg4_mask[i], + max_gmsl_ser_reg4_value[i]); } } @@ -379,12 +387,12 @@ static void max_gmsl_detect_remote_error(struct max_gmsl_dp_ser_priv *priv, ret = max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_INTR3); - if (priv->link_a_is_enabled) { + if (priv->link_is_enabled[MAX_GMSL_LINK_A]) { if ((ret & MAX_GMSL_DP_SER_REM_ERR_FLAG_A) != 0) dev_err(dev, "%s: Remote deserializer error detected on Link A\n", __func__); } - if (priv->link_b_is_enabled) { + if (priv->link_is_enabled[MAX_GMSL_LINK_B]) { if ((ret & MAX_GMSL_DP_SER_REM_ERR_FLAG_B) != 0) dev_err(dev, "%s: Remote deserializer error detected on Link B\n", __func__); } @@ -467,12 +475,12 @@ static int max_gmsl_dp_ser_init(struct device *dev) * Write RESET_LINK = 0 (for both Phy A, 0x29, and Phy B, 0x33) * to initiate the GMSL link lock process. */ - if (priv->link_a_is_enabled) + if (priv->link_is_enabled[MAX_GMSL_LINK_A]) max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_CTRL_PHY_A, MAX_GMSL_DP_SER_LINK_CTRL_A_MASK, 0x0); - if (priv->link_b_is_enabled) + if (priv->link_is_enabled[MAX_GMSL_LINK_B]) max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_LINK_CTRL_PHY_B, MAX_GMSL_DP_SER_LINK_CTRL_B_MASK, @@ -487,11 +495,11 @@ static int max_gmsl_dp_ser_init(struct device *dev) max_gmsl_dp_ser_read(priv, MAX_GMSL_DP_SER_INTR9); - if (priv->link_a_is_enabled) + if (priv->link_is_enabled[MAX_GMSL_LINK_A]) max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_INTR2, MAX_GMSL_DP_SER_REM_ERR_OEN_A_MASK, MAX_GMSL_DP_SER_REM_ERR_OEN_A_VAL); - if (priv->link_b_is_enabled) + if (priv->link_is_enabled[MAX_GMSL_LINK_B]) max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_INTR2, MAX_GMSL_DP_SER_REM_ERR_OEN_B_MASK, MAX_GMSL_DP_SER_REM_ERR_OEN_B_VAL); @@ -539,20 +547,6 @@ static int max_gmsl_dp_ser_parse_mst_props(struct i2c_client *client, else dev_info(dev, "%s: MST mode not enabled\n", __func__); - priv->enable_gmsl3 = of_property_read_bool(ser, - "enable-gmsl3"); - if (priv->enable_gmsl3) - dev_info(dev, "%s: GMSL3 mode enabled\n", __func__); - else - dev_info(dev, "%s: GMSL3 mode not enabled\n", __func__); - - priv->enable_gmsl_fec = of_property_read_bool(ser, - "enable-gmsl-fec"); - if (priv->enable_gmsl_fec) - dev_info(dev, "%s: GMSL FEC enabled\n", __func__); - else - dev_info(dev, "%s: GMSL FEC not enabled\n", __func__); - if (priv->enable_mst) { err = of_property_read_variable_u32_array(ser, "mst-payload-ids", @@ -595,6 +589,69 @@ static int max_gmsl_dp_ser_parse_mst_props(struct i2c_client *client, return 0; } +static int max_gmsl_dp_ser_parse_gmsl_props(struct i2c_client *client, + struct max_gmsl_dp_ser_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct device_node *ser = dev->of_node; + int err = 0; + + err = of_property_read_variable_u32_array(ser, "enable-gmsl3", + priv->enable_gmsl3, 1, + ARRAY_SIZE(priv->enable_gmsl3)); + if (err < 0) { + if (err == -EINVAL) { + dev_info(dev, + "%s: enable-gmsl3 property not found\n", + __func__); + } else { + dev_err(dev, + "%s: enable-gmsl3 property does not have valid data\n", + __func__); + return -EINVAL; + } + } + + err = of_property_read_variable_u32_array(ser, "enable-gmsl-fec", + priv->enable_gmsl_fec, 1, + ARRAY_SIZE(priv->enable_gmsl_fec)); + if (err < 0) { + if (err == -EINVAL) { + dev_info(dev, + "%s: enable-gmsl-fec property not found\n", + __func__); + } else { + dev_err(dev, + "%s: enable-gmsl-fec property does not have valid data\n", + __func__); + return -EINVAL; + } + } + + return 0; +} + +static int max_gmsl_dp_ser_parse_dt_props(struct i2c_client *client, + struct max_gmsl_dp_ser_priv *priv) +{ + struct device *dev = &priv->client->dev; + int err = 0; + + err = max_gmsl_dp_ser_parse_mst_props(client, priv); + if (err != 0) { + dev_err(dev, "%s: error parsing MST props\n", __func__); + return -EFAULT; + } + + err = max_gmsl_dp_ser_parse_gmsl_props(client, priv); + if (err != 0) { + dev_err(dev, "%s: error parsing GMSL props\n", __func__); + return -EFAULT; + } + + return err; +} + static int max_gmsl_dp_ser_parse_dt(struct i2c_client *client, struct max_gmsl_dp_ser_priv *priv) { @@ -655,11 +712,11 @@ static int max_gmsl_dp_ser_parse_dt(struct i2c_client *client, if ((priv->gmsl_link_select[i] == MAX_GMSL_DP_SER_ENABLE_LINK_A) || (priv->gmsl_link_select[i] == MAX_GMSL_DP_SER_ENABLE_LINK_AB)) { - priv->link_a_is_enabled = true; + priv->link_is_enabled[MAX_GMSL_LINK_A] = true; } else if ((priv->gmsl_link_select[i] == MAX_GMSL_DP_SER_ENABLE_LINK_B) || (priv->gmsl_link_select[i] == MAX_GMSL_DP_SER_ENABLE_LINK_AB)) { - priv->link_b_is_enabled = true; + priv->link_is_enabled[MAX_GMSL_LINK_B] = true; } else { dev_info(dev, "%s: GMSL Link select values are invalid\n", @@ -668,9 +725,9 @@ static int max_gmsl_dp_ser_parse_dt(struct i2c_client *client, } } - err = max_gmsl_dp_ser_parse_mst_props(client, priv); + err = max_gmsl_dp_ser_parse_dt_props(client, priv); if (err != 0) { - dev_err(dev, "%s: error parsing MST props\n", __func__); + dev_err(dev, "%s: error parsing DT props\n", __func__); return -EFAULT; } From 819e83a8e3050ab0c19e67db336e1a3cf55cc3a9 Mon Sep 17 00:00:00 2001 From: Prafull Suryawanshi Date: Tue, 28 Mar 2023 16:59:59 +0530 Subject: [PATCH 22/22] video: bridge: max_gmsl: add superframe support This change adds symmetric superframe support to maxim gmsl dp serializer driver. This includes below - 1. Read device tree properties associated with superframe feature. 2. Calculate values for register from timings parameter. 3. Hardcode few values for 1080p mode as formula to calculate those not yet available. 4. Sanity check as only X-Y or Z-U dual view possible with current support. Device tree settings which needs to enable 1. Superframe structure in display-timings structure like below superframe-info { pipe_x_view: view-0 { x = <0>; y = <0>; width = <1920>; height = <1080>; hfront-porch = <24>; hback-porch = <40>; hsync-len = <16>; vfront-porch = <3>; vback-porch = <18>; vsync-len = <10>; }; pipe_y_view: view-1 { x = <1920>; y = <0>; width = <1920>; height = <1080>; hfront-porch = <24>; hback-porch = <40>; hsync-len = <16>; vfront-porch = <3>; vback-porch = <18>; vsync-len = <10>; }; }; }; 2. Superframe video timings in maxim serializer node as below superframe-video-timings { pipe-x { superframe-group = <0>; timings-phandle = <&pipe_x_view>; }; pipe-y { superframe-group = <0>; timings-phandle = <&pipe_y_view>; }; pipe-z { superframe-group = <1>; timings-phandle = <&pipe_z_view>; }; pipe-u { superframe-group = <1>; timings-phandle = <&pipe_u_view>; }; }; Signed-off-by: Prafull Suryawanshi Change-Id: I80b345f093c21a6e68ee75014eb337ecb579c85b --- .../dc/bridge/maxim_gmsl_dp_serializer.c | 474 ++++++++++++++++++ 1 file changed, 474 insertions(+) diff --git a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c index bcc86b50..637062d2 100644 --- a/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c +++ b/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c @@ -137,12 +137,76 @@ #define MAX_GMSL_DP_SER_INTERNAL_CRC_ERR_DET 0x4 #define MAX_GMSL_DP_SER_INTERNAL_CRC_ERR_INJ 0x10 +/* XY Dual View Configuration Registers */ +#define MAX_GMSL_DP_SER_ASYM_14_X 0X04CE +#define MAX_GMSL_DP_SER_ASYM_14_Y 0X05CE +#define MAX_GMSL_DP_SER_ASYM_15_X 0X04CF +#define MAX_GMSL_DP_SER_ASYM_15_Y 0X05CF +#define MAX_GMSL_DP_SER_ASYM_17_X 0X04D1 +#define MAX_GMSL_DP_SER_ASYM_17_Y 0X05D1 + +/* Pipe X Superframe Registers, each next PIPE register at 0x100 offset */ +#define MAX_GMSL_DP_SER_X_M_L 0X04C0 +#define MAX_GMSL_DP_SER_X_M_M 0X04C1 +#define MAX_GMSL_DP_SER_X_M_H 0X04C2 +#define MAX_GMSL_DP_SER_X_N_L 0X04C3 +#define MAX_GMSL_DP_SER_X_N_M 0X04C4 +#define MAX_GMSL_DP_SER_X_N_H 0X04C5 +#define MAX_GMSL_DP_SER_X_X_OFFSET_L 0X04C6 +#define MAX_GMSL_DP_SER_X_X_OFFSET_H 0X04C7 +#define MAX_GMSL_DP_SER_X_X_MAX_L 0X04C8 +#define MAX_GMSL_DP_SER_X_X_MAX_H 0X04C9 +#define MAX_GMSL_DP_SER_X_Y_MAX_L 0X04CA +#define MAX_GMSL_DP_SER_X_Y_MAX_H 0X04CB +#define MAX_GMSL_DP_SER_X_VS_DLY_L 0X04D8 +#define MAX_GMSL_DP_SER_X_VS_DLY_M 0X04D9 +#define MAX_GMSL_DP_SER_X_VS_DLY_H 0X04DA +#define MAX_GMSL_DP_SER_X_VS_HIGH_L 0X04DB +#define MAX_GMSL_DP_SER_X_VS_HIGH_M 0X04DC +#define MAX_GMSL_DP_SER_X_VS_HIGH_H 0X04DD +#define MAX_GMSL_DP_SER_X_VS_LOW_L 0X04DE +#define MAX_GMSL_DP_SER_X_VS_LOW_M 0X04DF +#define MAX_GMSL_DP_SER_X_VS_LOW_H 0X04E0 +#define MAX_GMSL_DP_SER_X_HS_DLY_L 0X04E1 +#define MAX_GMSL_DP_SER_X_HS_DLY_M 0X04E2 +#define MAX_GMSL_DP_SER_X_HS_DLY_H 0X04E3 +#define MAX_GMSL_DP_SER_X_HS_HIGH_L 0X04E4 +#define MAX_GMSL_DP_SER_X_HS_HIGH_H 0X04E5 +#define MAX_GMSL_DP_SER_X_HS_LOW_L 0X04E6 +#define MAX_GMSL_DP_SER_X_HS_LOW_H 0X04E7 +#define MAX_GMSL_DP_SER_X_HS_CNT_L 0X04E8 +#define MAX_GMSL_DP_SER_X_HS_CNT_H 0X04E9 +#define MAX_GMSL_DP_SER_X_HS_LLOW_L 0X04EA +#define MAX_GMSL_DP_SER_X_HS_LLOW_M 0X04EB +#define MAX_GMSL_DP_SER_X_HS_LLOW_H 0X04EC +#define MAX_GMSL_DP_SER_X_DE_DLY_L 0X04ED +#define MAX_GMSL_DP_SER_X_DE_DLY_M 0X04EE +#define MAX_GMSL_DP_SER_X_DE_DLY_H 0X04EF +#define MAX_GMSL_DP_SER_X_DE_HIGH_L 0X04F0 +#define MAX_GMSL_DP_SER_X_DE_HIGH_H 0X04F1 +#define MAX_GMSL_DP_SER_X_DE_LOW_L 0X04F2 +#define MAX_GMSL_DP_SER_X_DE_LOW_H 0X04F3 +#define MAX_GMSL_DP_SER_X_DE_CNT_L 0X04F4 +#define MAX_GMSL_DP_SER_X_DE_CNT_H 0X04F5 +#define MAX_GMSL_DP_SER_X_DE_LLOW_L 0X04F6 +#define MAX_GMSL_DP_SER_X_DE_LLOW_M 0X04F7 +#define MAX_GMSL_DP_SER_X_DE_LLOW_H 0X04F8 +#define MAX_GMSL_DP_SER_X_LUT_TEMPLATE 0x04CD + #define MAX_GMSL_ARRAY_SIZE 4 #define MAX_GMSL_LINKS 2 #define MAX_GMSL_LINK_A 0 #define MAX_GMSL_LINK_B 1 +#define MAX_GMSL_PIPE_X 0 +#define MAX_GMSL_PIPE_Y 1 +#define MAX_GMSL_PIPE_Z 2 +#define MAX_GMSL_PIPE_U 3 + +#define MAX_GMSL_DP_SER_DUAL_VIEW_ZY_OFFSET 0x200 +#define MAX_GMSL_DP_SER_PIPE_SUBIMAGE_OFFSET 0x100 + struct max_gmsl_dp_ser_source { struct fwnode_handle *fwnode; }; @@ -152,6 +216,36 @@ static const struct regmap_config max_gmsl_dp_ser_i2c_regmap = { .val_bits = 8, }; +struct max_gmsl_subimage_timing { + bool enable; + u32 superframe_group; + u32 x_start; + u32 h_active; + u32 h_front_porch; + u32 h_back_porch; + u32 h_sync_len; + u32 h_total; + u32 y_start; + u32 v_active; + u32 v_front_porch; + u32 v_back_porch; + u32 v_sync_len; + u32 v_total; +}; + +struct max_gmsl_subimage_attr { + u32 m, n, x_offset, x_max, y_max; + u32 vs_dly, vs_high, vs_low; + u32 hs_dly, hs_high, hs_low, hs_cnt, hs_llow; + u32 de_dly, de_high, de_low, de_cnt, de_llow; + u8 lut_template; +}; + +struct superframe_info { + struct max_gmsl_subimage_timing subimage_timing[MAX_GMSL_ARRAY_SIZE]; + struct max_gmsl_subimage_attr subimage_attr[MAX_GMSL_ARRAY_SIZE]; +}; + struct max_gmsl_dp_ser_priv { struct i2c_client *client; struct gpio_desc *gpiod_pwrdn; @@ -168,6 +262,7 @@ struct max_gmsl_dp_ser_priv { bool link_is_enabled[MAX_GMSL_LINKS]; u32 enable_gmsl3[MAX_GMSL_LINKS]; u32 enable_gmsl_fec[MAX_GMSL_LINKS]; + struct superframe_info superframe; }; static int max_gmsl_dp_ser_read(struct max_gmsl_dp_ser_priv *priv, int reg) @@ -191,6 +286,7 @@ static int max_gmsl_dp_ser_write(struct max_gmsl_dp_ser_priv *priv, u32 reg, u8 dev_err(&priv->client->dev, "%s: register 0x%02x write failed (%d)\n", __func__, reg, ret); + return ret; } @@ -417,6 +513,159 @@ static irqreturn_t max_gsml_dp_ser_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static void max_gmsl_dp_ser_program_dual_view_for_superframe_group(struct device *dev, u32 pipe_id) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max_gmsl_dp_ser_priv *priv = i2c_get_clientdata(client); + u32 offset = 0; + + /* Z-U registers offset shifted by 0x200 */ + if (pipe_id == MAX_GMSL_PIPE_Z) + offset = MAX_GMSL_DP_SER_DUAL_VIEW_ZY_OFFSET; + + /* + * TODO: Get formula and sequence to calculate these regiters. + * As of now, just hard code these settings for symmetric dual view. + * These needs to be program in exact sequence and same register + * is modified with different values. As of now, data sheet does not + * have details for all fields of these registers. + */ + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_ASYM_14_Y + offset, 0x37); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_ASYM_17_X + offset, 0xF8); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_ASYM_17_Y + offset, 0xF8); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_ASYM_15_X + offset, 0xBF); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_ASYM_15_Y + offset, 0xBF); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_ASYM_17_X + offset, 0xFC); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_ASYM_17_Y + offset, 0xFC); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_ASYM_14_X + offset, 0x2F); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_ASYM_14_X + offset, 0x0F); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_ASYM_14_Y + offset, 0x27); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_ASYM_14_Y + offset, 0x07); +} + +static void max_gmsl_dp_ser_program_subimage_timings(struct device *dev, u32 pipe_id) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max_gmsl_dp_ser_priv *priv = i2c_get_clientdata(client); + u32 offset = pipe_id * MAX_GMSL_DP_SER_PIPE_SUBIMAGE_OFFSET; + + // Asymmetric dual-view total pixel count per frame in sub-image + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_M_L + offset, (priv->superframe.subimage_attr[pipe_id].m & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_M_M + offset, (priv->superframe.subimage_attr[pipe_id].m & 0xFF00) >> 8); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_M_H + offset, (priv->superframe.subimage_attr[pipe_id].m & 0xFF0000) >> 16); + + // Asymmetric dual-view total pixel count per frame for superframe + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_N_L + offset, (priv->superframe.subimage_attr[pipe_id].n & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_N_M + offset, (priv->superframe.subimage_attr[pipe_id].n & 0xFF00) >> 8); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_N_H + offset, (priv->superframe.subimage_attr[pipe_id].n & 0xFF0000) >> 16); + + // Number of pixels to skip before writing when receiving a new line + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_X_OFFSET_L + offset, (priv->superframe.subimage_attr[pipe_id].x_offset & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_X_OFFSET_H + offset, (priv->superframe.subimage_attr[pipe_id].x_offset & 0xFF00) >> 8); + + // Index of the last pixel of sub-image in the superframe + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_X_MAX_L + offset, (priv->superframe.subimage_attr[pipe_id].x_max & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_X_MAX_H + offset, (priv->superframe.subimage_attr[pipe_id].x_max & 0xFF00) >> 8); + + // Index of the last line of sub-image in the superframe + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_Y_MAX_L + offset, (priv->superframe.subimage_attr[pipe_id].y_max & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_Y_MAX_H + offset, (priv->superframe.subimage_attr[pipe_id].y_max & 0xFF00) >> 8); + + // VS delay in terms of PCLK cycles. The output VS is delayed by VS_DELAY cycles from the start. + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_VS_DLY_L + offset, (priv->superframe.subimage_attr[pipe_id].vs_dly & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_VS_DLY_M + offset, (priv->superframe.subimage_attr[pipe_id].vs_dly & 0xFF00) >> 8); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_VS_DLY_H + offset, (priv->superframe.subimage_attr[pipe_id].vs_dly & 0xFF0000) >> 16); + + // VS high period in terms of PCLK + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_VS_HIGH_L + offset, (priv->superframe.subimage_attr[pipe_id].vs_high & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_VS_HIGH_M + offset, (priv->superframe.subimage_attr[pipe_id].vs_high & 0xFF00) >> 8); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_VS_HIGH_H + offset, (priv->superframe.subimage_attr[pipe_id].vs_high & 0xFF0000) >> 16); + + // VS low period in terms of PCLK cycles + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_VS_LOW_L + offset, (priv->superframe.subimage_attr[pipe_id].vs_low & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_VS_LOW_M + offset, (priv->superframe.subimage_attr[pipe_id].vs_low & 0xFF00) >> 8); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_VS_LOW_H + offset, (priv->superframe.subimage_attr[pipe_id].vs_low & 0xFF0000) >> 16); + + // HS delay in terms of PCLK cycles The output HS is delayed by HS_DELAY cycles from the start. + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_HS_DLY_L + offset, (priv->superframe.subimage_attr[pipe_id].hs_dly & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_HS_DLY_M + offset, (priv->superframe.subimage_attr[pipe_id].hs_dly & 0xFF00) >> 8); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_HS_DLY_H + offset, (priv->superframe.subimage_attr[pipe_id].hs_dly & 0xFF0000) >> 16); + + // HS high period in terms of PCLK cycles + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_HS_HIGH_L + offset, (priv->superframe.subimage_attr[pipe_id].hs_high & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_HS_HIGH_H + offset, (priv->superframe.subimage_attr[pipe_id].hs_high & 0xFF00) >> 8); + + // HS low period in terms of PCLK cycles + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_HS_LOW_L + offset, (priv->superframe.subimage_attr[pipe_id].hs_low & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_HS_LOW_H + offset, (priv->superframe.subimage_attr[pipe_id].hs_low & 0xFF00) >> 8); + + // HS pulses per frame + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_HS_CNT_L + offset, (priv->superframe.subimage_attr[pipe_id].hs_cnt & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_HS_CNT_H + offset, (priv->superframe.subimage_attr[pipe_id].hs_cnt & 0xFF00) >> 8); + + // HS long low period in terms of PCLK cycles + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_HS_LLOW_L + offset, (priv->superframe.subimage_attr[pipe_id].hs_llow & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_HS_LLOW_M + offset, (priv->superframe.subimage_attr[pipe_id].hs_llow & 0xFF00) >> 8); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_HS_LLOW_H + offset, (priv->superframe.subimage_attr[pipe_id].hs_llow & 0xFF0000) >> 16); + + // DE delay in terms of PCLK cycles The output HS is delayed by HS_DELAY cycles from the start. + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_DE_DLY_L + offset, (priv->superframe.subimage_attr[pipe_id].de_dly & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_DE_DLY_M + offset, (priv->superframe.subimage_attr[pipe_id].de_dly & 0xFF00) >> 8); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_DE_DLY_H + offset, (priv->superframe.subimage_attr[pipe_id].de_dly & 0xFF0000) >> 16); + + // DE high period in terms of PCLK cycles + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_DE_HIGH_L + offset, (priv->superframe.subimage_attr[pipe_id].de_high & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_DE_HIGH_H + offset, (priv->superframe.subimage_attr[pipe_id].de_high & 0xFF00) >> 8); + + // DE low period in terms of PCLK cycles + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_DE_LOW_L + offset, (priv->superframe.subimage_attr[pipe_id].de_low & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_DE_LOW_H + offset, (priv->superframe.subimage_attr[pipe_id].de_low & 0xFF00) >> 8); + + // DE pulses per frame + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_DE_CNT_L + offset, (priv->superframe.subimage_attr[pipe_id].de_cnt & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_DE_CNT_H + offset, (priv->superframe.subimage_attr[pipe_id].de_cnt & 0xFF00) >> 8); + + // DE long low period in terms of PCLK cycles + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_DE_LLOW_L + offset, (priv->superframe.subimage_attr[pipe_id].de_llow & 0xFF)); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_DE_LLOW_M + offset, (priv->superframe.subimage_attr[pipe_id].de_llow & 0xFF00) >> 8); + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_DE_LLOW_H + offset, (priv->superframe.subimage_attr[pipe_id].de_llow & 0xFF0000) >> 16); + + // LUT Template + max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_X_LUT_TEMPLATE + offset, (priv->superframe.subimage_attr[pipe_id].lut_template)); +} + +static int max_gmsl_dp_ser_configure_superframe(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max_gmsl_dp_ser_priv *priv = i2c_get_clientdata(client); + + /* + * Currently Enforcing X-Y and/or Z-U Dual view. + * TODO: Add support for other combinations of pipe. + */ + if ((priv->superframe.subimage_timing[MAX_GMSL_PIPE_X].superframe_group != priv->superframe.subimage_timing[MAX_GMSL_PIPE_Y].superframe_group) || + (priv->superframe.subimage_timing[MAX_GMSL_PIPE_Z].superframe_group != priv->superframe.subimage_timing[MAX_GMSL_PIPE_U].superframe_group)) { + dev_err(dev, "%s: Invalid superframe-group combination\n", __func__); + return -1; + } + + if (priv->superframe.subimage_timing[MAX_GMSL_PIPE_X].enable && priv->superframe.subimage_timing[MAX_GMSL_PIPE_Y].enable) { + /* X and Y both enabled, program superframe registers */ + max_gmsl_dp_ser_program_dual_view_for_superframe_group(dev, MAX_GMSL_PIPE_X); + max_gmsl_dp_ser_program_subimage_timings(dev, MAX_GMSL_PIPE_X); + max_gmsl_dp_ser_program_subimage_timings(dev, MAX_GMSL_PIPE_Y); + } + + if (priv->superframe.subimage_timing[MAX_GMSL_PIPE_Z].enable && priv->superframe.subimage_timing[MAX_GMSL_PIPE_U].enable) { + /* Z and U both enabled, program superframe registers */ + max_gmsl_dp_ser_program_dual_view_for_superframe_group(dev, MAX_GMSL_PIPE_Z); + max_gmsl_dp_ser_program_subimage_timings(dev, MAX_GMSL_PIPE_Z); + max_gmsl_dp_ser_program_subimage_timings(dev, MAX_GMSL_PIPE_U); + } + + return 0; +} + static int max_gmsl_dp_ser_init(struct device *dev) { struct max_gmsl_dp_ser_priv *priv; @@ -519,6 +768,9 @@ static int max_gmsl_dp_ser_init(struct device *dev) max_gmsl_dp_ser_write(priv, MAX_GMSL_DP_SER_INTERNAL_CRC_U, MAX_GMSL_DP_SER_INTERNAL_CRC_ENABLE); + /* configure superframe settings */ + max_gmsl_dp_ser_configure_superframe(dev); + /* enable video output */ max_gmsl_dp_ser_update(priv, MAX_GMSL_DP_SER_VID_TX_X, MAX_GMSL_DP_SER_VID_TX_MASK, 0x1); @@ -631,6 +883,226 @@ static int max_gmsl_dp_ser_parse_gmsl_props(struct i2c_client *client, return 0; } +static void max_gmsl_dp_ser_calc_superframe_pipe_attr(struct i2c_client *client, + struct max_gmsl_dp_ser_priv *priv, + int pipe_id) +{ + //Asymmetric dual-view total pixel count per frame in sub-image + priv->superframe.subimage_attr[pipe_id].m = priv->superframe.subimage_timing[pipe_id].h_total * priv->superframe.subimage_timing[pipe_id].v_total; + + //Asymmetric dual-view total pixel count per frame for superframe + // TODO: Get formula for this value + priv->superframe.subimage_attr[pipe_id].n = 461000; + + //Number of pixels to skip before writing when receiving a new line + priv->superframe.subimage_attr[pipe_id].x_offset = priv->superframe.subimage_timing[pipe_id].x_start; + + //Index of the last pixel of sub-image in the superframe + priv->superframe.subimage_attr[pipe_id].x_max = priv->superframe.subimage_timing[pipe_id].h_active + priv->superframe.subimage_attr[pipe_id].x_offset; + + //Index of the last line of sub-image in the superframe + priv->superframe.subimage_attr[pipe_id].y_max = priv->superframe.subimage_timing[pipe_id].v_active; + + //VS delay in terms of PCLK cycles. The output VS is delayed by VS_DELAY cycles from the start. + priv->superframe.subimage_attr[pipe_id].vs_dly = priv->superframe.subimage_timing[pipe_id].h_total * (priv->superframe.subimage_timing[pipe_id].v_active + priv->superframe.subimage_timing[pipe_id].v_front_porch); + + //VS high period in terms of PCLK + priv->superframe.subimage_attr[pipe_id].vs_high = priv->superframe.subimage_timing[pipe_id].h_total * priv->superframe.subimage_timing[pipe_id].v_sync_len; + + //VS low period in terms of PCLK cycles + //TODO Get formula for this value + priv->superframe.subimage_attr[pipe_id].vs_low = 36000; + + //HS delay in terms of PCLK cycles The output HS is delayed by HS_DELAY cycles from the start. + priv->superframe.subimage_attr[pipe_id].hs_dly = 0; + + //HS high period in terms of PCLK cycles + priv->superframe.subimage_attr[pipe_id].hs_high = priv->superframe.subimage_timing[pipe_id].h_sync_len; + + //HS low period in terms of PCLK cycles + priv->superframe.subimage_attr[pipe_id].hs_low = priv->superframe.subimage_timing[pipe_id].h_active + priv->superframe.subimage_timing[pipe_id].h_front_porch + priv->superframe.subimage_timing[pipe_id].h_back_porch; + + //HS pulses per frame + priv->superframe.subimage_attr[pipe_id].hs_cnt = priv->superframe.subimage_timing[pipe_id].v_total; + + //HS long low period in terms of PCLK cycles + priv->superframe.subimage_attr[pipe_id].hs_llow = 0; + + //DE delay in terms of PCLK cycles The output DE is delayed by DE_DELAY cycles from the start. + priv->superframe.subimage_attr[pipe_id].de_dly = priv->superframe.subimage_timing[pipe_id].h_back_porch + priv->superframe.subimage_timing[pipe_id].h_sync_len; + + //DE high period in terms of PCLK cycles + priv->superframe.subimage_attr[pipe_id].de_high = priv->superframe.subimage_timing[pipe_id].h_active; + + //DE low period in terms of PCLK cycles + priv->superframe.subimage_attr[pipe_id].de_low = priv->superframe.subimage_timing[pipe_id].h_front_porch + priv->superframe.subimage_timing[pipe_id].h_sync_len + priv->superframe.subimage_timing[pipe_id].h_back_porch; + + //Active lines per frame + priv->superframe.subimage_attr[pipe_id].de_cnt = priv->superframe.subimage_timing[pipe_id].v_active; + + //DE long low period in terms of PCLK cycles + //TODO Get formula for this value + priv->superframe.subimage_attr[pipe_id].de_llow = 61944; + + //LUT template + //TODO Get formula for this value + priv->superframe.subimage_attr[pipe_id].lut_template = 20; +} + +static int max_gmsl_dp_ser_parse_superframe_pipe_props(struct i2c_client *client, + struct max_gmsl_dp_ser_priv *priv, + struct device_node *pipe_node, + int pipe_id) +{ + struct device *dev = &priv->client->dev; + int err = 0; + u32 val = 0; + struct device_node *timings_handle; + + err = of_property_read_u32(pipe_node, "superframe-group", &val); + if (err) { + dev_err(dev, "%s: - superframe-group property not found\n", + __func__); + return err; + } + priv->superframe.subimage_timing[pipe_id].superframe_group = val; + + err = of_property_read_u32(pipe_node, "timings-phandle", &val); + if (err) { + dev_err(dev, "%s: - timings-phandle property not found\n", + __func__); + return err; + } + + timings_handle = of_find_node_by_phandle(val); + if (timings_handle == NULL) { + dev_err(dev, "%s: - can not get timings node\n", + __func__); + return -1; + } + + err = of_property_read_u32(timings_handle, "x", &val); + if (err) { + dev_err(dev, "%s: - x property not found\n", + __func__); + return err; + } + priv->superframe.subimage_timing[pipe_id].x_start = val; + + err = of_property_read_u32(timings_handle, "width", &val); + if (err) { + dev_err(dev, "%s: - width property not found\n", + __func__); + return err; + } + priv->superframe.subimage_timing[pipe_id].h_active = val; + + err = of_property_read_u32(timings_handle, "hfront-porch", &val); + if (err) { + dev_err(dev, "%s: - hfront-porch property not found\n", + __func__); + return err; + } + priv->superframe.subimage_timing[pipe_id].h_front_porch = val; + + err = of_property_read_u32(timings_handle, "hback-porch", &val); + if (err) { + dev_err(dev, "%s: - hback-porch property not found\n", + __func__); + return err; + } + priv->superframe.subimage_timing[pipe_id].h_back_porch = val; + + err = of_property_read_u32(timings_handle, "hsync-len", &val); + if (err) { + dev_err(dev, "%s: - hsync-len property not found\n", + __func__); + return err; + } + priv->superframe.subimage_timing[pipe_id].h_sync_len = val; + + priv->superframe.subimage_timing[pipe_id].h_total = priv->superframe.subimage_timing[pipe_id].h_active + priv->superframe.subimage_timing[pipe_id].h_front_porch + priv->superframe.subimage_timing[pipe_id].h_back_porch + priv->superframe.subimage_timing[pipe_id].h_sync_len; + + err = of_property_read_u32(timings_handle, "y", &val); + if (err) { + dev_err(dev, "%s: - y property not found\n", + __func__); + return err; + } + priv->superframe.subimage_timing[pipe_id].y_start = val; + + err = of_property_read_u32(timings_handle, "height", &val); + if (err) { + dev_err(dev, "%s: - height property not found\n", + __func__); + return err; + } + priv->superframe.subimage_timing[pipe_id].v_active = val; + + err = of_property_read_u32(timings_handle, "vfront-porch", &val); + if (err) { + dev_err(dev, "%s: - vfront-porch property not found\n", + __func__); + return err; + } + priv->superframe.subimage_timing[pipe_id].v_front_porch = val; + + err = of_property_read_u32(timings_handle, "vback-porch", &val); + if (err) { + dev_err(dev, "%s: - vback-porch property not found\n", + __func__); + return err; + } + priv->superframe.subimage_timing[pipe_id].v_back_porch = val; + + err = of_property_read_u32(timings_handle, "vsync-len", &val); + if (err) { + dev_err(dev, "%s: - vsync-len property not found\n", + __func__); + return err; + } + priv->superframe.subimage_timing[pipe_id].v_sync_len = val; + + priv->superframe.subimage_timing[pipe_id].v_total = priv->superframe.subimage_timing[pipe_id].v_active + priv->superframe.subimage_timing[pipe_id].v_front_porch + priv->superframe.subimage_timing[pipe_id].v_back_porch + priv->superframe.subimage_timing[pipe_id].v_sync_len; + + return 0; +} + +static void max_gmsl_dp_ser_parse_superframe_props(struct i2c_client *client, + struct max_gmsl_dp_ser_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct device_node *ser = dev->of_node; + int err, i = 0; + struct device_node *superframe_video_timings, *pipe_node; + char pipe_name[MAX_GMSL_ARRAY_SIZE][7] = { "pipe-x", "pipe-y", "pipe-z", "pipe-u"}; + + /* check if superframe timings present in device tree */ + superframe_video_timings = of_find_node_by_name(ser, "superframe-video-timings"); + if (superframe_video_timings == NULL) { + dev_info(dev, "%s: - superframe-video-timings property not found\n", + __func__); + return; + } + + /* get superframe subimage properties for each pipe */ + for (i = 0; i < MAX_GMSL_ARRAY_SIZE; i++) { + pipe_node = of_find_node_by_name(superframe_video_timings, pipe_name[i]); + if (pipe_node) { + err = max_gmsl_dp_ser_parse_superframe_pipe_props(client, priv, pipe_node, i); + if (!err) { + /* pipe have valid properties, calculate attributes */ + priv->superframe.subimage_timing[i].enable = true; + max_gmsl_dp_ser_calc_superframe_pipe_attr(client, priv, i); + } else { + dev_err(dev, "%s: - %s property found but properties are invalid\n", __func__, pipe_name[i]); + } + } else { + dev_info(dev, "%s: - %s property not found\n", __func__, pipe_name[i]); + } + } +} + static int max_gmsl_dp_ser_parse_dt_props(struct i2c_client *client, struct max_gmsl_dp_ser_priv *priv) { @@ -649,6 +1121,8 @@ static int max_gmsl_dp_ser_parse_dt_props(struct i2c_client *client, return -EFAULT; } + max_gmsl_dp_ser_parse_superframe_props(client, priv); + return err; }