From 2182c42cb8df4d11eff3812a7ce10ea1bbba2e8d Mon Sep 17 00:00:00 2001 From: Cai Wenzhong Date: Tue, 24 Oct 2023 16:48:24 +0800 Subject: [PATCH] media: i2c: add Maxim Dual GMSL2/GMSL1 to CSI-2 Deserializer driver. 1. maxim deserializer support: max96716/max96718 1.1 local maxim deserlializer depend on CONFIG_VIDEO_DES_MAXIM2C 2. maxim serializer support: max9295/max96715/max96717 2.1 remote max9295 serializer depend on CONFIG_MAXIM2C_SER_MAX9295 2.2 remote max96715 serializer depend on CONFIG_MAXIM2C_SER_MAX96715 2.3 remote max96717 serializer depend on CONFIG_MAXIM2C_SER_MAX96717 Signed-off-by: Cai Wenzhong Change-Id: Iff158e3a05927c8e3d2c17e4aa04bfc0fcc86a37 --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/maxim2c/Kconfig | 46 + drivers/media/i2c/maxim2c/Makefile | 14 + drivers/media/i2c/maxim2c/maxim2c_api.h | 103 ++ drivers/media/i2c/maxim2c/maxim2c_drv.c | 746 +++++++++++++ drivers/media/i2c/maxim2c/maxim2c_drv.h | 110 ++ drivers/media/i2c/maxim2c/maxim2c_i2c.c | 407 +++++++ drivers/media/i2c/maxim2c/maxim2c_i2c.h | 55 + drivers/media/i2c/maxim2c/maxim2c_link.c | 618 +++++++++++ drivers/media/i2c/maxim2c/maxim2c_link.h | 80 ++ .../media/i2c/maxim2c/maxim2c_mipi_txphy.c | 559 ++++++++++ .../media/i2c/maxim2c/maxim2c_mipi_txphy.h | 65 ++ drivers/media/i2c/maxim2c/maxim2c_pattern.c | 351 +++++++ drivers/media/i2c/maxim2c/maxim2c_pattern.h | 15 + drivers/media/i2c/maxim2c/maxim2c_remote.c | 434 ++++++++ drivers/media/i2c/maxim2c/maxim2c_remote.h | 36 + drivers/media/i2c/maxim2c/maxim2c_v4l2.c | 992 ++++++++++++++++++ .../media/i2c/maxim2c/maxim2c_video_pipe.c | 346 ++++++ .../media/i2c/maxim2c/maxim2c_video_pipe.h | 44 + drivers/media/i2c/maxim2c/remote_max9295.c | 331 ++++++ drivers/media/i2c/maxim2c/remote_max96715.c | 381 +++++++ drivers/media/i2c/maxim2c/remote_max96717.c | 310 ++++++ 23 files changed, 6045 insertions(+) create mode 100644 drivers/media/i2c/maxim2c/Kconfig create mode 100644 drivers/media/i2c/maxim2c/Makefile create mode 100644 drivers/media/i2c/maxim2c/maxim2c_api.h create mode 100644 drivers/media/i2c/maxim2c/maxim2c_drv.c create mode 100644 drivers/media/i2c/maxim2c/maxim2c_drv.h create mode 100644 drivers/media/i2c/maxim2c/maxim2c_i2c.c create mode 100644 drivers/media/i2c/maxim2c/maxim2c_i2c.h create mode 100644 drivers/media/i2c/maxim2c/maxim2c_link.c create mode 100644 drivers/media/i2c/maxim2c/maxim2c_link.h create mode 100644 drivers/media/i2c/maxim2c/maxim2c_mipi_txphy.c create mode 100644 drivers/media/i2c/maxim2c/maxim2c_mipi_txphy.h create mode 100644 drivers/media/i2c/maxim2c/maxim2c_pattern.c create mode 100644 drivers/media/i2c/maxim2c/maxim2c_pattern.h create mode 100644 drivers/media/i2c/maxim2c/maxim2c_remote.c create mode 100644 drivers/media/i2c/maxim2c/maxim2c_remote.h create mode 100644 drivers/media/i2c/maxim2c/maxim2c_v4l2.c create mode 100644 drivers/media/i2c/maxim2c/maxim2c_video_pipe.c create mode 100644 drivers/media/i2c/maxim2c/maxim2c_video_pipe.h create mode 100644 drivers/media/i2c/maxim2c/remote_max9295.c create mode 100644 drivers/media/i2c/maxim2c/remote_max96715.c create mode 100644 drivers/media/i2c/maxim2c/remote_max96717.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index c314d0fab2f5..6c3243f87fd7 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -674,6 +674,7 @@ config VIDEO_MAX96756 To compile this driver as a module, choose M here: the module will be called max96756. +source "drivers/media/i2c/maxim2c/Kconfig" source "drivers/media/i2c/maxim4c/Kconfig" comment "Video and audio decoders" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index f4d92b86e774..9c346ad3409e 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ obj-$(CONFIG_VIDEO_NVP6158) += nvp6158_drv/ obj-$(CONFIG_VIDEO_NVP6188) += nvp6188.o obj-$(CONFIG_VIDEO_NVP6324) += jaguar1_drv/ +obj-$(CONFIG_VIDEO_DES_MAXIM2C) += maxim2c/ obj-$(CONFIG_VIDEO_DES_MAXIM4C) += maxim4c/ obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o diff --git a/drivers/media/i2c/maxim2c/Kconfig b/drivers/media/i2c/maxim2c/Kconfig new file mode 100644 index 000000000000..b07b96ae511f --- /dev/null +++ b/drivers/media/i2c/maxim2c/Kconfig @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Maxim Dual GMSL deserializer and serializer devices +# +config VIDEO_DES_MAXIM2C + tristate "Maxim Dual GMSL deserializer support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + This driver supports the Maxim Dual GMSL2/GMSL1 deserializer. + + To compile this driver as a module, choose M here: the + module will be called maxim2c. + +menu "Maxim Dual GMSL serializer devices support" + visible if VIDEO_DES_MAXIM2C + +config MAXIM2C_SER_MAX9295 + tristate "Maxim GMSL2 serializer max9295 support" + depends on VIDEO_DES_MAXIM2C + help + This driver supports the Maxim GMSL2 max9295 serializer. + + To compile this driver as a module, choose M here: the + module will be called remote_max9295. + +config MAXIM2C_SER_MAX96715 + tristate "Maxim GMSL1 Serializer max96715 support" + depends on VIDEO_DES_MAXIM2C + help + This driver supports the Maxim GMSL1 max96715 serializer. + + To compile this driver as a module, choose M here: the + module will be called remote_max96715. + +config MAXIM2C_SER_MAX96717 + tristate "Maxim GMSL2 Serializer max96717 support" + depends on VIDEO_DES_MAXIM2C + help + This driver supports the Maxim GMSL2 max96717 serializer. + + To compile this driver as a module, choose M here: the + module will be called remote_max96717. + +endmenu diff --git a/drivers/media/i2c/maxim2c/Makefile b/drivers/media/i2c/maxim2c/Makefile new file mode 100644 index 000000000000..f9b799053064 --- /dev/null +++ b/drivers/media/i2c/maxim2c/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_DES_MAXIM2C) += maxim2c.o +maxim2c-objs += maxim2c_i2c.o \ + maxim2c_mipi_txphy.o \ + maxim2c_video_pipe.o \ + maxim2c_link.o \ + maxim2c_remote.o \ + maxim2c_pattern.o \ + maxim2c_v4l2.o \ + maxim2c_drv.o + +obj-$(CONFIG_MAXIM2C_SER_MAX9295) += remote_max9295.o +obj-$(CONFIG_MAXIM2C_SER_MAX96715) += remote_max96715.o +obj-$(CONFIG_MAXIM2C_SER_MAX96717) += remote_max96717.o diff --git a/drivers/media/i2c/maxim2c/maxim2c_api.h b/drivers/media/i2c/maxim2c/maxim2c_api.h new file mode 100644 index 000000000000..7668c1f4ae76 --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_api.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Maxim Dual GMSL Deserializer driver API function declaration + * + * Copyright (c) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + */ + +#ifndef __MAXIM2C_API_H__ +#define __MAXIM2C_API_H__ + +#include "maxim2c_i2c.h" +#include "maxim2c_link.h" +#include "maxim2c_video_pipe.h" +#include "maxim2c_mipi_txphy.h" +#include "maxim2c_remote.h" +#include "maxim2c_pattern.h" +#include "maxim2c_drv.h" + +#define MAXIM2C_NAME "maxim2c" + +/* Maxim Deserializer Test Pattern */ +#define MAXIM2C_TEST_PATTERN 0 + +/* Maxim Deserializer pwdn on/off enable */ +#define MAXIM2C_LOCAL_DES_ON_OFF_EN 0 + +/* maxim2c i2c api */ +int maxim2c_i2c_write_reg(struct i2c_client *client, + u16 reg_addr, u16 reg_len, u16 val_len, u32 reg_val); +int maxim2c_i2c_read_reg(struct i2c_client *client, + u16 reg_addr, u16 reg_len, u16 val_len, u32 *reg_val); +int maxim2c_i2c_update_reg(struct i2c_client *client, + u16 reg_addr, u16 reg_len, + u32 val_len, u32 val_mask, u32 reg_val); + +int maxim2c_i2c_write_byte(struct i2c_client *client, + u16 reg_addr, u16 reg_len, u8 reg_val); +int maxim2c_i2c_read_byte(struct i2c_client *client, + u16 reg_addr, u16 reg_len, u8 *reg_val); +int maxim2c_i2c_update_byte(struct i2c_client *client, + u16 reg_addr, u16 reg_len, u8 val_mask, u8 reg_val); + +int maxim2c_i2c_write_array(struct i2c_client *client, + const struct maxim2c_i2c_regval *regs); +int maxim2c_i2c_load_init_seq(struct device *dev, + struct device_node *node, struct maxim2c_i2c_init_seq *init_seq); +int maxim2c_i2c_run_init_seq(struct i2c_client *client, + struct maxim2c_i2c_init_seq *init_seq); + +/* maxim2c link api */ +u8 maxim2c_link_get_lock_state(maxim2c_t *maxim2c, u8 link_mask); +int maxim2c_link_oneshot_reset(maxim2c_t *maxim2c, u8 link_mask); +int maxim2c_link_mask_enable(maxim2c_t *maxim2c, u8 link_mask, bool enable); +int maxim2c_link_wait_linklock(maxim2c_t *maxim2c, u8 link_mask); +int maxim2c_link_select_remote_enable(maxim2c_t *maxim2c, u8 link_mask); +int maxim2c_link_select_remote_control(maxim2c_t *maxim2c, u8 link_mask); +int maxim2c_link_hw_init(maxim2c_t *maxim2c); +void maxim2c_link_data_init(maxim2c_t *maxim2c); +int maxim2c_link_parse_dt(maxim2c_t *maxim2c, struct device_node *of_node); + +/* maxim2c video pipe api */ +int maxim2c_video_pipe_hw_init(maxim2c_t *maxim2c); +int maxim2c_video_pipe_mask_enable(maxim2c_t *maxim2c, u8 video_pipe_mask, bool enable); +int maxim2c_video_pipe_linkid_enable(maxim2c_t *maxim2c, u8 link_id, bool enable); +void maxim2c_video_pipe_data_init(maxim2c_t *maxim2c); +int maxim2c_video_pipe_parse_dt(maxim2c_t *maxim2c, struct device_node *of_node); + +/* maxim2c mipi txphy api */ +int maxim2c_mipi_txphy_hw_init(maxim2c_t *maxim2c); +void maxim2c_mipi_txphy_data_init(maxim2c_t *maxim2c); +int maxim2c_mipi_txphy_parse_dt(maxim2c_t *maxim2c, struct device_node *of_node); +int maxim2c_mipi_txphy_enable(maxim2c_t *maxim2c, bool enable); +int maxim2c_dphy_dpll_predef_set(maxim2c_t *maxim2c, s64 link_freq_hz); +int maxim2c_mipi_csi_output(maxim2c_t *maxim2c, bool enable); + +/* maxim2c remote api */ +int maxim2c_remote_mfd_add_devices(maxim2c_t *maxim2c); +int maxim2c_remote_devices_init(maxim2c_t *maxim2c, u8 link_init_mask); +int maxim2c_remote_devices_deinit(maxim2c_t *maxim2c, u8 link_init_mask); +int maxim2c_remote_load_init_seq(maxim2c_remote_t *remote_device); +int maxim2c_remote_i2c_addr_select(maxim2c_remote_t *remote_device, u32 i2c_id); +int maxim2c_remote_i2c_client_init(maxim2c_remote_t *remote_device, + struct i2c_client *des_client); +int maxim2c_remote_device_register(maxim2c_t *maxim2c, + maxim2c_remote_t *remote_device); + +/* maxim2c v4l2 subdev api */ +int maxim2c_v4l2_subdev_init(maxim2c_t *maxim2c); +void maxim2c_v4l2_subdev_deinit(maxim2c_t *maxim2c); + +/* maxim2c driver api */ +int maxim2c_module_hw_init(maxim2c_t *maxim2c); +int maxim2c_hot_plug_detect_work_start(maxim2c_t *maxim2c); + +/* maxim2c pattern api */ +int maxim2c_pattern_hw_init(maxim2c_t *maxim2c); +int maxim2c_pattern_support_mode_init(maxim2c_t *maxim2c); +int maxim2c_pattern_data_init(maxim2c_t *maxim2c); +int maxim2c_pattern_enable(maxim2c_t *maxim2c, bool enable); + +#endif /* __MAXIM2C_API_H__ */ diff --git a/drivers/media/i2c/maxim2c/maxim2c_drv.c b/drivers/media/i2c/maxim2c/maxim2c_drv.c new file mode 100644 index 000000000000..bd5cf4cfa9ec --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_drv.c @@ -0,0 +1,746 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Dual GMSL2/GMSL1 to CSI-2 Deserializer driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + * V2.00.00 maxim serdes dual GMSL2/GMSL1 driver framework. + * 1. local deserializer support: max96716/max96718 + * 2. remote serializer support: max9295/max96715/max96717 + * 3. support deserializer and serializer auto adaptive + * 4. support deserializer output test pattern + * 5. support remote serializer channel management + * 6. support remote serializer I2c address mapping + * 7. support remote serializer hot plug detection and recovery + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "maxim2c_api.h" + +#define DRIVER_VERSION KERNEL_VERSION(2, 0x00, 0x00) + +#define MAXIM2C_XVCLK_FREQ 25000000 + +static int maxim2c_check_local_chipid(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + int ret = 0, loop = 0; + u8 chipid = 0; + + for (loop = 0; loop < 5; loop++) { + if (loop != 0) { + dev_info(dev, "check local chipid retry (%d)", loop); + msleep(10); + } + + ret = maxim2c_i2c_read_byte(client, + MAXIM2C_REG_CHIP_ID, MAXIM2C_I2C_REG_ADDR_16BITS, + &chipid); + if (ret == 0) { + if (chipid == maxim2c->chipid) { + if (chipid == MAX96716_CHIP_ID) { + dev_info(dev, "MAX96716 is Detected\n"); + return 0; + } + + if (chipid == MAX96718_CHIP_ID) { + dev_info(dev, "MAX96718 is Detected\n"); + return 0; + } + } else { + // if chipid is unexpected, retry + dev_err(dev, "Unexpected maxim chipid = %02x\n", chipid); + } + } + } + + dev_err(dev, "maxim check chipid error, ret(%d)\n", ret); + + return -ENODEV; +} + +static irqreturn_t maxim2c_hot_plug_detect_irq_handler(int irq, void *dev_id) +{ + struct maxim2c *maxim2c = dev_id; + struct device *dev = &maxim2c->client->dev; + int lock_gpio_level = 0; + + mutex_lock(&maxim2c->mutex); + if (maxim2c->streaming) { + lock_gpio_level = gpiod_get_value_cansleep(maxim2c->lock_gpio); + if (lock_gpio_level == 0) { + dev_info(dev, "serializer hot plug out\n"); + + maxim2c->hot_plug_state = MAXIM2C_HOT_PLUG_OUT; + } else { + dev_info(dev, "serializer hot plug in\n"); + + maxim2c->hot_plug_state = MAXIM2C_HOT_PLUG_IN; + } + + queue_delayed_work(maxim2c->hot_plug_work.state_check_wq, + &maxim2c->hot_plug_work.state_d_work, + msecs_to_jiffies(100)); + } + mutex_unlock(&maxim2c->mutex); + + return IRQ_HANDLED; +} + +static void maxim2c_lock_irq_init(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + int ret = 0; + + if (!IS_ERR(maxim2c->lock_gpio)) { + maxim2c->hot_plug_irq = gpiod_to_irq(maxim2c->lock_gpio); + if (maxim2c->hot_plug_irq < 0) { + dev_err(dev, "failed to get hot plug irq\n"); + } else { + ret = devm_request_threaded_irq(dev, + maxim2c->hot_plug_irq, + NULL, + maxim2c_hot_plug_detect_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "maxim2c_hot_plug", + maxim2c); + if (ret) { + dev_err(dev, "failed to request hot plug irq (%d)\n", ret); + maxim2c->hot_plug_irq = -1; + } else { + disable_irq(maxim2c->hot_plug_irq); + } + } + } +} + +static void maxim2c_hot_plug_state_check_work(struct work_struct *work) +{ + struct maxim2c_hot_plug_work *hot_plug_work = + container_of(work, struct maxim2c_hot_plug_work, state_d_work.work); + struct maxim2c *maxim2c = + container_of(hot_plug_work, struct maxim2c, hot_plug_work); + struct device *dev = &maxim2c->client->dev; + u8 curr_lock_state = 0, last_lock_state = 0, link_lock_change = 0; + u8 link_enable_mask = 0, link_id = 0; + + dev_dbg(dev, "%s\n", __func__); + + mutex_lock(&maxim2c->mutex); + if (maxim2c->streaming == 0) { + mutex_unlock(&maxim2c->mutex); + return; + } + + link_enable_mask = maxim2c->gmsl_link.link_enable_mask; + last_lock_state = maxim2c->link_lock_state; + if ((maxim2c->hot_plug_state == MAXIM2C_HOT_PLUG_OUT) + && (last_lock_state == link_enable_mask)) { + maxim2c_link_select_remote_control(maxim2c, 0); + } + + curr_lock_state = maxim2c_link_get_lock_state(maxim2c, link_enable_mask); + link_lock_change = (last_lock_state ^ curr_lock_state); + if (link_lock_change) { + dev_dbg(dev, "lock state: current = 0x%02x, last = 0x%02x\n", + curr_lock_state, last_lock_state); + + maxim2c->link_lock_state = curr_lock_state; + } + + if (link_lock_change & MAXIM2C_LINK_MASK_A) { + link_id = MAXIM2C_LINK_ID_A; + + if (curr_lock_state & MAXIM2C_LINK_MASK_A) { + dev_info(dev, "Link A plug in\n"); + + if (maxim2c->hot_plug_irq > 0) + disable_irq(maxim2c->hot_plug_irq); + + maxim2c_remote_devices_init(maxim2c, MAXIM2C_LINK_MASK_A); + + if (maxim2c->hot_plug_irq > 0) + enable_irq(maxim2c->hot_plug_irq); + + maxim2c_video_pipe_linkid_enable(maxim2c, link_id, true); + } else { + dev_info(dev, "Link A plug out\n"); + + maxim2c_video_pipe_linkid_enable(maxim2c, link_id, false); + } + } + + if (link_lock_change & MAXIM2C_LINK_MASK_B) { + link_id = MAXIM2C_LINK_ID_B; + + if (curr_lock_state & MAXIM2C_LINK_MASK_B) { + dev_info(dev, "Link B plug in\n"); + + if (maxim2c->hot_plug_irq > 0) + disable_irq(maxim2c->hot_plug_irq); + + maxim2c_remote_devices_init(maxim2c, MAXIM2C_LINK_MASK_B); + + if (maxim2c->hot_plug_irq > 0) + enable_irq(maxim2c->hot_plug_irq); + + maxim2c_video_pipe_linkid_enable(maxim2c, link_id, true); + } else { + dev_info(dev, "Link B plug out\n"); + + maxim2c_video_pipe_linkid_enable(maxim2c, link_id, false); + } + } + + if (curr_lock_state == link_enable_mask) { + // remote control mask enable + maxim2c_link_select_remote_control(maxim2c, link_enable_mask); + } else { + queue_delayed_work(maxim2c->hot_plug_work.state_check_wq, + &maxim2c->hot_plug_work.state_d_work, + msecs_to_jiffies(200)); + } + + mutex_unlock(&maxim2c->mutex); +} + +int maxim2c_hot_plug_detect_work_start(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + u8 link_lock_state = 0, link_enable_mask = 0; + + link_lock_state = maxim2c->link_lock_state; + link_enable_mask = maxim2c->gmsl_link.link_enable_mask; + + if (link_lock_state != link_enable_mask) { + dev_info(dev, "%s: link_lock = 0x%02x, link_mask = 0x%02x\n", + __func__, link_lock_state, link_enable_mask); + + maxim2c->hot_plug_state = MAXIM2C_HOT_PLUG_OUT; + + queue_delayed_work(maxim2c->hot_plug_work.state_check_wq, + &maxim2c->hot_plug_work.state_d_work, + msecs_to_jiffies(200)); + } + + return 0; +} + +static int maxim2c_lock_state_work_init(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + + INIT_DELAYED_WORK(&maxim2c->hot_plug_work.state_d_work, + maxim2c_hot_plug_state_check_work); + maxim2c->hot_plug_work.state_check_wq = + create_singlethread_workqueue("maxim2c work queue"); + if (maxim2c->hot_plug_work.state_check_wq == NULL) { + dev_err(dev, "failed to create hot plug work queue\n"); + return -ENOMEM; + } + + return 0; +} + +static int maxim2c_lock_state_work_deinit(maxim2c_t *maxim2c) +{ + if (maxim2c->hot_plug_work.state_check_wq) { + cancel_delayed_work_sync(&maxim2c->hot_plug_work.state_d_work); + destroy_workqueue(maxim2c->hot_plug_work.state_check_wq); + maxim2c->hot_plug_work.state_check_wq = NULL; + } + + return 0; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 maxim2c_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, MAXIM2C_XVCLK_FREQ / 1000 / 1000); +} + +static int maxim2c_local_device_power_on(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + + if (!IS_ERR(maxim2c->pwdn_gpio)) { + dev_info(dev, "local device pwdn gpio on\n"); + + gpiod_set_value_cansleep(maxim2c->pwdn_gpio, 1); + + usleep_range(20000, 20100); + } + + return 0; +} + +static void maxim2c_local_device_power_off(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + + if (!IS_ERR(maxim2c->pwdn_gpio)) { + dev_info(dev, "local device pwdn gpio off\n"); + + gpiod_set_value_cansleep(maxim2c->pwdn_gpio, 0); + } +} + +static int maxim2c_remote_device_power_on(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + + // remote PoC enable + if (!IS_ERR(maxim2c->pocen_gpio)) { + dev_info(dev, "remote device pocen gpio on\n"); + + gpiod_set_value_cansleep(maxim2c->pocen_gpio, 1); + usleep_range(5000, 10000); + } + + return 0; +} + +static int maxim2c_remote_device_power_off(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + + // remote PoC enable + if (!IS_ERR(maxim2c->pocen_gpio)) { + dev_info(dev, "remote device pocen gpio off\n"); + + gpiod_set_value_cansleep(maxim2c->pocen_gpio, 0); + } + + return 0; +} + +static int maxim2c_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + int ret = 0; + +#if MAXIM2C_LOCAL_DES_ON_OFF_EN + ret |= maxim2c_local_device_power_on(maxim2c); +#endif /* MAXIM2C_LOCAL_DES_ON_OFF_EN */ + + ret |= maxim2c_remote_device_power_on(maxim2c); + + return ret; +} + +static int maxim2c_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + int ret = 0; + + ret |= maxim2c_remote_device_power_off(maxim2c); + +#if MAXIM2C_LOCAL_DES_ON_OFF_EN + maxim2c_local_device_power_off(maxim2c); +#endif /* MAXIM2C_LOCAL_DES_ON_OFF_EN */ + + return ret; +} + +static const struct dev_pm_ops maxim2c_pm_ops = { + SET_RUNTIME_PM_OPS( + maxim2c_runtime_suspend, maxim2c_runtime_resume, NULL) +}; + +static void maxim2c_module_data_init(maxim2c_t *maxim2c) +{ + maxim2c_link_data_init(maxim2c); + maxim2c_video_pipe_data_init(maxim2c); + maxim2c_mipi_txphy_data_init(maxim2c); +} + +static int maxim2c_extra_init_seq_parse(maxim2c_t *maxim2c, struct device_node *node) +{ + struct device *dev = &maxim2c->client->dev; + struct device_node *init_seq_node = NULL; + struct maxim2c_i2c_init_seq *init_seq = NULL; + + init_seq_node = of_get_child_by_name(node, "extra-init-sequence"); + if (IS_ERR_OR_NULL(init_seq_node)) { + dev_dbg(dev, "%pOF no child node extra-init-sequence\n", node); + return 0; + } + + if (!of_device_is_available(init_seq_node)) { + dev_dbg(dev, "%pOF is disabled\n", init_seq_node); + + of_node_put(init_seq_node); + return 0; + } + + dev_info(dev, "load extra-init-sequence\n"); + + init_seq = &maxim2c->extra_init_seq; + maxim2c_i2c_load_init_seq(dev, + init_seq_node, init_seq); + + of_node_put(init_seq_node); + + return 0; +} + +static int maxim2c_module_parse_dt(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + struct device_node *node = NULL; + + // maxim serdes local + node = of_get_child_by_name(dev->of_node, "serdes-local-device"); + if (IS_ERR_OR_NULL(node)) { + dev_err(dev, "%pOF has no child node: serdes-local-device\n", + dev->of_node); + + return -ENODEV; + } + + if (!of_device_is_available(node)) { + dev_info(dev, "%pOF is disabled\n", node); + + of_node_put(node); + return -ENODEV; + } + + /* gmsl link parse dt */ + maxim2c_link_parse_dt(maxim2c, node); + + /* video pipe parse dt */ + maxim2c_video_pipe_parse_dt(maxim2c, node); + + /* mipi txphy parse dt */ + maxim2c_mipi_txphy_parse_dt(maxim2c, node); + + /* extra init seq parse dt */ + maxim2c_extra_init_seq_parse(maxim2c, node); + + of_node_put(node); + + return 0; +} + +static int maxim2c_run_extra_init_seq(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + int ret = 0; + + ret = maxim2c_i2c_run_init_seq(client, + &maxim2c->extra_init_seq); + if (ret) { + dev_err(dev, "extra init sequence error\n"); + return ret; + } + + return 0; +} + +static int maxim2c_module_hw_previnit(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + int ret = 0; + + // Disable data transmission through video pipe. + ret = maxim2c_i2c_update_byte(client, + 0x0002, MAXIM2C_I2C_REG_ADDR_16BITS, + 0xF0, 0x00); + if (ret) + return ret; + + // Video Pipe Y/Z Disable + ret = maxim2c_i2c_update_byte(client, + 0x0160, MAXIM2C_I2C_REG_ADDR_16BITS, + BIT(1) | BIT(0), 0); + if (ret) + return ret; + + // MIPI CSI output disable. + ret = maxim2c_i2c_write_byte(client, + 0x0313, MAXIM2C_I2C_REG_ADDR_16BITS, + 0x00); + if (ret) + return ret; + + // MIPI TXPHY standby + ret = maxim2c_i2c_update_byte(client, + 0x0332, MAXIM2C_I2C_REG_ADDR_16BITS, + 0xF0, 0x00); + if (ret) + return ret; + + return 0; +} + +static int maxim2c_module_hw_postinit(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + int ret = 0; + + // video pipe disable all + ret |= maxim2c_i2c_write_byte(client, + 0x0160, MAXIM2C_I2C_REG_ADDR_16BITS, + 0); + + // remote control disable all + ret |= maxim2c_link_select_remote_control(maxim2c, 0); + + // Enable data transmission through video pipe. + ret |= maxim2c_i2c_update_byte(client, + 0x0002, MAXIM2C_I2C_REG_ADDR_16BITS, + 0xF0, 0xF0); + + return ret; +} + +int maxim2c_module_hw_init(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + int ret = 0; + + ret = maxim2c_module_hw_previnit(maxim2c); + if (ret) { + dev_err(dev, "%s: hw prev init error\n", __func__); + + return ret; + } + + ret = maxim2c_link_hw_init(maxim2c); + if (ret) { + dev_err(dev, "%s: hw link init error\n", __func__); + return ret; + } + + ret = maxim2c_video_pipe_hw_init(maxim2c); + if (ret) { + dev_err(dev, "%s: hw pipe init error\n", __func__); + return ret; + } + + ret = maxim2c_mipi_txphy_hw_init(maxim2c); + if (ret) { + dev_err(dev, "%s: hw txphy init error\n", __func__); + return ret; + } + + ret = maxim2c_run_extra_init_seq(maxim2c); + if (ret) { + dev_err(dev, "%s: run extra init seq error\n", __func__); + return ret; + } + + ret = maxim2c_module_hw_postinit(maxim2c); + if (ret) { + dev_err(dev, "%s: hw post init error\n", __func__); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(maxim2c_module_hw_init); + +static int maxim2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + maxim2c_t *maxim2c = NULL; + u32 chip_id; + int ret = 0; + + dev_info(dev, "driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, DRIVER_VERSION & 0x00ff); + + chip_id = (uintptr_t)of_device_get_match_data(dev); + if (chip_id == MAX96716_CHIP_ID) { + dev_info(dev, "maxim2c driver for max96716\n"); + } else if (chip_id == MAX96718_CHIP_ID) { + dev_info(dev, "maxim2c driver for max96718\n"); + } else { + dev_err(dev, "maxim2c driver unknown chip\n"); + return -EINVAL; + } + + maxim2c = devm_kzalloc(dev, sizeof(*maxim2c), GFP_KERNEL); + if (!maxim2c) { + dev_err(dev, "maxim2c probe no memory error\n"); + return -ENOMEM; + } + + maxim2c->client = client; + maxim2c->chipid = chip_id; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &maxim2c->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &maxim2c->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &maxim2c->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &maxim2c->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + maxim2c->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(maxim2c->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios, maybe no use\n"); + else + usleep_range(1000, 1100); + + maxim2c->pocen_gpio = devm_gpiod_get(dev, "pocen", GPIOD_OUT_LOW); + if (IS_ERR(maxim2c->pocen_gpio)) + dev_warn(dev, "Failed to get pocen-gpios\n"); + + maxim2c->lock_gpio = devm_gpiod_get(dev, "lock", GPIOD_IN); + if (IS_ERR(maxim2c->lock_gpio)) + dev_warn(dev, "Failed to get lock-gpios\n"); + + mutex_init(&maxim2c->mutex); + + ret = maxim2c_local_device_power_on(maxim2c); + if (ret) + goto err_destroy_mutex; + + ret = maxim2c_check_local_chipid(maxim2c); + if (ret) + goto err_power_off; + + // client->dev->driver_data = subdev + // subdev->dev->driver_data = maxim2c + ret = maxim2c_v4l2_subdev_init(maxim2c); + if (ret) { + dev_err(dev, "maxim2c probe v4l2 subdev init error\n"); + goto err_power_off; + } + +#if MAXIM2C_TEST_PATTERN + ret = maxim2c_pattern_data_init(maxim2c); + if (ret) + goto err_power_off; + +#if (MAXIM2C_LOCAL_DES_ON_OFF_EN == 0) + ret = maxim2c_pattern_hw_init(maxim2c); + if (ret) + goto err_power_off; +#endif /* MAXIM2C_LOCAL_DES_ON_OFF_EN */ + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; +#endif /* MAXIM2C_TEST_PATTERN */ + + maxim2c_module_data_init(maxim2c); + maxim2c_module_parse_dt(maxim2c); + +#if (MAXIM2C_LOCAL_DES_ON_OFF_EN == 0) + ret = maxim2c_module_hw_init(maxim2c); + if (ret) + goto err_subdev_deinit; +#endif /* MAXIM2C_LOCAL_DES_ON_OFF_EN */ + + ret = maxim2c_remote_mfd_add_devices(maxim2c); + if (ret) + goto err_subdev_deinit; + + maxim2c_lock_irq_init(maxim2c); + maxim2c_lock_state_work_init(maxim2c); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_subdev_deinit: + maxim2c_v4l2_subdev_deinit(maxim2c); +err_power_off: + maxim2c_local_device_power_off(maxim2c); +err_destroy_mutex: + mutex_destroy(&maxim2c->mutex); + + return ret; +} + +static int maxim2c_remove(struct i2c_client *client) +{ + maxim2c_t *maxim2c = i2c_get_clientdata(client); + + maxim2c_lock_state_work_deinit(maxim2c); + + maxim2c_v4l2_subdev_deinit(maxim2c); + + mutex_destroy(&maxim2c->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + maxim2c_local_device_power_off(maxim2c); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static const struct of_device_id maxim2c_of_match[] = { + { + .compatible = "maxim2c,max96716", + .data = (const void *)MAX96716_CHIP_ID + }, { + .compatible = "maxim2c,max96718", + .data = (const void *)MAX96718_CHIP_ID + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, maxim2c_of_match); + +static struct i2c_driver maxim2c_i2c_driver = { + .driver = { + .name = MAXIM2C_NAME, + .pm = &maxim2c_pm_ops, + .of_match_table = of_match_ptr(maxim2c_of_match), + }, + .probe = &maxim2c_probe, + .remove = &maxim2c_remove, +}; + +module_i2c_driver(maxim2c_i2c_driver); + +MODULE_AUTHOR("Cai Wenzhong "); +MODULE_DESCRIPTION("Maxim dual gmsl deserializer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/maxim2c/maxim2c_drv.h b/drivers/media/i2c/maxim2c/maxim2c_drv.h new file mode 100644 index 000000000000..dfaad261cd93 --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_drv.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Rockchip Electronics Co., Ltd. + * + */ + +#ifndef __MAXIM2C_DRV_H__ +#define __MAXIM2C_DRV_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "maxim2c_i2c.h" +#include "maxim2c_link.h" +#include "maxim2c_mipi_txphy.h" +#include "maxim2c_remote.h" +#include "maxim2c_pattern.h" + +#define MAXIM2C_REG_CHIP_ID 0x0D +#define MAX96716_CHIP_ID 0xBE +#define MAX96718_CHIP_ID 0xB8 + +enum { + MAXIM2C_HOT_PLUG_OUT = 0, + MAXIM2C_HOT_PLUG_IN, +}; + +struct maxim2c_hot_plug_work { + struct workqueue_struct *state_check_wq; + struct delayed_work state_d_work; + u32 hot_plug_state; +}; + +struct maxim2c_mode { + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + u32 link_freq_idx; + u32 bus_fmt; + u32 bpp; + const struct regval *reg_list; + u32 vc[PAD_MAX]; +}; + +typedef struct maxim2c { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *pocen_gpio; + struct gpio_desc *lock_gpio; + + struct mutex mutex; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_fwnode_endpoint bus_cfg; + + u32 chipid; + + bool streaming; + bool power_on; + bool hot_plug; + u8 is_reset; + int hot_plug_irq; + u32 hot_plug_state; + u32 link_lock_state; + struct maxim2c_hot_plug_work hot_plug_work; + + struct maxim2c_mode supported_mode; + const struct maxim2c_mode *cur_mode; + u32 cfg_modes_num; + + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + + maxim2c_gmsl_link_t gmsl_link; + maxim2c_video_pipe_t video_pipe; + maxim2c_mipi_txphy_t mipi_txphy; + + struct maxim2c_pattern pattern; + + struct maxim2c_i2c_init_seq extra_init_seq; + + struct mfd_cell remote_mfd_devs[MAXIM2C_LINK_ID_MAX]; + maxim2c_remote_t *remote_device[MAXIM2C_LINK_ID_MAX]; +} maxim2c_t; + +#endif /* __MAXIM2C_DRV_H__ */ diff --git a/drivers/media/i2c/maxim2c/maxim2c_i2c.c b/drivers/media/i2c/maxim2c/maxim2c_i2c.c new file mode 100644 index 000000000000..2ae75000d4a1 --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_i2c.c @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Dual GMSL Deserializer I2C read/write driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + */ +#include +#include +#include +#include "maxim2c_i2c.h" + +/* Write registers up to 4 at a time */ +int maxim2c_i2c_write_reg(struct i2c_client *client, + u16 reg_addr, u16 reg_len, u32 val_len, u32 reg_val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + dev_info(&client->dev, "i2c addr(0x%02x) write: 0x%04x (%d) = 0x%08x (%d)\n", + client->addr, reg_addr, reg_len, reg_val, val_len); + + if (val_len > 4) + return -EINVAL; + + if (reg_len == 2) { + buf[0] = reg_addr >> 8; + buf[1] = reg_addr & 0xff; + + buf_i = 2; + } else { + buf[0] = reg_addr & 0xff; + + buf_i = 1; + } + + val_be = cpu_to_be32(reg_val); + val_p = (u8 *)&val_be; + val_i = 4 - val_len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, (val_len + reg_len)) != (val_len + reg_len)) { + dev_err(&client->dev, + "%s: writing register 0x%04x from 0x%02x failed\n", + __func__, reg_addr, client->addr); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL(maxim2c_i2c_write_reg); + +/* Read registers up to 4 at a time */ +int maxim2c_i2c_read_reg(struct i2c_client *client, + u16 reg_addr, u16 reg_len, u32 val_len, u32 *reg_val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg_addr); + u8 *reg_be_p; + int ret; + + if (val_len > 4 || !val_len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + reg_be_p = (u8 *)®_addr_be; + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = reg_len; + msgs[0].buf = ®_be_p[2 - reg_len]; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = val_len; + msgs[1].buf = &data_be_p[4 - val_len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, + "%s: reading register 0x%04x from 0x%02x failed\n", + __func__, reg_addr, client->addr); + return -EIO; + } + + *reg_val = be32_to_cpu(data_be); + +#if 0 + dev_info(&client->dev, "i2c addr(0x%02x) read: 0x%04x (%d) = 0x%08x (%d)\n", + client->addr, reg_addr, reg_len, *reg_val, val_len); +#endif + + return 0; +} +EXPORT_SYMBOL(maxim2c_i2c_read_reg); + +/* Update registers up to 4 at a time */ +int maxim2c_i2c_update_reg(struct i2c_client *client, + u16 reg_addr, u16 reg_len, u32 val_len, u32 val_mask, u32 reg_val) +{ + u32 value; + int ret; + + ret = maxim2c_i2c_read_reg(client, reg_addr, reg_len, val_len, &value); + if (ret) + return ret; + + value &= ~val_mask; + value |= (reg_val & val_mask); + ret = maxim2c_i2c_write_reg(client, reg_addr, reg_len, val_len, value); + + return ret; +} +EXPORT_SYMBOL(maxim2c_i2c_update_reg); + +int maxim2c_i2c_write_byte(struct i2c_client *client, + u16 reg_addr, u16 reg_len, u8 reg_val) +{ + int ret = 0; + + ret = maxim2c_i2c_write_reg(client, + reg_addr, reg_len, + MAXIM2C_I2C_REG_VALUE_08BITS, reg_val); + + return ret; +} +EXPORT_SYMBOL(maxim2c_i2c_write_byte); + +int maxim2c_i2c_read_byte(struct i2c_client *client, + u16 reg_addr, u16 reg_len, u8 *reg_val) +{ + int ret = 0; + u32 value = 0; + u8 *value_be_p = (u8 *)&value; + + ret = maxim2c_i2c_read_reg(client, + reg_addr, reg_len, + MAXIM2C_I2C_REG_VALUE_08BITS, &value); + + *reg_val = *value_be_p; + + return ret; +} +EXPORT_SYMBOL(maxim2c_i2c_read_byte); + +int maxim2c_i2c_update_byte(struct i2c_client *client, + u16 reg_addr, u16 reg_len, u8 val_mask, u8 reg_val) +{ + u8 value; + int ret; + + ret = maxim2c_i2c_read_byte(client, reg_addr, reg_len, &value); + if (ret) + return ret; + + value &= ~val_mask; + value |= (reg_val & val_mask); + ret = maxim2c_i2c_write_byte(client, reg_addr, reg_len, value); + + return ret; +} +EXPORT_SYMBOL(maxim2c_i2c_update_byte); + +int maxim2c_i2c_write_array(struct i2c_client *client, + const struct maxim2c_i2c_regval *regs) +{ + u32 i = 0; + int ret = 0; + + for (i = 0; (ret == 0) && (regs[i].reg_addr != MAXIM2C_REG_NULL); i++) { + if (regs[i].val_mask != 0) + ret = maxim2c_i2c_update_reg(client, + regs[i].reg_addr, regs[i].reg_len, + regs[i].val_len, regs[i].val_mask, regs[i].reg_val); + else + ret = maxim2c_i2c_write_reg(client, + regs[i].reg_addr, regs[i].reg_len, + regs[i].val_len, regs[i].reg_val); + + if (regs[i].delay != 0) + usleep_range(regs[i].delay * 1000, regs[i].delay * 1000 + 100); + } + + return ret; +} +EXPORT_SYMBOL(maxim2c_i2c_write_array); + +static int maxim2c_i2c_parse_init_seq(struct device *dev, + const u8 *seq_data, int data_len, struct maxim2c_i2c_init_seq *init_seq) +{ + struct maxim2c_i2c_regval *reg_val = NULL; + u8 *data_buf = NULL, *d8 = NULL; + u32 i = 0; + + if ((seq_data == NULL) || (init_seq == NULL)) { + dev_err(dev, "%s: input parameter = NULL\n", __func__); + return -EINVAL; + } + + if ((init_seq->seq_item_size == 0) + || (data_len == 0) + || (init_seq->reg_len == 0) + || (init_seq->val_len == 0)) { + dev_err(dev, "%s: input parameter size zero\n", __func__); + return -EINVAL; + } + + // data_len = seq_item_size * N + if (data_len % init_seq->seq_item_size) { + dev_err(dev, "%s: data_len or seq_item_size error\n", __func__); + return -EINVAL; + } + + // seq_item_size = reg_len + val_len * 2 + 1 + if (init_seq->seq_item_size != + (init_seq->reg_len + init_seq->val_len * 2 + 1)) { + dev_err(dev, "%s: seq_item_size or reg_len or val_len error\n", __func__); + return -EINVAL; + } + + data_buf = devm_kmemdup(dev, seq_data, data_len, GFP_KERNEL); + if (!data_buf) { + dev_err(dev, "%s data buf error\n", __func__); + return -ENOMEM; + } + + d8 = data_buf; + + init_seq->reg_seq_size = data_len / init_seq->seq_item_size; + init_seq->reg_seq_size += 1; // add 1 for end register setting + + init_seq->reg_init_seq = devm_kcalloc(dev, init_seq->reg_seq_size, + sizeof(struct maxim2c_i2c_regval), GFP_KERNEL); + if (!init_seq->reg_init_seq) { + dev_err(dev, "%s init seq buffer error\n", __func__); + return -ENOMEM; + } + + for (i = 0; i < init_seq->reg_seq_size - 1; i++) { + reg_val = &init_seq->reg_init_seq[i]; + + reg_val->reg_len = init_seq->reg_len; + reg_val->val_len = init_seq->val_len; + + reg_val->reg_addr = 0; + switch (init_seq->reg_len) { + case 4: + reg_val->reg_addr |= (*d8 << 24); + d8 += 1; + fallthrough; + case 3: + reg_val->reg_addr |= (*d8 << 16); + d8 += 1; + fallthrough; + case 2: + reg_val->reg_addr |= (*d8 << 8); + d8 += 1; + fallthrough; + case 1: + reg_val->reg_addr |= (*d8 << 0); + d8 += 1; + break; + } + + reg_val->reg_val = 0; + switch (init_seq->val_len) { + case 4: + reg_val->reg_val |= (*d8 << 24); + d8 += 1; + fallthrough; + case 3: + reg_val->reg_val |= (*d8 << 16); + d8 += 1; + fallthrough; + case 2: + reg_val->reg_val |= (*d8 << 8); + d8 += 1; + fallthrough; + case 1: + reg_val->reg_val |= (*d8 << 0); + d8 += 1; + break; + } + + reg_val->val_mask = 0; + switch (init_seq->val_len) { + case 4: + reg_val->val_mask |= (*d8 << 24); + d8 += 1; + fallthrough; + case 3: + reg_val->val_mask |= (*d8 << 16); + d8 += 1; + fallthrough; + case 2: + reg_val->val_mask |= (*d8 << 8); + d8 += 1; + fallthrough; + case 1: + reg_val->val_mask |= (*d8 << 0); + d8 += 1; + break; + } + + reg_val->delay = *d8; + d8 += 1; + } + + // End register setting + init_seq->reg_init_seq[init_seq->reg_seq_size - 1].reg_len = init_seq->reg_len; + init_seq->reg_init_seq[init_seq->reg_seq_size - 1].reg_addr = MAXIM2C_REG_NULL; + + return 0; +} + +int maxim2c_i2c_load_init_seq(struct device *dev, + struct device_node *node, struct maxim2c_i2c_init_seq *init_seq) +{ + const void *init_seq_data = NULL; + u32 seq_data_len = 0, value = 0; + int ret = 0; + + if ((node == NULL) || (init_seq == NULL)) { + dev_err(dev, "%s input parameter error\n", __func__); + return -EINVAL; + } + + init_seq->reg_init_seq = NULL; + init_seq->reg_seq_size = 0; + + if (!of_device_is_available(node)) { + dev_info(dev, "%pOF is disabled\n", node); + + return 0; + } + + init_seq_data = of_get_property(node, "init-sequence", &seq_data_len); + if (!init_seq_data) { + dev_err(dev, "failed to get property init-sequence\n"); + return -EINVAL; + } + if (seq_data_len == 0) { + dev_err(dev, "init-sequence date is empty\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "seq-item-size", &value); + if (ret) { + dev_err(dev, "failed to get property seq-item-size\n"); + return -EINVAL; + } else { + dev_info(dev, "seq-item-size property: %d", value); + init_seq->seq_item_size = value; + } + + ret = of_property_read_u32(node, "reg-addr-len", &value); + if (ret) { + dev_err(dev, "failed to get property reg-addr-len\n"); + return -EINVAL; + } else { + dev_info(dev, "reg-addr-len property: %d", value); + init_seq->reg_len = value; + } + + ret = of_property_read_u32(node, "reg-val-len", &value); + if (ret) { + dev_err(dev, "failed to get property reg-val-len\n"); + return -EINVAL; + } else { + dev_info(dev, "reg-val-len property: %d", value); + init_seq->val_len = value; + } + + ret = maxim2c_i2c_parse_init_seq(dev, + init_seq_data, seq_data_len, init_seq); + if (ret) { + dev_err(dev, "failed to parse init-sequence\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(maxim2c_i2c_load_init_seq); + +int maxim2c_i2c_run_init_seq(struct i2c_client *client, + struct maxim2c_i2c_init_seq *init_seq) +{ + int ret = 0; + + if (init_seq == NULL || init_seq->reg_init_seq == NULL) + return 0; + + ret = maxim2c_i2c_write_array(client, + init_seq->reg_init_seq); + return ret; +} +EXPORT_SYMBOL(maxim2c_i2c_run_init_seq); diff --git a/drivers/media/i2c/maxim2c/maxim2c_i2c.h b/drivers/media/i2c/maxim2c/maxim2c_i2c.h new file mode 100644 index 000000000000..620a4ffb7678 --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_i2c.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Rockchip Electronics Co., Ltd. + * + */ + +#ifndef __MAXIM2C_I2C_H__ +#define __MAXIM2C_I2C_H__ + +#include + +/* register address: 8bit or 16bit */ +#define MAXIM2C_I2C_REG_ADDR_08BITS 1 +#define MAXIM2C_I2C_REG_ADDR_16BITS 2 + +/* register value: 8bit or 16bit or 24bit */ +#define MAXIM2C_I2C_REG_VALUE_08BITS 1 +#define MAXIM2C_I2C_REG_VALUE_16BITS 2 +#define MAXIM2C_I2C_REG_VALUE_24BITS 3 + +/* I2C Device ID */ +enum { + MAXIM2C_I2C_DES_DEF, /* Deserializer I2C address: Default */ + + MAXIM2C_I2C_SER_DEF, /* Serializer I2C address: Default */ + MAXIM2C_I2C_SER_MAP, /* Serializer I2C address: Mapping */ + + MAXIM2C_I2C_CAM_DEF, /* Camera I2C address: Default */ + MAXIM2C_I2C_CAM_MAP, /* Camera I2C address: Mapping */ + + MAXIM2C_I2C_DEV_MAX, +}; + +/* i2c register array end */ +#define MAXIM2C_REG_NULL 0xFFFF + +struct maxim2c_i2c_regval { + u16 reg_len; + u16 reg_addr; + u32 val_len; + u32 reg_val; + u32 val_mask; + u8 delay; +}; + +/* seq_item_size = reg_len + val_len * 2 + 1 */ +struct maxim2c_i2c_init_seq { + struct maxim2c_i2c_regval *reg_init_seq; + u32 reg_seq_size; + u32 seq_item_size; + u32 reg_len; + u32 val_len; +}; + +#endif /* __MAXIM2C_I2C_H__ */ diff --git a/drivers/media/i2c/maxim2c/maxim2c_link.c b/drivers/media/i2c/maxim2c/maxim2c_link.c new file mode 100644 index 000000000000..4b17335dd7b4 --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_link.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Dual GMSL Deserializer Link driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + */ +#include +#include "maxim2c_api.h" + +static int maxim2c_link_enable_vdd_ldo1(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + int ret = 0; + + /* IF VDD = 1.2V: Enable REG_ENABLE and REG_MNL + * CTRL0: Enable REG_ENABLE + * CTRL2: Enable REG_MNL + */ + ret |= maxim2c_i2c_update_byte(client, + 0x0010, MAXIM2C_I2C_REG_ADDR_16BITS, BIT(2), BIT(2)); + ret |= maxim2c_i2c_update_byte(client, + 0x0012, MAXIM2C_I2C_REG_ADDR_16BITS, BIT(4), BIT(4)); + + return ret; +} + +static int maxim2c_link_set_rate(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + maxim2c_gmsl_link_t *gmsl_link = &maxim2c->gmsl_link; + struct maxim2c_link_cfg *link_cfg = NULL; + u8 link_rate = 0; + int ret = 0; + + /* Link A rate setting */ + link_rate = 0; /* default Transmitter Rate is 187.5Mbps */ + link_cfg = &gmsl_link->link_cfg[MAXIM2C_LINK_ID_A]; + if (link_cfg->link_enable) { + /* Link A: Receiver Rate */ + if (link_cfg->link_rx_rate == MAXIM2C_LINK_RX_RATE_3GBPS) + link_rate |= (0x1 << 0); + else + link_rate |= (0x2 << 0); + + ret |= maxim2c_i2c_update_byte(client, + 0x0001, MAXIM2C_I2C_REG_ADDR_16BITS, + 0x0F, link_rate); + } + + /* Link B rate setting */ + link_rate = 0; /* default Transmitter Rate is 187.5Mbps */ + link_cfg = &gmsl_link->link_cfg[MAXIM2C_LINK_ID_B]; + if (link_cfg->link_enable) { + /* Link B: Receiver Rate */ + if (link_cfg->link_rx_rate == MAXIM2C_LINK_RX_RATE_3GBPS) + link_rate |= (0x1 << 0); + else + link_rate |= (0x2 << 0); + + ret |= maxim2c_i2c_update_byte(client, + 0x0004, MAXIM2C_I2C_REG_ADDR_16BITS, + 0x0F, link_rate); + } + + return ret; +} + +static int maxim2c_link_run_init_seq(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + maxim2c_gmsl_link_t *gmsl_link = &maxim2c->gmsl_link; + struct maxim2c_link_cfg *link_cfg = NULL; + struct maxim2c_i2c_init_seq *init_seq = NULL; + int link_idx = 0; + int ret = 0; + + // link init sequence + for (link_idx = 0; link_idx < MAXIM2C_LINK_ID_MAX; link_idx++) { + link_cfg = &gmsl_link->link_cfg[link_idx]; + init_seq = &link_cfg->link_init_seq; + ret = maxim2c_i2c_run_init_seq(client, init_seq); + if (ret) { + dev_err(dev, "link id = %d init sequence error\n", link_idx); + return ret; + } + } + + return 0; +} + +static int maxim2c_link_status_init(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + maxim2c_gmsl_link_t *gmsl_link = &maxim2c->gmsl_link; + struct maxim2c_link_cfg *link_cfg = NULL; + u8 reg_mask = 0, reg_value = 0; + u16 reg_addr = 0; + int ret = 0, link_idx = 0; + + gmsl_link->link_enable_mask = 0x00; + gmsl_link->link_type_mask = 0x03; + gmsl_link->link_locked_mask = 0; + + reg_mask = 0xC0; + reg_value = 0xC0; /* default GMSL2 */ + for (link_idx = 0; link_idx < MAXIM2C_LINK_ID_MAX; link_idx++) { + link_cfg = &gmsl_link->link_cfg[link_idx]; + if (link_cfg->link_enable) { + gmsl_link->link_enable_mask |= BIT(link_idx); + + if (link_cfg->link_type == MAXIM2C_GMSL1) { + gmsl_link->link_type_mask &= ~BIT(link_idx); + reg_value &= ~BIT(6 + link_idx); + } + } + } + ret |= maxim2c_i2c_update_byte(client, + 0x0006, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + + // AUTO_LINK disable, LINK_CFG for Link A and Link B select + reg_mask = BIT(4) | BIT(1) | BIT(0); + reg_value = 0; + for (link_idx = 0; link_idx < MAXIM2C_LINK_ID_MAX; link_idx++) { + link_cfg = &gmsl_link->link_cfg[link_idx]; + if (link_cfg->link_enable) + reg_value |= BIT(link_idx); + } + ret |= maxim2c_i2c_update_byte(client, + 0x0010, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + + // GMSL1 Link disable forward and reverse control channel + reg_mask = BIT(1) | BIT(0); + reg_value = 0; + for (link_idx = 0; link_idx < MAXIM2C_LINK_ID_MAX; link_idx++) { + reg_addr = 0x0B04 + 0x100 * link_idx; + ret |= maxim2c_i2c_update_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + } + + // GMSL2 Link disable remote control channel + reg_mask = BIT(4); + reg_value = BIT(4); + reg_addr = 0x0001; + ret |= maxim2c_i2c_update_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + + reg_mask = BIT(2); + reg_value = BIT(2); + reg_addr = 0x0003; + ret |= maxim2c_i2c_update_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + + return ret; +} + +u8 maxim2c_link_get_lock_state(maxim2c_t *maxim2c, u8 link_mask) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + maxim2c_gmsl_link_t *gmsl_link = &maxim2c->gmsl_link; + u8 link_type = 0, link_lock = 0, lock_state = 0; + + dev_dbg(dev, "%s, link_mask = 0x%x\n", __func__, link_mask); + + // Link A + if (link_mask & MAXIM2C_LINK_MASK_A) { + link_type = gmsl_link->link_cfg[MAXIM2C_LINK_ID_A].link_type; + if (link_type == MAXIM2C_GMSL2) { + // GMSL2 Link A + maxim2c_i2c_read_byte(client, + 0x0013, MAXIM2C_I2C_REG_ADDR_16BITS, + &link_lock); + if (link_lock & BIT(3)) { + lock_state |= MAXIM2C_LINK_MASK_A; + dev_dbg(dev, "GMSL2 Link A locked\n"); + } + } else { + // GMSL1 Link A + maxim2c_i2c_read_byte(client, + 0x0BCB, MAXIM2C_I2C_REG_ADDR_16BITS, + &link_lock); + if (link_lock & BIT(0)) { + lock_state |= MAXIM2C_LINK_MASK_A; + dev_dbg(dev, "GMSL1 Link A locked\n"); + } + } + + // record link lock + if (lock_state & MAXIM2C_LINK_MASK_A) + gmsl_link->link_locked_mask |= MAXIM2C_LINK_MASK_A; + else + gmsl_link->link_locked_mask &= ~MAXIM2C_LINK_MASK_A; + } + + // Link B + if (link_mask & MAXIM2C_LINK_MASK_B) { + link_type = gmsl_link->link_cfg[MAXIM2C_LINK_ID_B].link_type; + if (link_type == MAXIM2C_GMSL2) { + // GMSL2 Link B + maxim2c_i2c_read_byte(client, + 0x5009, MAXIM2C_I2C_REG_ADDR_16BITS, + &link_lock); + if (link_lock & BIT(3)) { + lock_state |= MAXIM2C_LINK_MASK_B; + dev_dbg(dev, "GMSL2 Link B locked\n"); + } + } else { + // GMSL1 Link B + maxim2c_i2c_read_byte(client, + 0x0CCB, MAXIM2C_I2C_REG_ADDR_16BITS, + &link_lock); + if (link_lock & BIT(0)) { + lock_state |= MAXIM2C_LINK_MASK_B; + dev_dbg(dev, "GMSL1 Link B locked\n"); + } + } + + // record link lock + if (lock_state & MAXIM2C_LINK_MASK_B) + gmsl_link->link_locked_mask |= MAXIM2C_LINK_MASK_B; + else + gmsl_link->link_locked_mask &= ~MAXIM2C_LINK_MASK_B; + } + + return lock_state; +} +EXPORT_SYMBOL(maxim2c_link_get_lock_state); + +int maxim2c_link_oneshot_reset(struct maxim2c *maxim2c, u8 link_mask) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + maxim2c_gmsl_link_t *gmsl_link = &maxim2c->gmsl_link; + struct maxim2c_link_cfg *link_cfg = NULL; + int ret = 0, link_idx = 0; + + dev_dbg(dev, "%s, link_mask = 0x%x\n", __func__, link_mask); + + // Link A + if (link_mask & MAXIM2C_LINK_MASK_A) { + link_idx = MAXIM2C_LINK_ID_A; + link_cfg = &gmsl_link->link_cfg[link_idx]; + if (link_cfg->link_enable && (link_mask & BIT(link_idx))) { + ret = maxim2c_i2c_update_byte(client, + 0x0010, MAXIM2C_I2C_REG_ADDR_16BITS, + BIT(5), BIT(5)); + if (ret) { + dev_err(dev, "Link A oneshot reset error\n"); + return ret; + } + } + } + + // Link B + if (link_mask & MAXIM2C_LINK_MASK_B) { + link_idx = MAXIM2C_LINK_ID_B; + link_cfg = &gmsl_link->link_cfg[link_idx]; + if (link_cfg->link_enable && (link_mask & BIT(link_idx))) { + ret = maxim2c_i2c_update_byte(client, + 0x0012, MAXIM2C_I2C_REG_ADDR_16BITS, + BIT(5), BIT(5)); + if (ret) { + dev_err(dev, "Link B oneshot reset error\n"); + return ret; + } + } + } + + return 0; +} +EXPORT_SYMBOL(maxim2c_link_oneshot_reset); + +int maxim2c_link_mask_enable(struct maxim2c *maxim2c, u8 link_mask, bool enable) +{ + return 0; +} +EXPORT_SYMBOL(maxim2c_link_mask_enable); + +int maxim2c_link_wait_linklock(struct maxim2c *maxim2c, u8 link_mask) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + u8 lock_state = 0, link_bit_mask = 0; + int loop_idx = 0, time_ms = 0, link_idx = 0; + + time_ms = 50; + msleep(time_ms); + + for (loop_idx = 0; loop_idx < 20; loop_idx++) { + if (loop_idx != 0) { + msleep(10); + time_ms += 10; + } + + for (link_idx = 0; link_idx < MAXIM2C_LINK_ID_MAX; link_idx++) { + link_bit_mask = BIT(link_idx); + + if ((link_mask & link_bit_mask) + && ((lock_state & link_bit_mask) == 0)) { + if (maxim2c_link_get_lock_state(maxim2c, link_bit_mask)) { + lock_state |= link_bit_mask; + dev_info(dev, "Link %c locked time: %d ms\n", + 'A' + link_idx, time_ms); + } + } + } + + if ((lock_state & link_mask) == link_mask) { + dev_info(dev, "All Links are locked: 0x%x, time_ms = %d\n", + lock_state, time_ms); + maxim2c->link_lock_state = lock_state; + return 0; + } + } + + if ((lock_state & link_mask) != 0) { + dev_info(dev, "Partial links are locked: 0x%x, time_ms = %d\n", + lock_state, time_ms); + maxim2c->link_lock_state = lock_state; + return 0; + } else { + dev_err(dev, "Failed to detect remote link, time_ms = %d!\n", time_ms); + maxim2c->link_lock_state = 0; + return -ENODEV; + } +} +EXPORT_SYMBOL(maxim2c_link_wait_linklock); + +int maxim2c_link_select_remote_enable(struct maxim2c *maxim2c, u8 link_mask) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + int ret = 0; + + dev_dbg(dev, "%s, link_mask = 0x%x\n", __func__, link_mask); + + ret = maxim2c_link_oneshot_reset(maxim2c, link_mask); + if (ret) { + dev_err(dev, "%s: link oneshot reset error, link mask = 0x%x\n", + __func__, link_mask); + return ret; + } + + ret = maxim2c_link_mask_enable(maxim2c, link_mask, true); + if (ret) { + dev_err(dev, "%s: link enable error, link mask = 0x%x\n", + __func__, link_mask); + return ret; + } + + maxim2c_link_wait_linklock(maxim2c, link_mask); + dev_info(dev, "link_mask = 0x%02x, link_lock = 0x%02x\n", + link_mask, maxim2c->link_lock_state); + + return 0; +} +EXPORT_SYMBOL(maxim2c_link_select_remote_enable); + +int maxim2c_link_select_remote_control(struct maxim2c *maxim2c, u8 link_mask) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + maxim2c_gmsl_link_t *gmsl_link = &maxim2c->gmsl_link; + struct maxim2c_link_cfg *link_cfg = NULL; + u8 reg_mask = 0, reg_value = 0; + u16 reg_addr = 0; + int link_idx = 0, ret = 0; + + dev_dbg(dev, "%s, link mask = 0x%x\n", __func__, link_mask); + + for (link_idx = 0; link_idx < MAXIM2C_LINK_ID_MAX; link_idx++) { + link_cfg = &gmsl_link->link_cfg[link_idx]; + if (link_cfg->link_enable == 0) + continue; + + if (link_cfg->link_type == MAXIM2C_GMSL1) { + // GMSL1 Link forward and reverse control channel + reg_mask = BIT(1) | BIT(0); + + if (link_mask & BIT(link_idx)) + // GMSL1: Enable control channel transmitter + reg_value = BIT(1) | BIT(0); + else + // GMSL1: Disable control channel transmitter + reg_value = 0; + + reg_addr = 0x0B04 + 0x100 * link_idx; + ret |= maxim2c_i2c_update_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + } else { + // GMSL2 Link remote control channel + if (link_idx == MAXIM2C_LINK_ID_A) { + reg_addr = 0x0001; + reg_mask = BIT(4); + + if (link_mask & BIT(link_idx)) + reg_value = 0; + else + reg_value = BIT(4); // Link A remote control channel disabled + ret |= maxim2c_i2c_update_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + } else { + reg_addr = 0x0003; + reg_mask = BIT(2); + if (link_mask & BIT(link_idx)) + reg_value = 0; + else + reg_value = BIT(2); // Link B remote control channel disabled + ret |= maxim2c_i2c_update_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + } + } + } + + return ret; +} +EXPORT_SYMBOL(maxim2c_link_select_remote_control); + +static int maxim2c_gmsl_link_config_parse_dt(struct device *dev, + maxim2c_gmsl_link_t *gmsl_link, + struct device_node *parent_node) +{ + struct device_node *node = NULL; + struct device_node *init_seq_node = NULL; + struct maxim2c_i2c_init_seq *init_seq = NULL; + struct maxim2c_link_cfg *link_cfg = NULL; + const char *link_cfg_name = "gmsl-link-config"; + u32 value = 0; + u32 sub_idx = 0, link_id = 0; + int ret = 0; + + node = NULL; + sub_idx = 0; + while ((node = of_get_next_child(parent_node, node))) { + if (!strncasecmp(node->name, + link_cfg_name, + strlen(link_cfg_name))) { + if (sub_idx >= MAXIM2C_LINK_ID_MAX) { + dev_err(dev, "%pOF: Too many matching %s node\n", + parent_node, link_cfg_name); + + of_node_put(node); + break; + } + + if (!of_device_is_available(node)) { + dev_info(dev, "%pOF is disabled\n", node); + + sub_idx++; + + continue; + } + + /* GMSL LINK: link id */ + ret = of_property_read_u32(node, "link-id", &link_id); + if (ret) { + // if link_id is error, parse next node + dev_err(dev, "Can not get link-id property!"); + + sub_idx++; + + continue; + } + if (link_id >= MAXIM2C_LINK_ID_MAX) { + // if link_id is error, parse next node + dev_err(dev, "Error link-id = %d!", link_id); + + sub_idx++; + + continue; + } + + link_cfg = &gmsl_link->link_cfg[link_id]; + + /* GMSL LINK: link enable */ + link_cfg->link_enable = 1; + + dev_info(dev, "gmsl link id = %d: link_enable = %d\n", + link_id, link_cfg->link_enable); + + /* GMSL LINK: other config */ + ret = of_property_read_u32(node, "link-type", &value); + if (ret == 0) { + dev_info(dev, "link-type property: %d", value); + link_cfg->link_type = value; + } + + ret = of_property_read_u32(node, "link-rx-rate", &value); + if (ret == 0) { + dev_info(dev, "link-rx-rate property: %d", value); + link_cfg->link_rx_rate = value; + } + + /* link init sequence */ + init_seq_node = of_get_child_by_name(node, "link-init-sequence"); + if (!IS_ERR_OR_NULL(init_seq_node)) { + dev_info(dev, "load pipe-init-sequence\n"); + + init_seq = &link_cfg->link_init_seq; + maxim2c_i2c_load_init_seq(dev, + init_seq_node, init_seq); + + of_node_put(init_seq_node); + } + + sub_idx++; + } + } + + return 0; +} + +int maxim2c_link_parse_dt(maxim2c_t *maxim2c, struct device_node *of_node) +{ + struct device *dev = &maxim2c->client->dev; + struct device_node *node = NULL; + maxim2c_gmsl_link_t *gmsl_link = &maxim2c->gmsl_link; + u32 value = 0; + int ret = 0; + + dev_info(dev, "=== maxim2c link parse dt ===\n"); + + node = of_get_child_by_name(of_node, "gmsl-links"); + if (IS_ERR_OR_NULL(node)) { + dev_err(dev, "%pOF has no child node: gmsl-links\n", + of_node); + return -ENODEV; + } + + if (!of_device_is_available(node)) { + dev_info(dev, "%pOF is disabled\n", node); + of_node_put(node); + return -ENODEV; + } + + /* vdd 1.2v ldo1 enable */ + ret = of_property_read_u32(node, "link-vdd-ldo1-en", &value); + if (ret == 0) { + dev_info(dev, "link-vdd-ldo1-en property: %d\n", value); + gmsl_link->link_vdd_ldo1_en = value; + } + + ret = maxim2c_gmsl_link_config_parse_dt(dev, gmsl_link, node); + + of_node_put(node); + + return ret; +} +EXPORT_SYMBOL(maxim2c_link_parse_dt); + +int maxim2c_link_hw_init(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + maxim2c_gmsl_link_t *gmsl_link = &maxim2c->gmsl_link; + int ret = 0; + + // All links disable at beginning. + ret = maxim2c_link_status_init(maxim2c); + if (ret) { + dev_err(dev, "%s: link status error\n", __func__); + return ret; + } + + if (gmsl_link->link_vdd_ldo1_en) + ret |= maxim2c_link_enable_vdd_ldo1(maxim2c); + if (ret) { + dev_err(dev, "%s: link vdd ldo enable error\n", __func__); + return ret; + } + + // Link Rate Setting + ret = maxim2c_link_set_rate(maxim2c); + if (ret) { + dev_err(dev, "%s: link set rate error\n", __func__); + return ret; + } + + // link init sequence + ret = maxim2c_link_run_init_seq(maxim2c); + if (ret) { + dev_err(dev, "%s: link run init seq error\n", __func__); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(maxim2c_link_hw_init); + +void maxim2c_link_data_init(maxim2c_t *maxim2c) +{ + maxim2c_gmsl_link_t *gmsl_link = &maxim2c->gmsl_link; + struct maxim2c_link_cfg *link_cfg = NULL; + int i = 0; + + gmsl_link->link_vdd_ldo1_en = 0; + + for (i = 0; i < MAXIM2C_LINK_ID_MAX; i++) { + link_cfg = &gmsl_link->link_cfg[i]; + + link_cfg->link_enable = 0; + link_cfg->link_type = MAXIM2C_GMSL2; + link_cfg->link_tx_rate = MAXIM2C_LINK_TX_RATE_187_5MPS; + link_cfg->link_init_seq.reg_init_seq = NULL; + } +} +EXPORT_SYMBOL(maxim2c_link_data_init); diff --git a/drivers/media/i2c/maxim2c/maxim2c_link.h b/drivers/media/i2c/maxim2c/maxim2c_link.h new file mode 100644 index 000000000000..3708ee95b584 --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_link.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Rockchip Electronics Co., Ltd. + * + */ + +#ifndef __MAXIM2C_LINK_H__ +#define __MAXIM2C_LINK_H__ + +#include "maxim2c_i2c.h" + +/* Link cable */ +enum maxim2c_link_cable { + MAXIM2C_CABLE_COAX = 0, + MAXIM2C_CABLE_STP, +}; + +/* Link Type */ +enum maxim2c_link_type { + MAXIM2C_GMSL1 = 0, + MAXIM2C_GMSL2, +}; + +/* Link Mode */ +enum maxim2c_link_mode { + MAXIM2C_GMSL_PIXEL = 0, + MAXIM2C_GMSL_TUNNEL, +}; + +/* I2C Remote Control Port */ +enum { + MAXIM2C_I2C_PORT0 = 0, + MAXIM2C_I2C_PORT1, + MAXIM2C_I2C_PORT2, + MAXIM2C_I2C_PORT_MAX, +}; + +/* Link SIO ID: 0 ~ 3 */ +enum { + MAXIM2C_LINK_ID_A = 0, + MAXIM2C_LINK_ID_B, + MAXIM2C_LINK_ID_MAX, +}; + +/* Link Bit Mask: bit0 ~ bit3 */ +#define MAXIM2C_LINK_MASK_A BIT(MAXIM2C_LINK_ID_A) +#define MAXIM2C_LINK_MASK_B BIT(MAXIM2C_LINK_ID_B) + +#define MAXIM2C_LINK_MASK_ALL GENMASK(MAXIM2C_LINK_ID_B, MAXIM2C_LINK_ID_A) + +/* Link Receiver Rate */ +enum maxim2c_link_rx_rate { + MAXIM2C_LINK_RX_RATE_3GBPS = 0, + MAXIM2C_LINK_RX_RATE_6GBPS, +}; + +/* Link Transmitter Rate */ +enum maxim2c_link_tx_rate { + MAXIM2C_LINK_TX_RATE_187_5MPS = 0, +}; + +struct maxim2c_link_cfg { + u8 link_enable; + u8 link_type; + u8 link_rx_rate; + u8 link_tx_rate; + + struct maxim2c_i2c_init_seq link_init_seq; +}; + +typedef struct maxim2c_gmsl_link { + u8 link_enable_mask; + u8 link_type_mask; + u8 link_locked_mask; + u8 link_vdd_ldo1_en; + + struct maxim2c_link_cfg link_cfg[MAXIM2C_LINK_ID_MAX]; +} maxim2c_gmsl_link_t; + +#endif /* __MAXIM2C_LINK_H__ */ diff --git a/drivers/media/i2c/maxim2c/maxim2c_mipi_txphy.c b/drivers/media/i2c/maxim2c/maxim2c_mipi_txphy.c new file mode 100644 index 000000000000..68fa50409c31 --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_mipi_txphy.c @@ -0,0 +1,559 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Dual GMSL Deserializer MIPI txphy driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + */ +#include + +#include "maxim2c_api.h" + +static int maxim2c_txphy_auto_init_deskew(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; + struct maxim2c_txphy_cfg *phy_cfg = NULL; + u16 reg_addr = 0; + u8 phy_idx = 0; + int ret = 0; + + // D-PHY Deskew Initial Calibration Control + for (phy_idx = 0; phy_idx < MAXIM2C_TXPHY_ID_MAX; phy_idx++) { + phy_cfg = &mipi_txphy->phy_cfg[phy_idx]; + if (phy_cfg->phy_enable && (phy_cfg->auto_deskew & BIT(7))) { + reg_addr = 0x0403 + 0x40 * phy_idx; + ret |= maxim2c_i2c_write_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + phy_cfg->auto_deskew); + } + } + + return ret; +} + +static int maxim2c_mipi_txphy_lane_mapping(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; + struct maxim2c_txphy_cfg *phy_cfg = NULL; + u8 reg_value = 0, reg_mask = 0; + int ret = 0; + + // MIPI TXPHY A/B: data lane mapping + reg_mask = 0; + reg_value = 0; + phy_cfg = &mipi_txphy->phy_cfg[MAXIM2C_TXPHY_ID_A]; + if (phy_cfg->phy_enable) { + reg_mask |= 0x0F; + reg_value |= (phy_cfg->data_lane_map << 0); + } + phy_cfg = &mipi_txphy->phy_cfg[MAXIM2C_TXPHY_ID_B]; + if (phy_cfg->phy_enable) { + reg_mask |= 0xF0; + reg_value |= (phy_cfg->data_lane_map << 4); + } + if (reg_mask != 0) { + ret |= maxim2c_i2c_update_byte(client, + 0x0333, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + } + + // MIPI TXPHY C/D: data lane mapping + reg_mask = 0; + reg_value = 0; + phy_cfg = &mipi_txphy->phy_cfg[MAXIM2C_TXPHY_ID_C]; + if (phy_cfg->phy_enable) { + reg_mask |= 0x0F; + reg_value |= (phy_cfg->data_lane_map << 0); + } + phy_cfg = &mipi_txphy->phy_cfg[MAXIM2C_TXPHY_ID_D]; + if (phy_cfg->phy_enable) { + reg_mask |= 0xF0; + reg_value |= (phy_cfg->data_lane_map << 4); + } + if (reg_mask != 0) { + ret |= maxim2c_i2c_update_byte(client, + 0x0334, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + } + + return ret; +} + +static int maxim2c_mipi_txphy_type_vcx_lane_num(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; + struct maxim2c_txphy_cfg *phy_cfg = NULL; + u8 phy_idx = 0; + u8 reg_mask = 0, reg_value = 0; + u16 reg_addr = 0; + int ret = 0; + + for (phy_idx = 0; phy_idx < MAXIM2C_TXPHY_ID_MAX; phy_idx++) { + phy_cfg = &mipi_txphy->phy_cfg[phy_idx]; + if (phy_cfg->phy_enable == 0) + continue; + + reg_mask = BIT(7) | BIT(6) | BIT(5) | BIT(3); + reg_value = 0; + + if (phy_cfg->phy_type == MAXIM2C_TXPHY_TYPE_CPHY) + reg_value |= BIT(5); + + if (phy_cfg->vc_ext_en) + reg_value |= BIT(3); + + reg_value |= ((phy_cfg->data_lane_num - 1) << 6); + + reg_addr = 0x040A + 0x40 * phy_idx; + ret |= maxim2c_i2c_update_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + } + + return ret; +} + +static int maxim2c_mipi_txphy_tunnel_init(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; + struct maxim2c_txphy_cfg *phy_cfg = NULL; + u8 phy_idx = 0; + u8 reg_mask = 0, reg_value = 0; + u16 reg_addr = 0; + int ret = 0; + + for (phy_idx = 0; phy_idx < MAXIM2C_TXPHY_ID_MAX; phy_idx++) { + phy_cfg = &mipi_txphy->phy_cfg[phy_idx]; + if (phy_cfg->phy_enable == 0) + continue; + + if (phy_cfg->tunnel_enable) { + // tunnel mode: enable + reg_mask = BIT(0); + reg_value = BIT(0); + + // tunnel pipe destination + reg_mask |= BIT(1); + reg_value |= ((phy_cfg->tunnel_dest & 0x1) << 1); + } else { + // tunnel mode: disable + reg_mask = BIT(0); + reg_value = 0; + } + + reg_addr = 0x0434 + 0x40 * phy_idx; + ret |= maxim2c_i2c_update_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + } + + return ret; +} + +int maxim2c_mipi_txphy_enable(maxim2c_t *maxim2c, bool enable) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; + u8 phy_idx = 0; + u8 reg_mask = 0, reg_value = 0; + int ret = 0; + + dev_dbg(dev, "%s: enable = %d\n", __func__, enable); + + reg_mask = 0xF0; + reg_value = 0; + + if (enable) { + for (phy_idx = 0; phy_idx < MAXIM2C_TXPHY_ID_MAX; phy_idx++) { + if (mipi_txphy->phy_cfg[phy_idx].phy_enable) + reg_value |= BIT(4 + phy_idx); + } + } + + ret |= maxim2c_i2c_update_byte(client, + 0x0332, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + + return ret; +} + +int maxim2c_dphy_dpll_predef_set(maxim2c_t *maxim2c, s64 link_freq_hz) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; + struct maxim2c_txphy_cfg *phy_cfg = NULL; + u32 link_freq_mhz = 0; + u16 reg_addr = 0; + u8 phy_idx = 0; + u8 dpll_mask = 0, dpll_val = 0, dpll_lock = 0; + int ret = 0; + + dpll_mask = 0; + + link_freq_mhz = (u32)div_s64(link_freq_hz, 1000000L); + dpll_val = DIV_ROUND_UP(link_freq_mhz * 2, 100) & 0x1F; + if (dpll_val == 0) + dpll_val = 15; /* default 1500MBps */ + // Disable software override for frequency fine tuning + dpll_val |= BIT(5); + + for (phy_idx = 0; phy_idx < MAXIM2C_TXPHY_ID_MAX; phy_idx++) { + phy_cfg = &mipi_txphy->phy_cfg[phy_idx]; + if ((phy_cfg->phy_enable == 0) || (phy_cfg->clock_master == 0)) + continue; + + if (phy_cfg->clock_mode != MAXIM2C_TXPHY_DPLL_PREDEF) + continue; + + dpll_mask |= BIT(phy_idx + 4); + + // Hold DPLL in reset (config_soft_rst_n = 0) before changing the rate + reg_addr = 0x1C00 + 0x100 * phy_idx; + ret |= maxim2c_i2c_write_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + 0xf4); + + // Set dpll data rate + reg_addr = 0x031D + 0x03 * phy_idx; + ret |= maxim2c_i2c_update_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + 0x3F, dpll_val); + + // Release reset to DPLL (config_soft_rst_n = 1) + reg_addr = 0x1C00 + 0x100 * phy_idx; + ret |= maxim2c_i2c_write_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + 0xf5); + } + + if (ret) { + dev_err(dev, "DPLL predef set error!\n"); + return ret; + } + +#if 0 + ret = read_poll_timeout(maxim2c_i2c_read_byte, ret, + !(ret < 0) && (dpll_lock & dpll_mask), + 1000, 10000, false, + client, + 0x0308, MAXIM2C_I2C_REG_ADDR_16BITS, + &dpll_lock); + if (ret < 0) { + dev_err(dev, "DPLL is unlocked: 0x%02x\n", dpll_lock); + return ret; + } else { + dev_info(dev, "DPLL is locked: 0x%02x\n", dpll_lock); + return 0; + } +#else + // The locking status of DPLL cannot be obtained before csi output + usleep_range(1000, 1100); + ret = maxim2c_i2c_read_byte(client, + 0x0308, MAXIM2C_I2C_REG_ADDR_16BITS, + &dpll_lock); + dev_info(dev, "DPLL lock state: 0x%02x\n", dpll_lock); + + return ret; +#endif +} +EXPORT_SYMBOL(maxim2c_dphy_dpll_predef_set); + +int maxim2c_mipi_csi_output(maxim2c_t *maxim2c, bool enable) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; + u8 reg_mask = 0, reg_value = 0; + int ret = 0; + + dev_dbg(dev, "%s: enable = %d\n", __func__, enable); + + if (mipi_txphy->force_clock_out_en != 0) { + reg_mask = BIT(7); + reg_value = enable ? BIT(7) : 0; + + // Force all MIPI clocks running Config + ret |= maxim2c_i2c_update_byte(client, + 0x0330, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + } + + /* Bit1 of the register 0x0313: CSI_OUT_EN + * 1 = CSI output enabled + * 0 = CSI output disabled + */ + reg_mask = BIT(1); + reg_value = enable ? BIT(1) : 0; + + // MIPI CSI output Setting + ret |= maxim2c_i2c_update_byte(client, + 0x0313, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + + return ret; +} +EXPORT_SYMBOL(maxim2c_mipi_csi_output); + +static int maxim2c_mipi_txphy_config_parse_dt(struct device *dev, + maxim2c_mipi_txphy_t *mipi_txphy, + struct device_node *parent_node) +{ + struct device_node *node = NULL; + struct maxim2c_txphy_cfg *phy_cfg = NULL; + const char *txphy_cfg_name = "mipi-txphy-config"; + u32 value = 0; + u32 sub_idx = 0, phy_id = 0; + int ret; + + node = NULL; + sub_idx = 0; + while ((node = of_get_next_child(parent_node, node))) { + if (!strncasecmp(node->name, + txphy_cfg_name, + strlen(txphy_cfg_name))) { + if (sub_idx >= MAXIM2C_TXPHY_ID_MAX) { + dev_err(dev, "%pOF: Too many matching %s node\n", + parent_node, txphy_cfg_name); + + of_node_put(node); + break; + } + + if (!of_device_is_available(node)) { + dev_info(dev, "%pOF is disabled\n", node); + + sub_idx++; + + continue; + } + + /* MIPI TXPHY: phy id */ + ret = of_property_read_u32(node, "phy-id", &phy_id); + if (ret) { + // if mipi txphy phy_id is error, parse next node + dev_err(dev, "Can not get phy-id property!"); + + sub_idx++; + + continue; + } + if (phy_id >= MAXIM2C_TXPHY_ID_MAX) { + // if mipi txphy phy_id is error, parse next node + dev_err(dev, "Error phy-id = %d!", phy_id); + + sub_idx++; + + continue; + } + + phy_cfg = &mipi_txphy->phy_cfg[phy_id]; + + /* MIPI TXPHY: phy enable */ + phy_cfg->phy_enable = 1; + + dev_info(dev, "mipi txphy id = %d: phy_enable = %d\n", + phy_id, phy_cfg->phy_enable); + + /* MIPI TXPHY: other config */ + ret = of_property_read_u32(node, "phy-type", &value); + if (ret == 0) { + dev_info(dev, "phy-type property: %d", value); + phy_cfg->phy_type = value; + } + + ret = of_property_read_u32(node, "auto-deskew", &value); + if (ret == 0) { + dev_info(dev, "auto-deskew property: 0x%x", value); + phy_cfg->auto_deskew = value; + } + + ret = of_property_read_u32(node, "data-lane-num", &value); + if (ret == 0) { + dev_info(dev, "data-lane-num property: %d", value); + phy_cfg->data_lane_num = value; + } + + ret = of_property_read_u32(node, "data-lane-map", &value); + if (ret == 0) { + dev_info(dev, "data-lane-map property: 0x%x", value); + phy_cfg->data_lane_map = value; + } + + ret = of_property_read_u32(node, "vc-ext-en", &value); + if (ret == 0) { + dev_info(dev, "vc-ext-en property: %d", value); + phy_cfg->vc_ext_en = value; + } + + ret = of_property_read_u32(node, "tunnel-enable", &value); + if (ret == 0) { + dev_info(dev, "tunnel-enable property: %d", value); + phy_cfg->tunnel_enable = value; + } + + ret = of_property_read_u32(node, "tunnel-dest", &value); + if (ret == 0) { + dev_info(dev, "tunnel-dest property: %d", value); + phy_cfg->tunnel_dest = value; + } + + ret = of_property_read_u32(node, "clock-mode", &value); + if (ret == 0) { + dev_info(dev, "clock-mode property: %d", value); + phy_cfg->clock_mode = value; + } + + sub_idx++; + } + } + + return 0; +} + +int maxim2c_mipi_txphy_parse_dt(maxim2c_t *maxim2c, struct device_node *of_node) +{ + struct device *dev = &maxim2c->client->dev; + struct device_node *node = NULL; + maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; + u32 value = 0; + int ret = 0; + + dev_info(dev, "=== maxim2c mipi txphy parse dt ===\n"); + + node = of_get_child_by_name(of_node, "mipi-txphys"); + if (IS_ERR_OR_NULL(node)) { + dev_err(dev, "%pOF has no child node: mipi-txphys\n", + of_node); + return -ENODEV; + } + + if (!of_device_is_available(node)) { + dev_info(dev, "%pOF is disabled\n", node); + of_node_put(node); + return -ENODEV; + } + + /* mipi txphy mode */ + ret = of_property_read_u32(node, "phy-mode", &value); + if (ret == 0) { + dev_info(dev, "phy-mode property: %d\n", value); + mipi_txphy->phy_mode = value; + } + dev_info(dev, "mipi txphy mode: %d\n", mipi_txphy->phy_mode); + + /* MIPI clocks running mode */ + ret = of_property_read_u32(node, "phy-force-clock-out", &value); + if (ret == 0) { + dev_info(dev, "phy-force-clock-out property: %d\n", value); + mipi_txphy->force_clock_out_en = value; + } + dev_info(dev, "mipi txphy force clock out enable: %d\n", + mipi_txphy->force_clock_out_en); + + ret = maxim2c_mipi_txphy_config_parse_dt(dev, mipi_txphy, node); + + of_node_put(node); + + return ret; +} +EXPORT_SYMBOL(maxim2c_mipi_txphy_parse_dt); + +int maxim2c_mipi_txphy_hw_init(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; + struct maxim2c_txphy_cfg *phy_cfg = NULL; + u8 mode = 0; + int ret = 0, i = 0; + + switch (mipi_txphy->phy_mode) { + case MAXIM2C_TXPHY_MODE_4X2LANES: + mode = BIT(0); + + // clock master + for (i = 0; i < MAXIM2C_TXPHY_ID_MAX; i++) { + if (mipi_txphy->phy_cfg[i].phy_enable) + mipi_txphy->phy_cfg[i].clock_master = 1; + } + + break; + case MAXIM2C_TXPHY_MODE_2X4LANES: + default: + mode = BIT(2); + + // clock master + phy_cfg = &mipi_txphy->phy_cfg[MAXIM2C_TXPHY_ID_B]; + if (phy_cfg->phy_enable) + phy_cfg->clock_master = 1; + + phy_cfg = &mipi_txphy->phy_cfg[MAXIM2C_TXPHY_ID_C]; + if (phy_cfg->phy_enable) + phy_cfg->clock_master = 1; + + break; + } + + // MIPI TXPHY Mode setting + ret |= maxim2c_i2c_write_byte(client, + 0x0330, MAXIM2C_I2C_REG_ADDR_16BITS, + mode); + + // Waits for a frame before generating MIPI Packet requests to the MIPI TX + ret |= maxim2c_i2c_update_byte(client, + 0x0325, MAXIM2C_I2C_REG_ADDR_16BITS, + BIT(7), BIT(7)); + + // mipi txphy data lane mapping + ret |= maxim2c_mipi_txphy_lane_mapping(maxim2c); + + // mipi txphy type, lane number, virtual channel extension + ret |= maxim2c_mipi_txphy_type_vcx_lane_num(maxim2c); + + // mipi txphy tunnel init + ret |= maxim2c_mipi_txphy_tunnel_init(maxim2c); + + // mipi txphy auto init deskew + ret |= maxim2c_txphy_auto_init_deskew(maxim2c); + + if (ret) { + dev_err(dev, "%s: txphy hw init error\n", __func__); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(maxim2c_mipi_txphy_hw_init); + +void maxim2c_mipi_txphy_data_init(maxim2c_t *maxim2c) +{ + maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; + struct maxim2c_txphy_cfg *phy_cfg = NULL; + int i = 0; + + mipi_txphy->phy_mode = MAXIM2C_TXPHY_MODE_2X4LANES; + mipi_txphy->force_clock_out_en = 1; + + for (i = 0; i < MAXIM2C_TXPHY_ID_MAX; i++) { + phy_cfg = &mipi_txphy->phy_cfg[i]; + + phy_cfg->phy_enable = 0; + phy_cfg->phy_type = MAXIM2C_TXPHY_TYPE_DPHY; + phy_cfg->auto_deskew = 0; + phy_cfg->data_lane_num = 4; + phy_cfg->data_lane_map = 0xe4; + phy_cfg->vc_ext_en = 0; + phy_cfg->tunnel_enable = 0; + phy_cfg->clock_master = 0; + phy_cfg->clock_mode = MAXIM2C_TXPHY_DPLL_PREDEF; + } +} +EXPORT_SYMBOL(maxim2c_mipi_txphy_data_init); diff --git a/drivers/media/i2c/maxim2c/maxim2c_mipi_txphy.h b/drivers/media/i2c/maxim2c/maxim2c_mipi_txphy.h new file mode 100644 index 000000000000..e3ef405bebd9 --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_mipi_txphy.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Rockchip Electronics Co., Ltd. + * + */ + +#ifndef __MAXIM2C_MIPI_TXPHY_H__ +#define __MAXIM2C_MIPI_TXPHY_H__ + +/* MIPI TXPHY ID: 0 ~ 3 */ +enum { + MAXIM2C_TXPHY_ID_A = 0, + MAXIM2C_TXPHY_ID_B, + MAXIM2C_TXPHY_ID_C, + MAXIM2C_TXPHY_ID_D, + MAXIM2C_TXPHY_ID_MAX, +}; + +/* MIPI TXPHY Bit Mask: bit0 ~ bit3 */ +#define MAXIM2C_TXPHY_MASK_A BIT(MAXIM2C_TXPHY_ID_A) +#define MAXIM2C_TXPHY_MASK_B BIT(MAXIM2C_TXPHY_ID_B) +#define MAXIM2C_TXPHY_MASK_C BIT(MAXIM2C_TXPHY_ID_C) +#define MAXIM2C_TXPHY_MASK_D BIT(MAXIM2C_TXPHY_ID_D) + +#define MAXIM2C_TXPHY_MASK_ALL GENMASK(MAXIM2C_TXPHY_ID_D, MAXIM2C_TXPHY_ID_A) + +/* MIPI TXPHY Type */ +enum { + MAXIM2C_TXPHY_TYPE_DPHY = 0, + MAXIM2C_TXPHY_TYPE_CPHY, +}; + +/* MIPI TXPHY Mode */ +enum { + MAXIM2C_TXPHY_MODE_2X4LANES = 0, /* PortA: 1x4Lanes, PortB: 1x4Lanes */ + MAXIM2C_TXPHY_MODE_4X2LANES, /* PortA: 2x2Lanes, PortB: 2x2Lanes */ +}; + +/* MIPI TXPHY DPLL */ +enum { + MAXIM2C_TXPHY_DPLL_PREDEF = 0, + MAXIM2C_TXPHY_DPLL_FINE_TUNING, +}; + +struct maxim2c_txphy_cfg { + u8 phy_enable; + u8 phy_type; + u8 auto_deskew; + u8 data_lane_num; + u8 data_lane_map; + u8 vc_ext_en; + u8 tunnel_enable; + u8 tunnel_dest; + u8 clock_master; + u8 clock_mode; +}; + +typedef struct maxim2c_mipi_txphy { + u8 phy_mode; /* mipi txphy mode */ + u8 force_clock_out_en; /* Force all MIPI clocks running */ + + struct maxim2c_txphy_cfg phy_cfg[MAXIM2C_TXPHY_ID_MAX]; +} maxim2c_mipi_txphy_t; + +#endif /* __MAXIM2C_MIPI_TXPHY_H__ */ diff --git a/drivers/media/i2c/maxim2c/maxim2c_pattern.c b/drivers/media/i2c/maxim2c/maxim2c_pattern.c new file mode 100644 index 000000000000..06f70f3ee27c --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_pattern.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Dual GMSL Deserializer Test Pattern Driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + */ +#include "maxim2c_api.h" + +#define PATTERN_WIDTH 1920 +#define PATTERN_HEIGHT 1080 + +/* pattern mode: checkerboard or gradient */ +enum { + PATTERN_CHECKERBOARD = 0, + PATTERN_GRADIENT, +}; + +/* pattern pclk: 25M or 75M 0r 150M or 600M */ +enum { + PATTERN_PCLK_25M = 0, + PATTERN_PCLK_75M, + PATTERN_PCLK_150M, + PATTERN_PCLK_600M, +}; + +static const struct maxim2c_mode maxim2c_pattern_mode = { + .width = PATTERN_WIDTH, + .height = PATTERN_HEIGHT, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .link_freq_idx = 24, + .bus_fmt = MEDIA_BUS_FMT_RGB888_1X24, + .bpp = 24, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, +}; + +int maxim2c_pattern_enable(maxim2c_t *maxim2c, bool enable) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + struct maxim2c_pattern *pattern = &maxim2c->pattern; + u32 pattern_mode; + u8 reg_mask = 0, reg_val = 0; + int ret = 0; + + dev_info(dev, "video pattern: enable = %d\n", enable); + + pattern_mode = pattern->pattern_mode; + + reg_mask = BIT(5) | BIT(4); + if (pattern_mode == PATTERN_CHECKERBOARD) { + /* Generate checkerboard pattern. */ + reg_val = enable ? BIT(4) : 0; + } else { + /* Generate gradient pattern. */ + reg_val = enable ? BIT(5) : 0; + } + ret = maxim2c_i2c_update_byte(client, + 0x0241, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_val); + + return ret; +} +EXPORT_SYMBOL(maxim2c_pattern_enable); + +static int maxim2c_pattern_previnit(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + int ret = 0; + + // Disable data transmission through video pipe. + ret = maxim2c_i2c_update_byte(client, + 0x0002, MAXIM2C_I2C_REG_ADDR_16BITS, + 0xF0, 0x00); + if (ret) + return ret; + + // video pipe disable. + ret = maxim2c_i2c_write_byte(client, + 0x0160, MAXIM2C_I2C_REG_ADDR_16BITS, + 0x00); + if (ret) + return ret; + + // MIPI CSI output disable. + ret = maxim2c_i2c_write_byte(client, + 0x0313, MAXIM2C_I2C_REG_ADDR_16BITS, + 0x00); + if (ret) + return ret; + + // MIPI TXPHY standby + ret = maxim2c_i2c_update_byte(client, + 0x0332, MAXIM2C_I2C_REG_ADDR_16BITS, + 0xF0, 0x00); + if (ret) + return ret; + + return 0; +} + +static int maxim2c_pattern_config(maxim2c_t *maxim2c) +{ + const u32 h_active = PATTERN_WIDTH; + const u32 h_fp = 88; + const u32 h_sw = 44; + const u32 h_bp = 148; + const u32 h_tot = h_active + h_fp + h_sw + h_bp; + + const u32 v_active = PATTERN_HEIGHT; + const u32 v_fp = 4; + const u32 v_sw = 5; + const u32 v_bp = 36; + const u32 v_tot = v_active + v_fp + v_sw + v_bp; + + struct i2c_client *client = maxim2c->client; + struct maxim2c_pattern *pattern = &maxim2c->pattern; + u32 pattern_mode; + u32 pattern_pclk; + u16 reg_addr = 0; + u8 reg_mask = 0, reg_val = 0; + int ret = 0, i = 0; + + pattern_mode = pattern->pattern_mode; + pattern_pclk = pattern->pattern_pclk; + + // PATGEN_MODE = 0, Pattern generator disabled + // use video from the serializer input + ret |= maxim2c_i2c_update_byte(client, + 0x0241, MAXIM2C_I2C_REG_ADDR_16BITS, + BIT(5) | BIT(4), 0x00); + + /* Pattern PCLK: + * 0b00 - 25MHz + * 0b01 - 75MHz + * 0b1x - (PATGEN_CLK_SRC: 0 - 150MHz, 1 - 600MHz). + */ + pattern_pclk = (pattern_pclk & 0x03); + ret |= maxim2c_i2c_write_byte(client, + 0x0038, MAXIM2C_I2C_REG_ADDR_16BITS, + pattern_pclk); + if (pattern_pclk >= PATTERN_PCLK_150M) { + reg_mask = BIT(7); + if (pattern_pclk == PATTERN_PCLK_600M) + reg_val = BIT(7); + else + reg_val = 0; + + for (i = 0; i < 2; i++) { + reg_addr = 0x01FC + i * 0x20; + ret |= maxim2c_i2c_update_byte(client, + reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_val); + } + } + + /* Configure Video Timing Generator for 1920x1080 @ 30 fps. */ + // VS_DLY = 0 + ret |= maxim2c_i2c_write_reg(client, + 0x0242, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_24BITS, 0x000000); + // VS_HIGH = Vsw * Htot + ret |= maxim2c_i2c_write_reg(client, + 0x0245, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_24BITS, v_sw * h_tot); + // VS_LOW = (Vactive + Vfp + Vbp) * Htot + ret |= maxim2c_i2c_write_reg(client, + 0x0248, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_24BITS, (v_active + v_fp + v_bp) * h_tot); + // V2H = VS_DLY + ret |= maxim2c_i2c_write_reg(client, + 0x024B, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_24BITS, 0x000000); + // HS_HIGH = Hsw + ret |= maxim2c_i2c_write_reg(client, + 0x024E, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_16BITS, h_sw); + // HS_LOW = Hactive + Hfp + Hbp + ret |= maxim2c_i2c_write_reg(client, + 0x0250, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_16BITS, h_active + h_fp + h_bp); + // HS_CNT = Vtot + ret |= maxim2c_i2c_write_reg(client, + 0x0252, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_16BITS, v_tot); + // V2D = VS_DLY + Htot * (Vsw + Vbp) + (Hsw + Hbp) + ret |= maxim2c_i2c_write_reg(client, + 0x0254, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_24BITS, h_tot * (v_sw + v_bp) + (h_sw + h_bp)); + // DE_HIGH = Hactive + ret |= maxim2c_i2c_write_reg(client, + 0x0257, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_16BITS, h_active); + // DE_LOW = Hfp + Hsw + Hbp + ret |= maxim2c_i2c_write_reg(client, + 0x0259, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_16BITS, h_fp + h_sw + h_bp); + // DE_CNT = Vactive + ret |= maxim2c_i2c_write_reg(client, + 0x025B, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_16BITS, v_active); + + /* Generate VS, HS and DE in free-running mode, Invert HS and VS. */ + ret |= maxim2c_i2c_write_byte(client, + 0x0240, MAXIM2C_I2C_REG_ADDR_16BITS, + 0xfb); + + /* Configure Video Pattern Generator. */ + if (pattern_mode == PATTERN_CHECKERBOARD) { + /* Set checkerboard pattern size. */ + ret |= maxim2c_i2c_write_reg(client, + 0x0264, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_24BITS, 0x3c3c3c); + + /* Set checkerboard pattern colors. */ + ret |= maxim2c_i2c_write_reg(client, + 0x025E, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_24BITS, 0xfecc00); + ret |= maxim2c_i2c_write_reg(client, + 0x0261, MAXIM2C_I2C_REG_ADDR_16BITS, + MAXIM2C_I2C_REG_VALUE_24BITS, 0x006aa7); + } else { + /* Set gradient increment. */ + ret |= maxim2c_i2c_write_byte(client, + 0x025D, MAXIM2C_I2C_REG_ADDR_16BITS, + 0x10); + } + + return ret; +} + +int maxim2c_pattern_support_mode_init(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + struct maxim2c_mode *supported_mode = NULL; + + dev_info(dev, "=== maxim2c pattern support mode init ===\n"); + + maxim2c->cfg_modes_num = 1; + maxim2c->cur_mode = &maxim2c->supported_mode; + supported_mode = &maxim2c->supported_mode; + + // init using def mode + memcpy(supported_mode, &maxim2c_pattern_mode, sizeof(struct maxim2c_mode)); + + return 0; +} +EXPORT_SYMBOL(maxim2c_pattern_support_mode_init); + +int maxim2c_pattern_data_init(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + struct device_node *node = NULL; + struct maxim2c_mode *supported_mode = NULL; + struct maxim2c_pattern *pattern = NULL; + maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; + int ret = 0; + + // maxim serdes local + node = of_get_child_by_name(dev->of_node, "serdes-local-device"); + if (IS_ERR_OR_NULL(node)) { + dev_err(dev, "%pOF has no child node: serdes-local-device\n", + dev->of_node); + return -ENODEV; + } + + if (!of_device_is_available(node)) { + dev_info(dev, "%pOF is disabled\n", node); + + of_node_put(node); + return -ENODEV; + } + + maxim2c_mipi_txphy_data_init(maxim2c); + + /* mipi txphy parse dt */ + ret = maxim2c_mipi_txphy_parse_dt(maxim2c, node); + if (ret) { + dev_err(dev, "%s: txphy parse dt error\n", __func__); + return ret; + } + + // pattern need enable force_clock_out_en + dev_info(dev, "Pattern mode force_clock_out_en default enable\n"); + mipi_txphy->force_clock_out_en = 1; + + // pattern generator and mode init + pattern = &maxim2c->pattern; + pattern->pattern_mode = PATTERN_CHECKERBOARD; + pattern->pattern_pclk = PATTERN_PCLK_75M; + + supported_mode = &maxim2c->supported_mode; + switch (pattern->pattern_pclk) { + case PATTERN_PCLK_25M: + supported_mode->max_fps.denominator = 100000; + break; + case PATTERN_PCLK_75M: + supported_mode->max_fps.denominator = 300000; + break; + case PATTERN_PCLK_150M: + supported_mode->max_fps.denominator = 600000; + if (supported_mode->link_freq_idx < 12) + dev_warn(dev, "link_freq_idx = %d is too low\n", + supported_mode->link_freq_idx); + break; + case PATTERN_PCLK_600M: + supported_mode->max_fps.denominator = 1500000; + if (supported_mode->link_freq_idx < 22) + dev_warn(dev, "link_freq_idx = %d is too low\n", + supported_mode->link_freq_idx); + break; + } + + dev_info(dev, "video pattern: mode = %d, pclk = %d\n", + pattern->pattern_mode, pattern->pattern_pclk); + + return 0; +} +EXPORT_SYMBOL(maxim2c_pattern_data_init); + +int maxim2c_pattern_hw_init(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + int ret = 0; + + ret = maxim2c_pattern_previnit(maxim2c); + if (ret) { + dev_err(dev, "%s: pattern previnit error\n", __func__); + return ret; + } + + ret = maxim2c_mipi_txphy_hw_init(maxim2c); + if (ret) { + dev_err(dev, "%s: txphy hw init error\n", __func__); + return ret; + } + + ret = maxim2c_pattern_config(maxim2c); + if (ret) { + dev_err(dev, "%s: pattern config error\n", __func__); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(maxim2c_pattern_hw_init); diff --git a/drivers/media/i2c/maxim2c/maxim2c_pattern.h b/drivers/media/i2c/maxim2c/maxim2c_pattern.h new file mode 100644 index 000000000000..2ea7b05e1089 --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_pattern.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Rockchip Electronics Co., Ltd. + * + */ + +#ifndef __MAXIM2C_PATTERN_H__ +#define __MAXIM2C_PATTERN_H__ + +struct maxim2c_pattern { + u32 pattern_mode; + u32 pattern_pclk; +}; + +#endif /* __MAXIM2C_PATTERN_H__ */ diff --git a/drivers/media/i2c/maxim2c/maxim2c_remote.c b/drivers/media/i2c/maxim2c/maxim2c_remote.c new file mode 100644 index 000000000000..461c064bcffe --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_remote.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Dual GMSL Deserializer Remode Device Manage + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + */ +#include +#include +#include +#include "maxim2c_api.h" + +static const char *maxim2c_remote_devs_name[MAXIM2C_LINK_ID_MAX] = { + "remote0", "remote1" +}; + +static const char *maxim2c_remote_link_compat[MAXIM2C_LINK_ID_MAX] = { + "maxim2c,link0", "maxim2c,link1" +}; + +static int maxim2c_remote_dev_info_parse(struct device *dev, + struct mfd_cell *remote_mfd_dev, u8 link_id) +{ + struct device_node *node = NULL; + const char *remote_device_name = "serdes-remote-device"; + const char *prop_str = NULL, *link_compat = NULL; + u32 sub_idx = 0, remote_id = 0; + int ret = 0; + + node = NULL; + sub_idx = 0; + while ((node = of_get_next_child(dev->of_node, node))) { + if (!strncasecmp(node->name, + remote_device_name, + strlen(remote_device_name))) { + if (sub_idx >= MAXIM2C_LINK_ID_MAX) { + dev_err(dev, "%pOF: Too many matching %s node\n", + dev->of_node, remote_device_name); + + of_node_put(node); + break; + } + + if (!of_device_is_available(node)) { + dev_info(dev, "%pOF is disabled\n", node); + + sub_idx++; + + continue; + } + + /* remote id */ + ret = of_property_read_u32(node, "remote-id", &remote_id); + if (ret) { + sub_idx++; + + continue; + } + if (remote_id >= MAXIM2C_LINK_ID_MAX) { + sub_idx++; + + continue; + } + + if (remote_id != link_id) { + sub_idx++; + + continue; + } + + dev_info(dev, "remote device id = %d\n", remote_id); + + ret = of_property_read_string(node, "compatible", &prop_str); + if (ret) { + dev_err(dev, "%pOF no compatible error\n", node); + + of_node_put(node); + return -EINVAL; + } + + link_compat = maxim2c_remote_link_compat[remote_id]; + if (!strncasecmp(prop_str, + link_compat, strlen(link_compat))) { + dev_info(dev, "compatible property: %s\n", prop_str); + + remote_mfd_dev->name = maxim2c_remote_devs_name[remote_id]; + remote_mfd_dev->of_compatible = prop_str; + + of_node_put(node); + return 0; + } + + dev_err(dev, "%pOF compatible and remote_id mismatch\n", node); + + of_node_put(node); + return -EINVAL; + } + } + + return -EINVAL; +} + +static int maxim2c_remote_mfd_devs_init(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + maxim2c_gmsl_link_t *gmsl_link = &maxim2c->gmsl_link; + struct mfd_cell *remote_mfd_dev = NULL; + int link_idx = 0, nr_mfd_cell = 0; + int ret = 0; + + remote_mfd_dev = maxim2c->remote_mfd_devs; + nr_mfd_cell = 0; + for (link_idx = 0; link_idx < MAXIM2C_LINK_ID_MAX; link_idx++) { + remote_mfd_dev->name = NULL; + remote_mfd_dev->of_compatible = NULL; + + if (gmsl_link->link_cfg[link_idx].link_enable == 0) { + dev_dbg(dev, "%s: link id = %d is disabled\n", + __func__, link_idx); + continue; + } + + ret = maxim2c_remote_dev_info_parse(dev, remote_mfd_dev, link_idx); + if (ret == 0) { + remote_mfd_dev++; + nr_mfd_cell++; + } + } + + dev_info(dev, "Total number of remote devices is %d", nr_mfd_cell); + + return nr_mfd_cell; +} + +int maxim2c_remote_mfd_add_devices(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + int nr_mfd_cell = 0, ret = 0; + + dev_info(dev, "=== maxim2c add remote devices ==="); + + nr_mfd_cell = maxim2c_remote_mfd_devs_init(maxim2c); + if (nr_mfd_cell == 0) { + dev_err(dev, "%s: remote mfd devices init error\n", + __func__); + return -EINVAL; + } + + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, + maxim2c->remote_mfd_devs, nr_mfd_cell, + NULL, 0, NULL); + if (ret) + dev_err(dev, "%s: add remote mfd devices error: %d\n", + __func__, ret); + + return ret; +} +EXPORT_SYMBOL(maxim2c_remote_mfd_add_devices); + +int maxim2c_remote_devices_init(maxim2c_t *maxim2c, u8 link_init_mask) +{ + struct device *dev = &maxim2c->client->dev; + struct maxim2c_remote *remote_device = NULL; + const struct maxim2c_remote_ops *remote_ops = NULL; + u8 link_mask = 0, link_enable = 0, link_locked = 0; + int ret = 0, i = 0; + + dev_dbg(dev, "%s: link init mask = 0x%02x\n", __func__, link_init_mask); + + for (i = 0; i < MAXIM2C_LINK_ID_MAX; i++) { + if ((link_init_mask & BIT(i)) == 0) { + dev_dbg(dev, "link id = %d init mask is disabled\n", i); + continue; + } + + link_enable = maxim2c->gmsl_link.link_cfg[i].link_enable; + if (link_enable == 0) { + dev_info(dev, "link id = %d is disabled\n", i); + continue; + } + + remote_device = maxim2c->remote_device[i]; + if (remote_device == NULL) { + dev_info(dev, "remote device id = %d isn't detected\n", i); + continue; + } + + if (remote_device->remote_enable == 0) { + dev_info(dev, "remote device id = %d isn't enabled\n", i); + continue; + } + + remote_ops = remote_device->remote_ops; + if (remote_ops == NULL) { + dev_info(dev, "remote device id = %d is no ops\n", i); + continue; + } + + link_mask = BIT(i); + link_locked = maxim2c_link_get_lock_state(maxim2c, link_mask); + if (link_locked != link_mask) { + dev_info(dev, "link id = %d is unlocked\n", i); + continue; + } + + maxim2c_link_select_remote_control(maxim2c, link_mask); + + if (remote_ops->remote_init) + ret |= remote_ops->remote_init(remote_device); + } + + return ret; +} +EXPORT_SYMBOL(maxim2c_remote_devices_init); + +int maxim2c_remote_devices_deinit(maxim2c_t *maxim2c, u8 link_init_mask) +{ + struct device *dev = &maxim2c->client->dev; + struct maxim2c_remote *remote_device = NULL; + const struct maxim2c_remote_ops *remote_ops = NULL; + u8 link_mask = 0, link_enable = 0, link_locked = 0; + int ret = 0, i = 0; + + dev_dbg(dev, "%s: link init mask = 0x%02x\n", __func__, link_init_mask); + + for (i = 0; i < MAXIM2C_LINK_ID_MAX; i++) { + if ((link_init_mask & BIT(i)) == 0) { + dev_dbg(dev, "link id = %d init mask is disabled\n", i); + continue; + } + + link_enable = maxim2c->gmsl_link.link_cfg[i].link_enable; + if (link_enable == 0) { + dev_info(dev, "link id = %d is disabled\n", i); + continue; + } + + remote_device = maxim2c->remote_device[i]; + if (remote_device == NULL) { + dev_info(dev, "remote device id = %d isn't detected\n", i); + continue; + } + + if (remote_device->remote_enable == 0) { + dev_info(dev, "remote device id = %d isn't enabled\n", i); + continue; + } + + remote_ops = remote_device->remote_ops; + if (remote_ops == NULL) { + dev_info(dev, "remote device id = %d is no ops\n", i); + continue; + } + + link_mask = BIT(i); + link_locked = maxim2c_link_get_lock_state(maxim2c, link_mask); + if (link_locked != link_mask) { + dev_info(dev, "link id = %d is unlocked\n", i); + continue; + } + + maxim2c_link_select_remote_control(maxim2c, link_mask); + + if (remote_ops->remote_deinit) + ret |= remote_ops->remote_deinit(remote_device); + } + + return ret; +} +EXPORT_SYMBOL(maxim2c_remote_devices_deinit); + +int maxim2c_remote_load_init_seq(maxim2c_remote_t *remote_device) +{ + struct device *dev = remote_device->dev; + struct device_node *node = NULL; + int ret = 0; + + node = of_get_child_by_name(dev->of_node, "remote-init-sequence"); + if (!IS_ERR_OR_NULL(node)) { + dev_info(dev, "load remote-init-sequence\n"); + + ret = maxim2c_i2c_load_init_seq(dev, node, + &remote_device->remote_init_seq); + + of_node_put(node); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(maxim2c_remote_load_init_seq); + +int maxim2c_remote_i2c_addr_select(maxim2c_remote_t *remote_device, u32 i2c_id) +{ + struct device *dev = remote_device->dev; + struct i2c_client *client = remote_device->client; + + if (i2c_id == MAXIM2C_I2C_SER_DEF) { + client->addr = remote_device->ser_i2c_addr_def; + dev_info(dev, "ser select default i2c addr = 0x%02x\n", client->addr); + } else if (i2c_id == MAXIM2C_I2C_SER_MAP) { + client->addr = remote_device->ser_i2c_addr_map; + dev_info(dev, "ser select mapping i2c addr = 0x%02x\n", client->addr); + } else { + dev_err(dev, "i2c select id = %d error\n", i2c_id); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(maxim2c_remote_i2c_addr_select); + +int maxim2c_remote_i2c_client_init(maxim2c_remote_t *remote_device, + struct i2c_client *des_client) +{ + struct device *dev = remote_device->dev; + struct i2c_client *ser_client = NULL; + u16 ser_client_addr = 0; + + if (remote_device->ser_i2c_addr_map) + ser_client_addr = remote_device->ser_i2c_addr_map; + else + ser_client_addr = remote_device->ser_i2c_addr_def; + ser_client = devm_i2c_new_dummy_device(&des_client->dev, + des_client->adapter, ser_client_addr); + if (IS_ERR(ser_client)) { + dev_err(dev, "failed to alloc i2c client.\n"); + return -PTR_ERR(ser_client); + } + ser_client->addr = remote_device->ser_i2c_addr_def; + + remote_device->client = ser_client; + i2c_set_clientdata(ser_client, remote_device); + + dev_info(dev, "remote i2c client init, i2c_addr = 0x%02x\n", + ser_client_addr); + + return 0; +} +EXPORT_SYMBOL(maxim2c_remote_i2c_client_init); + +static int maxim2c_remote_device_chain_check(maxim2c_remote_t *remote_device) +{ + struct device *dev = NULL; + struct device_node *endpoint = NULL; + struct device_node *link_node = NULL; + u8 remote_id, link_id; + u32 value; + int ret = 0; + + if (remote_device == NULL) { + dev_err(dev, "%s: input parameter is error\n", __func__); + return -EINVAL; + } + + dev = remote_device->dev; + remote_id = remote_device->remote_id; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "%s: no endpoint error\n", __func__); + return -EINVAL; + } + + link_node = of_graph_get_remote_port_parent(endpoint); + if (!link_node) { + dev_err(dev, "%pOF: endpoint has no remote port parent error\n", + endpoint); + return -EINVAL; + } + + ret = of_property_read_u32(link_node, "link-id", &value); + if (ret) { + dev_err(dev, "%pOF: no property link_id error\n", link_node); + + of_node_put(link_node); + return -EINVAL; + } + of_node_put(link_node); + link_id = value; + + if (remote_id != link_id) { + dev_err(dev, "remote_id (%d) != link_id (%d) of %pOF\n", + remote_id, link_id, link_node); + return -EINVAL; + } + + return 0; +} + +int maxim2c_remote_device_register(maxim2c_t *maxim2c, + maxim2c_remote_t *remote_device) +{ + struct device *dev = NULL; + u8 remote_id; + int ret = 0; + + if ((maxim2c == NULL) || (remote_device == NULL)) { + dev_err(dev, "%s: input parameter is error!\n", __func__); + + return -EINVAL; + } + + dev = remote_device->dev; + remote_id = remote_device->remote_id; + if (remote_id >= MAXIM2C_LINK_ID_MAX) { + dev_err(dev, "%s: remote_id = %d is error\n", + __func__, remote_id); + + return -EINVAL; + } + + if (maxim2c->remote_device[remote_id] != NULL) { + dev_err(dev, "%s: remote_id = %d is conflict\n", + __func__, remote_id); + + return -EINVAL; + } + + ret = maxim2c_remote_device_chain_check(remote_device); + if (ret) { + dev_err(dev, "%s: remote device id = %d chain error\n", + __func__, remote_id); + return -EINVAL; + } + + remote_device->remote_enable = 1; + maxim2c->remote_device[remote_id] = remote_device; + + return 0; +} +EXPORT_SYMBOL(maxim2c_remote_device_register); diff --git a/drivers/media/i2c/maxim2c/maxim2c_remote.h b/drivers/media/i2c/maxim2c/maxim2c_remote.h new file mode 100644 index 000000000000..9044d4665fa8 --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_remote.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Rockchip Electronics Co., Ltd. + * + */ + +#ifndef __MAXIM2C_REMOTE_H__ +#define __MAXIM2C_REMOTE_H__ + +#include "maxim2c_i2c.h" + +struct maxim2c_remote; + +struct maxim2c_remote_ops { + int (*remote_init)(struct maxim2c_remote *remote); + int (*remote_deinit)(struct maxim2c_remote *remote); +}; + +typedef struct maxim2c_remote { + struct i2c_client *client; + struct device *dev; + void *local; + const struct maxim2c_remote_ops *remote_ops; + struct maxim2c_i2c_init_seq remote_init_seq; + + u8 remote_id; + u8 remote_enable; + + u8 ser_i2c_addr_def; + u8 ser_i2c_addr_map; + + u8 cam_i2c_addr_def; + u8 cam_i2c_addr_map; +} maxim2c_remote_t; + +#endif /* __MAXIM2C_REMOTE_H__ */ diff --git a/drivers/media/i2c/maxim2c/maxim2c_v4l2.c b/drivers/media/i2c/maxim2c/maxim2c_v4l2.c new file mode 100644 index 000000000000..fd79ab824eed --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_v4l2.c @@ -0,0 +1,992 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Dual GMSL Deserializer V4L2 driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "maxim2c_api.h" + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define MIPI_PHY_FREQ_MHZ(x) ((x) * 1000000UL) + +/* link freq = index * MIPI_PHY_FREQ_MHZ(50) */ +static const s64 link_freq_items[] = { + MIPI_PHY_FREQ_MHZ(0), + MIPI_PHY_FREQ_MHZ(50), + MIPI_PHY_FREQ_MHZ(100), + MIPI_PHY_FREQ_MHZ(150), + MIPI_PHY_FREQ_MHZ(200), + MIPI_PHY_FREQ_MHZ(250), + MIPI_PHY_FREQ_MHZ(300), + MIPI_PHY_FREQ_MHZ(350), + MIPI_PHY_FREQ_MHZ(400), + MIPI_PHY_FREQ_MHZ(450), + MIPI_PHY_FREQ_MHZ(500), + MIPI_PHY_FREQ_MHZ(550), + MIPI_PHY_FREQ_MHZ(600), + MIPI_PHY_FREQ_MHZ(650), + MIPI_PHY_FREQ_MHZ(700), + MIPI_PHY_FREQ_MHZ(750), + MIPI_PHY_FREQ_MHZ(800), + MIPI_PHY_FREQ_MHZ(850), + MIPI_PHY_FREQ_MHZ(900), + MIPI_PHY_FREQ_MHZ(950), + MIPI_PHY_FREQ_MHZ(1000), + MIPI_PHY_FREQ_MHZ(1050), + MIPI_PHY_FREQ_MHZ(1100), + MIPI_PHY_FREQ_MHZ(1150), + MIPI_PHY_FREQ_MHZ(1200), + MIPI_PHY_FREQ_MHZ(1250), +}; + +static const struct maxim2c_mode maxim2c_def_mode = { + .width = 1920, + .height = 1080, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .link_freq_idx = 15, + .bus_fmt = MEDIA_BUS_FMT_UYVY8_2X8, + .bpp = 16, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_2, + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_3, +}; + +static struct rkmodule_csi_dphy_param rk3588_dcphy_param = { + .vendor = PHY_VENDOR_SAMSUNG, + .lp_vol_ref = 3, + .lp_hys_sw = {3, 0, 0, 0}, + .lp_escclk_pol_sel = {1, 0, 0, 0}, + .skew_data_cal_clk = {0, 0, 0, 0}, + .clk_hs_term_sel = 2, + .data_hs_term_sel = {2, 2, 2, 2}, + .reserved = {0}, +}; + +static int maxim2c_support_mode_init(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + struct device_node *node = NULL; + struct maxim2c_mode *mode = NULL; + u32 value = 0, vc_array[PAD_MAX]; + int ret = 0, i = 0, array_size = 0; + + dev_info(dev, "=== maxim2c support mode init ===\n"); + +#if MAXIM2C_TEST_PATTERN + ret = maxim2c_pattern_support_mode_init(maxim2c); + return ret; +#endif + + maxim2c->cfg_modes_num = 1; + maxim2c->cur_mode = &maxim2c->supported_mode; + mode = &maxim2c->supported_mode; + + // init using def mode + memcpy(mode, &maxim2c_def_mode, sizeof(struct maxim2c_mode)); + + node = of_get_child_by_name(dev->of_node, "support-mode-config"); + if (IS_ERR_OR_NULL(node)) { + dev_info(dev, "no mode config node, using default config.\n"); + + return 0; + } + + if (!of_device_is_available(node)) { + dev_info(dev, "%pOF is disabled, using default config.\n", node); + + of_node_put(node); + + return 0; + } + + ret = of_property_read_u32(node, "sensor-width", &value); + if (ret == 0) { + dev_info(dev, "sensor-width property: %d\n", value); + mode->width = value; + } + dev_info(dev, "support mode: width = %d\n", mode->width); + + ret = of_property_read_u32(node, "sensor-height", &value); + if (ret == 0) { + dev_info(dev, "sensor-height property: %d\n", value); + mode->height = value; + } + dev_info(dev, "support mode: height = %d\n", mode->height); + + ret = of_property_read_u32(node, "bus-format", &value); + if (ret == 0) { + dev_info(dev, "bus-format property: %d\n", value); + mode->bus_fmt = value; + } + dev_info(dev, "support mode: bus_fmt = 0x%x\n", mode->bus_fmt); + + ret = of_property_read_u32(node, "bpp", &value); + if (ret == 0) { + dev_info(dev, "bpp property: %d\n", value); + mode->bpp = value; + } + dev_info(dev, "support mode: bpp = %d\n", mode->bpp); + + ret = of_property_read_u32(node, "max-fps-numerator", &value); + if (ret == 0) { + dev_info(dev, "max-fps-numerator property: %d\n", value); + mode->max_fps.numerator = value; + } + dev_info(dev, "support mode: numerator = %d\n", mode->max_fps.numerator); + + ret = of_property_read_u32(node, "max-fps-denominator", &value); + if (ret == 0) { + dev_info(dev, "max-fps-denominator property: %d\n", value); + mode->max_fps.denominator = value; + } + dev_info(dev, "support mode: denominator = %d\n", mode->max_fps.denominator); + + ret = of_property_read_u32(node, "link-freq-idx", &value); + if (ret == 0) { + dev_info(dev, "link-freq-idx property: %d\n", value); + mode->link_freq_idx = value; + } + dev_info(dev, "support mode: link_freq_idx = %d\n", mode->link_freq_idx); + + ret = of_property_read_u32(node, "hts-def", &value); + if (ret == 0) { + dev_info(dev, "hts-def property: %d\n", value); + mode->hts_def = value; + } + dev_info(dev, "support mode: hts_def = %d\n", mode->hts_def); + + ret = of_property_read_u32(node, "vts-def", &value); + if (ret == 0) { + dev_info(dev, "vts-def property: %d\n", value); + mode->vts_def = value; + } + dev_info(dev, "support mode: vts_def = %d\n", mode->vts_def); + + ret = of_property_read_u32(node, "exp-def", &value); + if (ret == 0) { + dev_info(dev, "exp-def property: %d\n", value); + mode->exp_def = value; + } + dev_info(dev, "support mode: exp_def = %d\n", mode->exp_def); + + array_size = of_property_read_variable_u32_array(node, + "vc-array", vc_array, 1, PAD_MAX); + if (array_size > 0) { + if (array_size > PAD_MAX) + array_size = PAD_MAX; + + for (i = 0; i < array_size; i++) { + dev_info(dev, "vc-array[%d] property: 0x%x\n", i, vc_array[i]); + mode->vc[i] = vc_array[i]; + } + } + for (i = 0; i < PAD_MAX; i++) + dev_info(dev, "support mode: vc[%d] = 0x%x\n", i, mode->vc[i]); + + of_node_put(node); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int maxim2c_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct maxim2c_mode *def_mode = &maxim2c->supported_mode; + + mutex_lock(&maxim2c->mutex); + + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = def_mode->bus_fmt; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&maxim2c->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int maxim2c_s_power(struct v4l2_subdev *sd, int on) +{ + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + struct i2c_client *client = maxim2c->client; + int ret = 0; + + mutex_lock(&maxim2c->mutex); + + /* If the power state is not modified - no work to do. */ + if (maxim2c->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + maxim2c->power_on = true; + } else { + pm_runtime_put(&client->dev); + maxim2c->power_on = false; + } + +unlock_and_return: + mutex_unlock(&maxim2c->mutex); + + return ret; +} + +static void maxim2c_get_module_inf(struct maxim2c *maxim2c, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, MAXIM2C_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, maxim2c->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, maxim2c->len_name, sizeof(inf->base.lens)); +} + +static void maxim2c_get_vicap_rst_inf(struct maxim2c *maxim2c, + struct rkmodule_vicap_reset_info *rst_info) +{ + struct i2c_client *client = maxim2c->client; + + rst_info->is_reset = maxim2c->hot_plug; + maxim2c->hot_plug = false; + rst_info->src = RKCIF_RESET_SRC_ERR_HOTPLUG; + + dev_info(&client->dev, "%s: rst_info->is_reset:%d.\n", + __func__, rst_info->is_reset); +} + +static void maxim2c_set_vicap_rst_inf(struct maxim2c *maxim2c, + struct rkmodule_vicap_reset_info rst_info) +{ + maxim2c->is_reset = rst_info.is_reset; +} + +static long maxim2c_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + struct rkmodule_csi_dphy_param *dphy_param; + long ret = 0; + + dev_dbg(&maxim2c->client->dev, "ioctl cmd = 0x%08x\n", cmd); + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + maxim2c_get_module_inf(maxim2c, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_VICAP_RST_INFO: + maxim2c_get_vicap_rst_inf(maxim2c, + (struct rkmodule_vicap_reset_info *)arg); + break; + case RKMODULE_SET_VICAP_RST_INFO: + maxim2c_set_vicap_rst_inf(maxim2c, + *(struct rkmodule_vicap_reset_info *)arg); + break; + case RKMODULE_SET_CSI_DPHY_PARAM: + dphy_param = (struct rkmodule_csi_dphy_param *)arg; + rk3588_dcphy_param = *dphy_param; + dev_dbg(&maxim2c->client->dev, "set dcphy param\n"); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + dphy_param = (struct rkmodule_csi_dphy_param *)arg; + *dphy_param = rk3588_dcphy_param; + dev_dbg(&maxim2c->client->dev, "get dcphy param\n"); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long maxim2c_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd, + unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_vicap_reset_info *vicap_rst_inf; + struct rkmodule_csi_dphy_param *dphy_param; + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = maxim2c_ioctl(sd, cmd, inf); + if (!ret) { + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) + ret = -EFAULT; + } + kfree(inf); + break; + case RKMODULE_GET_VICAP_RST_INFO: + vicap_rst_inf = kzalloc(sizeof(*vicap_rst_inf), GFP_KERNEL); + if (!vicap_rst_inf) { + ret = -ENOMEM; + return ret; + } + + ret = maxim2c_ioctl(sd, cmd, vicap_rst_inf); + if (!ret) { + ret = copy_to_user(up, vicap_rst_inf, sizeof(*vicap_rst_inf)); + if (ret) + ret = -EFAULT; + } + kfree(vicap_rst_inf); + break; + case RKMODULE_SET_VICAP_RST_INFO: + vicap_rst_inf = kzalloc(sizeof(*vicap_rst_inf), GFP_KERNEL); + if (!vicap_rst_inf) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(vicap_rst_inf, up, sizeof(*vicap_rst_inf)); + if (!ret) + ret = maxim2c_ioctl(sd, cmd, vicap_rst_inf); + else + ret = -EFAULT; + kfree(vicap_rst_inf); + break; + case RKMODULE_SET_CSI_DPHY_PARAM: + dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL); + if (!dphy_param) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(dphy_param, up, sizeof(*dphy_param)); + if (!ret) + ret = maxim2c_ioctl(sd, cmd, dphy_param); + else + ret = -EFAULT; + kfree(dphy_param); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL); + if (!dphy_param) { + ret = -ENOMEM; + return ret; + } + + ret = maxim2c_ioctl(sd, cmd, dphy_param); + if (!ret) { + ret = copy_to_user(up, dphy_param, sizeof(*dphy_param)); + if (ret) + ret = -EFAULT; + } + kfree(dphy_param); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif /* CONFIG_COMPAT */ + +static int __maxim2c_start_stream(struct maxim2c *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + int ret = 0; + s64 link_freq_hz = 0; + u8 link_mask = 0, link_freq_idx = 0; + u8 video_pipe_mask = 0; + +#if MAXIM2C_LOCAL_DES_ON_OFF_EN +#if MAXIM2C_TEST_PATTERN + ret = maxim2c_pattern_hw_init(maxim2c); + if (ret) { + dev_err(dev, "test pattern hw init error\n"); + return ret; + } +#else + ret = maxim2c_module_hw_init(maxim2c); + if (ret) { + dev_err(dev, "maxim2c module hw init error\n"); + return ret; + } +#endif /* MAXIM2C_TEST_PATTERN */ +#endif /* MAXIM2C_LOCAL_DES_ON_OFF_EN */ + + link_mask = maxim2c->gmsl_link.link_enable_mask; + video_pipe_mask = maxim2c->video_pipe.pipe_enable_mask; + + // disable all remote control + ret = maxim2c_link_select_remote_control(maxim2c, 0); + if (ret) { + dev_err(dev, "link disable remote control error\n"); + return ret; + } + + // disable all video pipe + ret = maxim2c_video_pipe_mask_enable(maxim2c, video_pipe_mask, false); + if (ret) { + dev_err(dev, "video pipe disable error\n"); + return ret; + } + + ret = maxim2c_link_select_remote_enable(maxim2c, link_mask); + if (ret) { + dev_err(dev, "link select enable error, mask = 0x%x\n", link_mask); + return ret; + } + + link_mask = maxim2c->gmsl_link.link_locked_mask; + ret = maxim2c_remote_devices_init(maxim2c, link_mask); + if (ret) { + dev_err(dev, "remote devices init error\n"); + return ret; + } + + // mipi txphy enable setting: standby or enable + ret = maxim2c_mipi_txphy_enable(maxim2c, true); + if (ret) { + dev_err(dev, "mipi txphy enable error\n"); + return ret; + } + + // mipi txphy dpll setting + link_freq_idx = maxim2c->cur_mode->link_freq_idx; + link_freq_hz = link_freq_items[link_freq_idx]; + ret = maxim2c_dphy_dpll_predef_set(maxim2c, link_freq_hz); + if (ret) { + dev_err(dev, "mipi txphy dpll setting error\n"); + return ret; + } + + // enable video pipe + ret = maxim2c_video_pipe_mask_enable(maxim2c, video_pipe_mask, true); + if (ret) { + dev_err(dev, "video pipe enable error\n"); + return ret; + } + + ret = maxim2c_link_select_remote_control(maxim2c, link_mask); + if (ret) { + dev_err(dev, "remote control enable error\n"); + return ret; + } + + /* In case these controls are set before streaming */ + mutex_unlock(&maxim2c->mutex); + ret = v4l2_ctrl_handler_setup(&maxim2c->ctrl_handler); + mutex_lock(&maxim2c->mutex); + if (ret) + return ret; + +#if MAXIM2C_TEST_PATTERN + ret = maxim2c_pattern_enable(maxim2c, true); + if (ret) { + dev_err(dev, "test pattern setting error\n"); + return ret; + } +#endif /* MAXIM2C_TEST_PATTERN */ + + ret = maxim2c_mipi_csi_output(maxim2c, true); + if (ret) { + dev_err(dev, "mipi csi output error\n"); + return ret; + } + + if (maxim2c->hot_plug_irq > 0) + enable_irq(maxim2c->hot_plug_irq); + + if (maxim2c->link_lock_state != maxim2c->gmsl_link.link_enable_mask) { + dev_info(dev, "partial links are locked, start hot plug detect work.\n"); + maxim2c_hot_plug_detect_work_start(maxim2c); + } + + return 0; +} + +static int __maxim2c_stop_stream(struct maxim2c *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + u8 link_mask = 0, pipe_mask = 0; + int ret = 0; + + link_mask = maxim2c->gmsl_link.link_enable_mask; + pipe_mask = maxim2c->video_pipe.pipe_enable_mask; + + if (maxim2c->hot_plug_irq > 0) + disable_irq(maxim2c->hot_plug_irq); + + if (maxim2c->hot_plug_work.state_check_wq) + cancel_delayed_work_sync(&maxim2c->hot_plug_work.state_d_work); + + ret |= maxim2c_mipi_csi_output(maxim2c, false); + ret |= maxim2c_mipi_txphy_enable(maxim2c, false); + +#if MAXIM2C_TEST_PATTERN + ret |= maxim2c_pattern_enable(maxim2c, false); +#endif /* MAXIM2C_TEST_PATTERN */ + + ret |= maxim2c_video_pipe_mask_enable(maxim2c, pipe_mask, false); + + ret |= maxim2c_remote_devices_deinit(maxim2c, link_mask); + + ret |= maxim2c_link_select_remote_control(maxim2c, 0); + ret |= maxim2c_link_mask_enable(maxim2c, link_mask, false); + + if (ret) { + dev_err(dev, "stop stream error\n"); + return ret; + } + + return 0; +} + +static int maxim2c_s_stream(struct v4l2_subdev *sd, int on) +{ + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + struct i2c_client *client = maxim2c->client; + int ret = 0; + + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + maxim2c->cur_mode->width, maxim2c->cur_mode->height, + DIV_ROUND_CLOSEST(maxim2c->cur_mode->max_fps.denominator, + maxim2c->cur_mode->max_fps.numerator)); + + mutex_lock(&maxim2c->mutex); + on = !!on; + if (on == maxim2c->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __maxim2c_start_stream(maxim2c); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __maxim2c_stop_stream(maxim2c); + pm_runtime_put(&client->dev); + } + + maxim2c->streaming = on; + +unlock_and_return: + mutex_unlock(&maxim2c->mutex); + + return ret; +} + +static int maxim2c_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + const struct maxim2c_mode *mode = maxim2c->cur_mode; + + mutex_lock(&maxim2c->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&maxim2c->mutex); + + return 0; +} + +static int maxim2c_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + const struct maxim2c_mode *mode = maxim2c->cur_mode; + + if (code->index != 0) + return -EINVAL; + code->code = mode->bus_fmt; + + return 0; +} + +static int maxim2c_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + + if (fse->index >= maxim2c->cfg_modes_num) + return -EINVAL; + + if (fse->code != maxim2c->supported_mode.bus_fmt) + return -EINVAL; + + fse->min_width = maxim2c->supported_mode.width; + fse->max_width = maxim2c->supported_mode.width; + fse->max_height = maxim2c->supported_mode.height; + fse->min_height = maxim2c->supported_mode.height; + + return 0; +} + +static int +maxim2c_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + + if (fie->index >= maxim2c->cfg_modes_num) + return -EINVAL; + + fie->code = maxim2c->supported_mode.bus_fmt; + fie->width = maxim2c->supported_mode.width; + fie->height = maxim2c->supported_mode.height; + fie->interval = maxim2c->supported_mode.max_fps; + + return 0; +} + +static int maxim2c_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + const struct maxim2c_mode *mode = maxim2c->cur_mode; + + mutex_lock(&maxim2c->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&maxim2c->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->pad < PAD_MAX && fmt->pad >= PAD0) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + } + mutex_unlock(&maxim2c->mutex); + + return 0; +} + +static int maxim2c_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + struct device *dev = &maxim2c->client->dev; + const struct maxim2c_mode *mode = NULL; + u64 link_freq = 0, pixel_rate = 0; + u8 data_lanes; + + mutex_lock(&maxim2c->mutex); + + mode = &maxim2c->supported_mode; + + fmt->format.code = mode->bus_fmt; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&maxim2c->mutex); + return -ENOTTY; +#endif + } else { + if (maxim2c->streaming) { + mutex_unlock(&maxim2c->mutex); + return -EBUSY; + } + + maxim2c->cur_mode = mode; + + __v4l2_ctrl_s_ctrl(maxim2c->link_freq, mode->link_freq_idx); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + link_freq = link_freq_items[mode->link_freq_idx]; + data_lanes = maxim2c->bus_cfg.bus.mipi_csi2.num_data_lanes; + pixel_rate = (u32)link_freq / mode->bpp * 2 * data_lanes; + __v4l2_ctrl_s_ctrl_int64(maxim2c->pixel_rate, pixel_rate); + + dev_info(dev, "mipi_freq_idx = %d, mipi_link_freq = %lld\n", + mode->link_freq_idx, link_freq); + dev_info(dev, "pixel_rate = %lld, bpp = %d\n", + pixel_rate, mode->bpp); + } + + mutex_unlock(&maxim2c->mutex); + + return 0; +} + +static int maxim2c_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left = 0; + sel->r.width = maxim2c->cur_mode->width; + sel->r.top = 0; + sel->r.height = maxim2c->cur_mode->height; + return 0; + } + + return -EINVAL; +} + +static int maxim2c_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *config) +{ + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + u32 val = 0; + u8 data_lanes = maxim2c->bus_cfg.bus.mipi_csi2.num_data_lanes; + + val |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + val |= (1 << (data_lanes - 1)); + + val |= V4L2_MBUS_CSI2_CHANNEL_3 | V4L2_MBUS_CSI2_CHANNEL_2 | + V4L2_MBUS_CSI2_CHANNEL_1 | V4L2_MBUS_CSI2_CHANNEL_0; + + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops maxim2c_internal_ops = { + .open = maxim2c_open, +}; +#endif + +static const struct v4l2_subdev_core_ops maxim2c_core_ops = { + .s_power = maxim2c_s_power, + .ioctl = maxim2c_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = maxim2c_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops maxim2c_video_ops = { + .s_stream = maxim2c_s_stream, + .g_frame_interval = maxim2c_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops maxim2c_pad_ops = { + .enum_mbus_code = maxim2c_enum_mbus_code, + .enum_frame_size = maxim2c_enum_frame_sizes, + .enum_frame_interval = maxim2c_enum_frame_interval, + .get_fmt = maxim2c_get_fmt, + .set_fmt = maxim2c_set_fmt, + .get_selection = maxim2c_get_selection, + .get_mbus_config = maxim2c_g_mbus_config, +}; + +static const struct v4l2_subdev_ops maxim2c_subdev_ops = { + .core = &maxim2c_core_ops, + .video = &maxim2c_video_ops, + .pad = &maxim2c_pad_ops, +}; + +static int maxim2c_initialize_controls(struct maxim2c *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + const struct maxim2c_mode *mode; + struct v4l2_ctrl_handler *handler; + u64 link_freq = 0, pixel_rate = 0; + u8 data_lanes; + int ret = 0; + + handler = &maxim2c->ctrl_handler; + + ret = v4l2_ctrl_handler_init(handler, 2); + if (ret) + return ret; + handler->lock = &maxim2c->mutex; + + mode = maxim2c->cur_mode; + maxim2c->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_items) - 1, 0, + link_freq_items); + __v4l2_ctrl_s_ctrl(maxim2c->link_freq, mode->link_freq_idx); + + link_freq = link_freq_items[mode->link_freq_idx]; + dev_info(dev, "mipi_freq_idx = %d, mipi_link_freq = %lld\n", + mode->link_freq_idx, link_freq); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + data_lanes = maxim2c->bus_cfg.bus.mipi_csi2.num_data_lanes; + pixel_rate = (u32)link_freq / mode->bpp * 2 * data_lanes; + maxim2c->pixel_rate = + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, + pixel_rate, 1, pixel_rate); + dev_info(dev, "pixel_rate = %lld, bpp = %d\n", + pixel_rate, mode->bpp); + + if (handler->error) { + ret = handler->error; + dev_err(dev, "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + maxim2c->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int maxim2c_mipi_data_lanes_parse(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + struct device_node *endpoint; + u8 mipi_data_lanes; + int ret = 0; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), + &maxim2c->bus_cfg); + if (ret) { + dev_err(dev, "Failed to get bus config\n"); + return -EINVAL; + } + mipi_data_lanes = maxim2c->bus_cfg.bus.mipi_csi2.num_data_lanes; + dev_info(dev, "mipi csi2 phy data lanes = %d\n", mipi_data_lanes); + + return 0; +} + +int maxim2c_v4l2_subdev_init(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + struct v4l2_subdev *sd = NULL; + char facing[2]; + int ret = 0; + + maxim2c_mipi_data_lanes_parse(maxim2c); + + maxim2c_support_mode_init(maxim2c); + + sd = &maxim2c->subdev; + v4l2_i2c_subdev_init(sd, client, &maxim2c_subdev_ops); + ret = maxim2c_initialize_controls(maxim2c); + if (ret) + goto err_free_handler; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &maxim2c_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif + +#if defined(CONFIG_MEDIA_CONTROLLER) + maxim2c->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &maxim2c->pad); + if (ret < 0) + goto err_free_handler; +#endif + + v4l2_set_subdevdata(sd, maxim2c); + + memset(facing, 0, sizeof(facing)); + if (strcmp(maxim2c->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + maxim2c->module_index, facing, MAXIM2C_NAME, + dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + +err_free_handler: + v4l2_ctrl_handler_free(&maxim2c->ctrl_handler); + + return ret; +} +EXPORT_SYMBOL(maxim2c_v4l2_subdev_init); + +void maxim2c_v4l2_subdev_deinit(maxim2c_t *maxim2c) +{ + struct v4l2_subdev *sd = &maxim2c->subdev; + + v4l2_async_unregister_subdev(sd); + +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + + v4l2_ctrl_handler_free(&maxim2c->ctrl_handler); +} +EXPORT_SYMBOL(maxim2c_v4l2_subdev_deinit); diff --git a/drivers/media/i2c/maxim2c/maxim2c_video_pipe.c b/drivers/media/i2c/maxim2c/maxim2c_video_pipe.c new file mode 100644 index 000000000000..032f7d7ff26f --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_video_pipe.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Dual GMSL Deserializer Video Pipe driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + */ +#include "maxim2c_api.h" + +static int maxim2c_video_pipe_select(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + maxim2c_video_pipe_t *video_pipe = &maxim2c->video_pipe; + struct maxim2c_pipe_cfg *video_pipe_cfg = NULL; + u8 link_idx, pipe_idx; + u8 reg_mask = 0, reg_value = 0; + int ret = 0; + + // video pipe selection + reg_mask = 0; + reg_value = 0; + + video_pipe_cfg = &video_pipe->pipe_cfg[MAXIM2C_PIPE_O_ID_Y]; + if (video_pipe_cfg->pipe_enable) { + reg_mask |= (0x7 << 0); + + pipe_idx = video_pipe_cfg->pipe_idx; + link_idx = video_pipe_cfg->link_idx; + reg_value |= ((pipe_idx + link_idx * MAXIM2C_PIPE_I_ID_MAX) << 0); + } + video_pipe_cfg = &video_pipe->pipe_cfg[MAXIM2C_PIPE_O_ID_Z]; + if (video_pipe_cfg->pipe_enable) { + reg_mask |= (0x7 << 3); + + pipe_idx = video_pipe_cfg->pipe_idx; + link_idx = video_pipe_cfg->link_idx; + + reg_value |= ((pipe_idx + link_idx * MAXIM2C_PIPE_I_ID_MAX) << 3); + } + + ret |= maxim2c_i2c_update_byte(client, + 0x0161, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + + return ret; +} + +static int maxim2c_video_pipe_run_init_seq(maxim2c_t *maxim2c) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + maxim2c_video_pipe_t *video_pipe = &maxim2c->video_pipe; + struct maxim2c_pipe_cfg *video_pipe_cfg = NULL; + struct maxim2c_i2c_init_seq *init_seq = NULL; + int i = 0; + int ret = 0; + + // video pipe init sequence + for (i = 0; i < MAXIM2C_PIPE_O_ID_MAX; i++) { + video_pipe_cfg = &video_pipe->pipe_cfg[i]; + init_seq = &video_pipe_cfg->pipe_init_seq; + ret = maxim2c_i2c_run_init_seq(client, init_seq); + if (ret) { + dev_err(dev, "pipe id = %d init sequence error\n", i); + return ret; + } + } + + // video pipe parallel mode init sequence + init_seq = &video_pipe->parallel_init_seq; + ret = maxim2c_i2c_run_init_seq(client, init_seq); + if (ret) { + dev_err(dev, "pipe parallel init sequence error\n"); + return ret; + } + + return 0; +} + +static int maxim2c_video_pipe_config_parse_dt(struct device *dev, + maxim2c_video_pipe_t *video_pipe, + struct device_node *parent_node) +{ + struct device_node *node = NULL; + struct device_node *init_seq_node = NULL; + struct maxim2c_i2c_init_seq *init_seq = NULL; + struct maxim2c_pipe_cfg *video_pipe_cfg = NULL; + const char *pipe_cfg_name = "video-pipe-config"; + u32 sub_idx = 0, pipe_id = 0; + u32 value = 0; + int ret = 0; + + node = NULL; + sub_idx = 0; + while ((node = of_get_next_child(parent_node, node))) { + if (!strncasecmp(node->name, + pipe_cfg_name, + strlen(pipe_cfg_name))) { + if (sub_idx >= MAXIM2C_PIPE_O_ID_MAX) { + dev_err(dev, "%pOF: Too many matching %s node\n", + parent_node, pipe_cfg_name); + + of_node_put(node); + break; + } + + if (!of_device_is_available(node)) { + dev_info(dev, "%pOF is disabled\n", node); + + sub_idx++; + + continue; + } + + /* Video Pipe: pipe id */ + ret = of_property_read_u32(node, "pipe-id", &pipe_id); + if (ret) { + // if pipe_id is error, parse next node + dev_err(dev, "Can not get pipe-id property!"); + + sub_idx++; + + continue; + } + if (pipe_id >= MAXIM2C_PIPE_O_ID_MAX) { + // if pipe_id is error, parse next node + dev_err(dev, "Error pipe-id = %d!", pipe_id); + + sub_idx++; + + continue; + } + + video_pipe_cfg = &video_pipe->pipe_cfg[pipe_id]; + + /* Video Pipe: pipe enable */ + video_pipe_cfg->pipe_enable = 1; + video_pipe->pipe_enable_mask |= BIT(pipe_id); + + dev_info(dev, "video pipe id = %d: pipe enable = %d\n", + pipe_id, video_pipe_cfg->pipe_enable); + + /* Video Pipe: other config */ + ret = of_property_read_u32(node, "pipe-idx", &value); + if (ret == 0) { + dev_info(dev, "pipe-idx property: %d", value); + video_pipe_cfg->pipe_idx = value; + } + + ret = of_property_read_u32(node, "link-idx", &value); + if (ret == 0) { + dev_info(dev, "link-idx property: %d", value); + video_pipe_cfg->link_idx = value; + } + + init_seq_node = of_get_child_by_name(node, "pipe-init-sequence"); + if (!IS_ERR_OR_NULL(init_seq_node)) { + dev_info(dev, "load pipe-init-sequence\n"); + + init_seq = &video_pipe_cfg->pipe_init_seq; + maxim2c_i2c_load_init_seq(dev, init_seq_node, init_seq); + + of_node_put(init_seq_node); + } + + sub_idx++; + } + } + + node = of_get_child_by_name(parent_node, "parallel-mode-config"); + if (!IS_ERR_OR_NULL(node)) { + if (!of_device_is_available(node)) { + dev_info(dev, "%pOF is disabled\n", node); + + of_node_put(node); + return 0; + } + + init_seq_node = of_get_child_by_name(node, "parallel-init-sequence"); + if (!IS_ERR_OR_NULL(init_seq_node)) { + dev_info(dev, "load parallel-init-sequence\n"); + + init_seq = &video_pipe->parallel_init_seq; + maxim2c_i2c_load_init_seq(dev, init_seq_node, init_seq); + + of_node_put(init_seq_node); + } + + of_node_put(node); + } + + return 0; +} + +int maxim2c_video_pipe_parse_dt(maxim2c_t *maxim2c, struct device_node *of_node) +{ + struct device *dev = &maxim2c->client->dev; + struct device_node *node = NULL; + maxim2c_video_pipe_t *video_pipe = &maxim2c->video_pipe; + int ret = 0; + + dev_info(dev, "=== maxim2c video pipe parse dt ===\n"); + + node = of_get_child_by_name(of_node, "video-pipes"); + if (IS_ERR_OR_NULL(node)) { + dev_err(dev, "%pOF has no child node: video-pipes\n", + of_node); + return -ENODEV; + } + + if (!of_device_is_available(node)) { + dev_info(dev, "%pOF is disabled\n", node); + of_node_put(node); + return -ENODEV; + } + + ret = maxim2c_video_pipe_config_parse_dt(dev, video_pipe, node); + + of_node_put(node); + + return ret; +} +EXPORT_SYMBOL(maxim2c_video_pipe_parse_dt); + +int maxim2c_video_pipe_mask_enable(maxim2c_t *maxim2c, u8 video_pipe_mask, bool enable) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + maxim2c_video_pipe_t *video_pipe = &maxim2c->video_pipe; + struct maxim2c_pipe_cfg *video_pipe_cfg = NULL; + u8 reg_mask = 0, reg_value = 0; + int i = 0; + int ret = 0; + + dev_dbg(dev, "%s, video_pipe_mask = 0x%x, enable = %d\n", + __func__, video_pipe_mask, enable); + + reg_mask = 0; + reg_value = 0; + // video pipe enable + for (i = 0; i < MAXIM2C_PIPE_O_ID_MAX; i++) { + video_pipe_cfg = &video_pipe->pipe_cfg[i]; + if (video_pipe_cfg->pipe_enable + && (video_pipe_mask & BIT(i))) { + reg_mask |= BIT(i); + if (enable) + reg_value |= BIT(i); + } + } + + if (reg_mask != 0) { + ret |= maxim2c_i2c_update_byte(client, + 0x0160, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + } + + return ret; +} +EXPORT_SYMBOL(maxim2c_video_pipe_mask_enable); + +int maxim2c_video_pipe_linkid_enable(maxim2c_t *maxim2c, u8 link_id, bool enable) +{ + struct i2c_client *client = maxim2c->client; + struct device *dev = &client->dev; + maxim2c_video_pipe_t *video_pipe = &maxim2c->video_pipe; + struct maxim2c_pipe_cfg *video_pipe_cfg = NULL; + u8 reg_mask = 0, reg_value = 0; + int i = 0; + int ret = 0; + + dev_dbg(dev, "%s, link_id = %d, enable = %d\n", + __func__, link_id, enable); + + reg_mask = 0; + reg_value = 0; + // video pipe enable + for (i = 0; i < MAXIM2C_PIPE_O_ID_MAX; i++) { + video_pipe_cfg = &video_pipe->pipe_cfg[i]; + if (video_pipe_cfg->pipe_enable + && (video_pipe_cfg->link_idx == link_id)) { + reg_mask = BIT(i); + if (enable) + reg_value = BIT(i); + } + } + + if (reg_mask != 0) { + ret = maxim2c_i2c_update_byte(client, + 0x0160, MAXIM2C_I2C_REG_ADDR_16BITS, + reg_mask, reg_value); + } + + return ret; +} +EXPORT_SYMBOL(maxim2c_video_pipe_linkid_enable); + +void maxim2c_video_pipe_data_init(maxim2c_t *maxim2c) +{ + maxim2c_video_pipe_t *video_pipe = &maxim2c->video_pipe; + struct maxim2c_pipe_cfg *video_pipe_cfg = NULL; + int i = 0; + + video_pipe->pipe_enable_mask = 0; + video_pipe->parallel_init_seq.reg_init_seq = NULL; + + for (i = 0; i < MAXIM2C_PIPE_O_ID_MAX; i++) { + video_pipe_cfg = &video_pipe->pipe_cfg[i]; + + video_pipe_cfg->pipe_enable = 0; + video_pipe_cfg->pipe_idx = MAXIM2C_PIPE_I_ID_Z; + video_pipe_cfg->link_idx = i; + video_pipe_cfg->pipe_init_seq.reg_init_seq = NULL; + } +} +EXPORT_SYMBOL(maxim2c_video_pipe_data_init); + +int maxim2c_video_pipe_hw_init(maxim2c_t *maxim2c) +{ + struct device *dev = &maxim2c->client->dev; + u8 pipe_enable_mask = 0; + int ret = 0; + + ret = maxim2c_video_pipe_select(maxim2c); + if (ret) { + dev_err(dev, "%s: video pipe select error\n", __func__); + return ret; + } + + pipe_enable_mask = maxim2c->video_pipe.pipe_enable_mask; + ret = maxim2c_video_pipe_mask_enable(maxim2c, pipe_enable_mask, true); + if (ret) { + dev_err(dev, "%s: video pipe mask enable error\n", __func__); + return ret; + } + + ret = maxim2c_video_pipe_run_init_seq(maxim2c); + if (ret) { + dev_err(dev, "%s: video pipe run init seq error\n", __func__); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(maxim2c_video_pipe_hw_init); diff --git a/drivers/media/i2c/maxim2c/maxim2c_video_pipe.h b/drivers/media/i2c/maxim2c/maxim2c_video_pipe.h new file mode 100644 index 000000000000..d7b149c37519 --- /dev/null +++ b/drivers/media/i2c/maxim2c/maxim2c_video_pipe.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Rockchip Electronics Co., Ltd. + * + */ + +#ifndef __MAXIM2C_VIDEO_PIPE_H__ +#define __MAXIM2C_VIDEO_PIPE_H__ + +#include "maxim2c_i2c.h" + +/* Video Pipe In ID: 0 ~ 3 */ +enum { + MAXIM2C_PIPE_I_ID_X = 0, + MAXIM2C_PIPE_I_ID_Y, + MAXIM2C_PIPE_I_ID_Z, + MAXIM2C_PIPE_I_ID_U, + MAXIM2C_PIPE_I_ID_MAX, +}; + +/* Video Pipe Out ID: 0 ~ 1 */ +enum { + MAXIM2C_PIPE_O_ID_Y = 0, + MAXIM2C_PIPE_O_ID_Z, + MAXIM2C_PIPE_O_ID_MAX, +}; + +/* Video Pipe Out Config */ +struct maxim2c_pipe_cfg { + u8 pipe_enable; + u8 pipe_idx; + u8 link_idx; + + struct maxim2c_i2c_init_seq pipe_init_seq; +}; + +typedef struct maxim2c_video_pipe { + u8 pipe_enable_mask; + + struct maxim2c_pipe_cfg pipe_cfg[MAXIM2C_PIPE_O_ID_MAX]; + struct maxim2c_i2c_init_seq parallel_init_seq; +} maxim2c_video_pipe_t; + +#endif /* __MAXIM2C_VIDEO_PIPE_H__ */ diff --git a/drivers/media/i2c/maxim2c/remote_max9295.c b/drivers/media/i2c/maxim2c/remote_max9295.c new file mode 100644 index 000000000000..0ae8a1cc6c28 --- /dev/null +++ b/drivers/media/i2c/maxim2c/remote_max9295.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Dual GMSL2/GMSL1 to CSI-2 Serializer driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + */ +#include +#include +#include +#include +#include + +#include "maxim2c_api.h" + +#define MAX9295_I2C_ADDR_DEF 0x40 + +#define MAX9295_CHIP_ID 0x91 +#define MAX9295_REG_CHIP_ID 0x0D + +static int max9295_i2c_addr_remap(maxim2c_remote_t *max9295) +{ + struct device *dev = max9295->dev; + struct i2c_client *client = max9295->client; + u16 i2c_8bit_addr = 0; + int ret = 0; + + if (max9295->ser_i2c_addr_map) { + dev_info(dev, "Serializer i2c address remap\n"); + + maxim2c_remote_i2c_addr_select(max9295, MAXIM2C_I2C_SER_DEF); + + i2c_8bit_addr = (max9295->ser_i2c_addr_map << 1); + ret = maxim2c_i2c_write_byte(client, + 0x0000, MAXIM2C_I2C_REG_ADDR_16BITS, + i2c_8bit_addr); + if (ret) { + dev_err(dev, "ser i2c address map setting error!\n"); + return ret; + } + + maxim2c_remote_i2c_addr_select(max9295, MAXIM2C_I2C_SER_MAP); + } + + if (max9295->cam_i2c_addr_map) { + dev_info(dev, "Camera i2c address remap\n"); + + i2c_8bit_addr = (max9295->cam_i2c_addr_map << 1); + ret = maxim2c_i2c_write_byte(client, + 0x0042, MAXIM2C_I2C_REG_ADDR_16BITS, + i2c_8bit_addr); + if (ret) { + dev_err(dev, "cam i2c address source setting error!\n"); + return ret; + } + + i2c_8bit_addr = (max9295->cam_i2c_addr_def << 1); + ret = maxim2c_i2c_write_byte(client, + 0x0043, MAXIM2C_I2C_REG_ADDR_16BITS, + i2c_8bit_addr); + if (ret) { + dev_err(dev, "cam i2c address destination setting error!\n"); + return ret; + } + } + + return 0; +} + +static int max9295_i2c_addr_def(maxim2c_remote_t *max9295) +{ + struct device *dev = max9295->dev; + struct i2c_client *client = max9295->client; + u16 i2c_8bit_addr = 0; + int ret = 0; + + if (max9295->ser_i2c_addr_map) { + dev_info(dev, "Serializer i2c address def\n"); + + maxim2c_remote_i2c_addr_select(max9295, MAXIM2C_I2C_SER_MAP); + + i2c_8bit_addr = (max9295->ser_i2c_addr_def << 1); + ret = maxim2c_i2c_write_byte(client, + 0x0000, MAXIM2C_I2C_REG_ADDR_16BITS, + i2c_8bit_addr); + if (ret) { + dev_err(dev, "ser i2c address def setting error!\n"); + return ret; + } + + maxim2c_remote_i2c_addr_select(max9295, MAXIM2C_I2C_SER_DEF); + } + + return 0; +} + +static int max9295_check_chipid(maxim2c_remote_t *max9295) +{ + struct device *dev = max9295->dev; + struct i2c_client *client = max9295->client; + u8 chip_id; + int ret = 0; + + // max9295 + ret = maxim2c_i2c_read_byte(client, + MAX9295_REG_CHIP_ID, MAXIM2C_I2C_REG_ADDR_16BITS, + &chip_id); + if (ret != 0) { + dev_info(dev, "Retry check chipid using map address\n"); + maxim2c_remote_i2c_addr_select(max9295, MAXIM2C_I2C_SER_MAP); + ret = maxim2c_i2c_read_byte(client, + MAX9295_REG_CHIP_ID, MAXIM2C_I2C_REG_ADDR_16BITS, + &chip_id); + if (ret != 0) { + dev_err(dev, "MAX9295 detect error, ret(%d)\n", ret); + maxim2c_remote_i2c_addr_select(max9295, MAXIM2C_I2C_SER_DEF); + + return -ENODEV; + } + + max9295_i2c_addr_def(max9295); + } + + if (chip_id != MAX9295_CHIP_ID) { + dev_err(dev, "Unexpected chip id = %02x\n", chip_id); + return -ENODEV; + } + + dev_info(dev, "Detected MAX9295 chip id: 0x%02x\n", chip_id); + + return 0; +} + +static int max9295_soft_power_down(maxim2c_remote_t *max9295) +{ + struct device *dev = max9295->dev; + struct i2c_client *client = max9295->client; + int ret = 0; + + ret = maxim2c_i2c_write_byte(client, + 0x10, MAXIM2C_I2C_REG_ADDR_16BITS, + BIT(7)); + if (ret) { + dev_err(dev, "soft power down setting error!\n"); + return ret; + } + + return 0; +} + +static int max9295_module_init(maxim2c_remote_t *max9295) +{ + struct device *dev = max9295->dev; + struct i2c_client *client = max9295->client; + int ret = 0; + + ret = maxim2c_remote_i2c_addr_select(max9295, MAXIM2C_I2C_SER_DEF); + if (ret) + return ret; + + ret = max9295_check_chipid(max9295); + if (ret) + return ret; + + ret = max9295_i2c_addr_remap(max9295); + if (ret) + return ret; + + ret = maxim2c_i2c_run_init_seq(client, + &max9295->remote_init_seq); + if (ret) { + dev_err(dev, "remote id = %d init sequence error\n", + max9295->remote_id); + return ret; + } + + return 0; +} + +static int max9295_module_deinit(maxim2c_remote_t *max9295) +{ + int ret = 0; + +#if 0 + ret |= max9295_i2c_addr_def(max9295); +#endif + ret |= max9295_soft_power_down(max9295); + + return ret; +} + +static const struct maxim2c_remote_ops max9295_ops = { + .remote_init = max9295_module_init, + .remote_deinit = max9295_module_deinit, +}; + +static int max9295_parse_dt(maxim2c_remote_t *max9295) +{ + struct device *dev = max9295->dev; + struct device_node *of_node = dev->of_node; + u32 value = 0; + int ret = 0; + + dev_info(dev, "=== maxim2c remote max9295 parse dt ===\n"); + + ret = of_property_read_u32(of_node, "remote-id", &value); + if (ret == 0) { + dev_info(dev, "remote-id property: %d\n", value); + max9295->remote_id = value; + } else { + max9295->remote_id = MAXIM2C_LINK_ID_MAX; + } + + dev_info(dev, "max9295 remote id: %d\n", max9295->remote_id); + + ret = of_property_read_u32(of_node, "ser-i2c-addr-def", &value); + if (ret == 0) { + dev_info(dev, "ser-i2c-addr-def property: 0x%x", value); + max9295->ser_i2c_addr_def = value; + } else { + max9295->ser_i2c_addr_def = MAX9295_I2C_ADDR_DEF; + } + + ret = of_property_read_u32(of_node, "ser-i2c-addr-map", &value); + if (ret == 0) { + dev_info(dev, "ser-i2c-addr-map property: 0x%x", value); + max9295->ser_i2c_addr_map = value; + } + + ret = of_property_read_u32(of_node, "cam-i2c-addr-def", &value); + if (ret == 0) { + dev_info(dev, "cam-i2c-addr-def property: 0x%x", value); + max9295->cam_i2c_addr_def = value; + } + + ret = of_property_read_u32(of_node, "cam-i2c-addr-map", &value); + if (ret == 0) { + dev_info(dev, "cam-i2c-addr-map property: 0x%x", value); + max9295->cam_i2c_addr_map = value; + } + + return 0; +} + +static int max9295_probe(struct platform_device *pdev) +{ + struct i2c_client *client = to_i2c_client(pdev->dev.parent); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + struct maxim2c_remote *max9295 = NULL; + u32 link_id = MAXIM2C_LINK_ID_MAX; + int ret = 0; + + dev_info(&pdev->dev, "max9295 serializer probe\n"); + + link_id = (uintptr_t)of_device_get_match_data(&pdev->dev); + link_id = link_id - MAXIM2C_LINK_ID_MAX; + if (link_id >= MAXIM2C_LINK_ID_MAX) { + dev_err(&pdev->dev, "max9295 probe match data error\n"); + return -EINVAL; + } + dev_info(&pdev->dev, "max9295 probe link id = %d\n", link_id); + + max9295 = devm_kzalloc(&pdev->dev, sizeof(*max9295), GFP_KERNEL); + if (!max9295) { + dev_err(&pdev->dev, "max9295 probe no memory error\n"); + return -ENOMEM; + } + + max9295->dev = &pdev->dev; + max9295->remote_ops = &max9295_ops; + max9295->local = maxim2c; + dev_set_drvdata(max9295->dev, max9295); + + max9295_parse_dt(max9295); + + if (max9295->remote_id != link_id) { + dev_err(&pdev->dev, "max9295 probe remote_id error\n"); + return -EINVAL; + } + + ret = maxim2c_remote_i2c_client_init(max9295, client); + if (ret) { + dev_err(&pdev->dev, "remote i2c client init error\n"); + return ret; + } + + ret = maxim2c_remote_device_register(maxim2c, max9295); + if (ret) { + dev_err(&pdev->dev, "remote serializer register error\n"); + return ret; + } + + maxim2c_remote_load_init_seq(max9295); + + return 0; +} + +static int max9295_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id max9295_of_table[] = { + { + .compatible = "maxim2c,link0,max9295", + .data = (const void *)(MAXIM2C_LINK_ID_MAX + MAXIM2C_LINK_ID_A) + }, { + .compatible = "maxim2c,link1,max9295", + .data = (const void *)(MAXIM2C_LINK_ID_MAX + MAXIM2C_LINK_ID_B) + }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, max9295_of_table); + +static struct platform_driver max9295_driver = { + .probe = max9295_probe, + .remove = max9295_remove, + .driver = { + .name = "maxim2c-max9295", + .of_match_table = max9295_of_table, + }, +}; + +module_platform_driver(max9295_driver); + +MODULE_AUTHOR("Cai Wenzhong "); +MODULE_DESCRIPTION("Maxim MAX9295 Serializer Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/maxim2c/remote_max96715.c b/drivers/media/i2c/maxim2c/remote_max96715.c new file mode 100644 index 000000000000..d1c43739b84a --- /dev/null +++ b/drivers/media/i2c/maxim2c/remote_max96715.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Dual GMSL2/GMSL1 to CSI-2 Serializer driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + */ +#include +#include +#include +#include +#include +#include + +#include "maxim2c_api.h" + +#define MAX96715_I2C_ADDR_DEF 0x40 + +#define MAX96715_CHIP_ID 0x45 +#define MAX96715_REG_CHIP_ID 0x1E + +/* Config and Video mode switch */ +#define MAX96715_MODE_SWITCH 1 + +enum { + LINK_MODE_VIDEO = 0, + LINK_MODE_CONFIG, +}; + +static int __maybe_unused max96715_link_mode_select(maxim2c_remote_t *max96715, u32 mode) +{ + struct device *dev = max96715->dev; + struct i2c_client *client = max96715->client; + u8 reg_mask = 0, reg_value = 0; + u32 delay_ms = 0; + int ret = 0; + + dev_dbg(dev, "%s: mode = %d\n", __func__, mode); + + reg_mask = BIT(7) | BIT(6); + if (mode == LINK_MODE_CONFIG) { + reg_value = BIT(6); + delay_ms = 5; + } else { + reg_value = BIT(7); + delay_ms = 50; + } + ret |= maxim2c_i2c_update_byte(client, + 0x04, MAXIM2C_I2C_REG_ADDR_08BITS, + reg_mask, reg_value); + + msleep(delay_ms); + + return ret; +} + +static int max96715_i2c_addr_remap(maxim2c_remote_t *max96715) +{ + struct device *dev = max96715->dev; + struct i2c_client *client = max96715->client; + u16 i2c_8bit_addr = 0; + int ret = 0; + + if (max96715->ser_i2c_addr_map) { + dev_info(dev, "Serializer i2c address remap\n"); + + maxim2c_remote_i2c_addr_select(max96715, MAXIM2C_I2C_SER_DEF); + + i2c_8bit_addr = (max96715->ser_i2c_addr_map << 1); + ret = maxim2c_i2c_write_byte(client, + 0x00, MAXIM2C_I2C_REG_ADDR_08BITS, + i2c_8bit_addr); + if (ret) { + dev_err(dev, "ser i2c address map setting error!\n"); + return ret; + } + + maxim2c_remote_i2c_addr_select(max96715, MAXIM2C_I2C_SER_MAP); + } + + if (max96715->cam_i2c_addr_map) { + dev_info(dev, "Camera i2c address remap\n"); + + i2c_8bit_addr = (max96715->cam_i2c_addr_map << 1); + ret = maxim2c_i2c_write_byte(client, + 0x09, MAXIM2C_I2C_REG_ADDR_08BITS, + i2c_8bit_addr); + if (ret) { + dev_err(dev, "cam i2c address source setting error!\n"); + return ret; + } + + i2c_8bit_addr = (max96715->cam_i2c_addr_def << 1); + ret = maxim2c_i2c_write_byte(client, + 0x0A, MAXIM2C_I2C_REG_ADDR_08BITS, + i2c_8bit_addr); + if (ret) { + dev_err(dev, "cam i2c address destination setting error!\n"); + return ret; + } + } + + return 0; +} + +static int max96715_i2c_addr_def(maxim2c_remote_t *max96715) +{ + struct device *dev = max96715->dev; + struct i2c_client *client = max96715->client; + u16 i2c_8bit_addr = 0; + int ret = 0; + + if (max96715->ser_i2c_addr_map) { + dev_info(dev, "Serializer i2c address def\n"); + + maxim2c_remote_i2c_addr_select(max96715, MAXIM2C_I2C_SER_MAP); + + i2c_8bit_addr = (max96715->ser_i2c_addr_def << 1); + ret = maxim2c_i2c_write_byte(client, + 0x00, MAXIM2C_I2C_REG_ADDR_08BITS, + i2c_8bit_addr); + if (ret) { + dev_err(dev, "ser i2c address def setting error!\n"); + return ret; + } + + maxim2c_remote_i2c_addr_select(max96715, MAXIM2C_I2C_SER_DEF); + } + + return 0; +} + +static int max96715_check_chipid(maxim2c_remote_t *max96715) +{ + struct device *dev = max96715->dev; + struct i2c_client *client = max96715->client; + u8 chip_id; + int ret = 0; + + // max96715 + ret = maxim2c_i2c_read_byte(client, + MAX96715_REG_CHIP_ID, MAXIM2C_I2C_REG_ADDR_08BITS, + &chip_id); + if (ret != 0) { + dev_info(dev, "Retry check chipid using map address\n"); + maxim2c_remote_i2c_addr_select(max96715, MAXIM2C_I2C_SER_MAP); + ret = maxim2c_i2c_read_byte(client, + MAX96715_REG_CHIP_ID, MAXIM2C_I2C_REG_ADDR_08BITS, + &chip_id); + if (ret != 0) { + dev_err(dev, "MAX96715 detect error, ret(%d)\n", ret); + maxim2c_remote_i2c_addr_select(max96715, MAXIM2C_I2C_SER_DEF); + + return -ENODEV; + } + + max96715_i2c_addr_def(max96715); + } + + if (chip_id != MAX96715_CHIP_ID) { + dev_err(dev, "Unexpected chip id = %02x\n", chip_id); + return -ENODEV; + } + + dev_info(dev, "Detected MAX96715 chip id: 0x%02x\n", chip_id); + + return 0; +} + +static int max96715_soft_power_down(maxim2c_remote_t *max96715) +{ + struct device *dev = max96715->dev; + struct i2c_client *client = max96715->client; + int ret = 0; + + ret = maxim2c_i2c_write_byte(client, + 0x13, MAXIM2C_I2C_REG_ADDR_08BITS, + BIT(7)); + if (ret) { + dev_err(dev, "soft power down setting error!\n"); + return ret; + } + + return 0; +} + +static int max96715_module_init(maxim2c_remote_t *max96715) +{ + struct device *dev = max96715->dev; + struct i2c_client *client = max96715->client; + int ret = 0; + + ret = maxim2c_remote_i2c_addr_select(max96715, MAXIM2C_I2C_SER_DEF); + if (ret) + return ret; + + ret = max96715_check_chipid(max96715); + if (ret) + return ret; + + ret = max96715_i2c_addr_remap(max96715); + if (ret) + return ret; + +#if MAX96715_MODE_SWITCH + ret = max96715_link_mode_select(max96715, LINK_MODE_CONFIG); + if (ret) + return ret; +#endif + + ret = maxim2c_i2c_run_init_seq(client, + &max96715->remote_init_seq); + + if (ret) { + dev_err(dev, "remote id = %d init sequence error\n", + max96715->remote_id); + + return ret; + } + +#if MAX96715_MODE_SWITCH + ret = max96715_link_mode_select(max96715, LINK_MODE_VIDEO); + if (ret) + return ret; +#endif + + return 0; +} + +static int max96715_module_deinit(maxim2c_remote_t *max96715) +{ + int ret = 0; + +#if 0 + ret |= max96715_i2c_addr_def(max96715); +#endif + ret |= max96715_soft_power_down(max96715); + + return ret; +} + +static const struct maxim2c_remote_ops max96715_ops = { + .remote_init = max96715_module_init, + .remote_deinit = max96715_module_deinit, +}; + +static int max96715_parse_dt(maxim2c_remote_t *max96715) +{ + struct device *dev = max96715->dev; + struct device_node *of_node = dev->of_node; + u32 value = 0; + int ret = 0; + + dev_info(dev, "=== maxim2c remote max96715 parse dt ===\n"); + + ret = of_property_read_u32(of_node, "remote-id", &value); + if (ret == 0) { + dev_info(dev, "remote-id property: %d\n", value); + max96715->remote_id = value; + } else { + max96715->remote_id = MAXIM2C_LINK_ID_MAX; + } + + dev_info(dev, "max96715 remote id: %d\n", max96715->remote_id); + + ret = of_property_read_u32(of_node, "ser-i2c-addr-def", &value); + if (ret == 0) { + dev_info(dev, "ser-i2c-addr-def property: 0x%x", value); + max96715->ser_i2c_addr_def = value; + } else { + max96715->ser_i2c_addr_def = MAX96715_I2C_ADDR_DEF; + } + + ret = of_property_read_u32(of_node, "ser-i2c-addr-map", &value); + if (ret == 0) { + dev_info(dev, "ser-i2c-addr-map property: 0x%x", value); + max96715->ser_i2c_addr_map = value; + } + + ret = of_property_read_u32(of_node, "cam-i2c-addr-def", &value); + if (ret == 0) { + dev_info(dev, "cam-i2c-addr-def property: 0x%x", value); + max96715->cam_i2c_addr_def = value; + } + + ret = of_property_read_u32(of_node, "cam-i2c-addr-map", &value); + if (ret == 0) { + dev_info(dev, "cam-i2c-addr-map property: 0x%x", value); + max96715->cam_i2c_addr_map = value; + } + + return 0; +} + +static int max96715_probe(struct platform_device *pdev) +{ + struct i2c_client *client = to_i2c_client(pdev->dev.parent); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + struct maxim2c_remote *max96715 = NULL; + u32 link_id = MAXIM2C_LINK_ID_MAX; + int ret = 0; + + dev_info(&pdev->dev, "max96715 serializer probe\n"); + + link_id = (uintptr_t)of_device_get_match_data(&pdev->dev); + link_id = link_id - MAXIM2C_LINK_ID_MAX; + if (link_id >= MAXIM2C_LINK_ID_MAX) { + dev_err(&pdev->dev, "max96715 probe match data error\n"); + return -EINVAL; + } + dev_info(&pdev->dev, "max96715 probe link id = %d\n", link_id); + + max96715 = devm_kzalloc(&pdev->dev, sizeof(*max96715), GFP_KERNEL); + if (!max96715) { + dev_err(&pdev->dev, "max96715 probe no memory error\n"); + return -ENOMEM; + } + + max96715->dev = &pdev->dev; + max96715->remote_ops = &max96715_ops; + max96715->local = maxim2c; + dev_set_drvdata(max96715->dev, max96715); + + max96715_parse_dt(max96715); + + if (max96715->remote_id != link_id) { + dev_err(&pdev->dev, "max96715 probe remote_id error\n"); + return -EINVAL; + } + + ret = maxim2c_remote_i2c_client_init(max96715, client); + if (ret) { + dev_err(&pdev->dev, "remote i2c client init error\n"); + return ret; + } + + ret = maxim2c_remote_device_register(maxim2c, max96715); + if (ret) { + dev_err(&pdev->dev, "remote serializer register error\n"); + return ret; + } + + maxim2c_remote_load_init_seq(max96715); + + return 0; +} + +static int max96715_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id max96715_of_table[] = { + { + .compatible = "maxim2c,link0,max96715", + .data = (const void *)(MAXIM2C_LINK_ID_MAX + MAXIM2C_LINK_ID_A) + }, { + .compatible = "maxim2c,link1,max96715", + .data = (const void *)(MAXIM2C_LINK_ID_MAX + MAXIM2C_LINK_ID_B) + }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, max96715_of_table); + +static struct platform_driver max96715_driver = { + .probe = max96715_probe, + .remove = max96715_remove, + .driver = { + .name = "maxim2c-max96715", + .of_match_table = max96715_of_table, + }, +}; + +module_platform_driver(max96715_driver); + +MODULE_AUTHOR("Cai Wenzhong "); +MODULE_DESCRIPTION("Maxim MAX96715 Serializer Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/maxim2c/remote_max96717.c b/drivers/media/i2c/maxim2c/remote_max96717.c new file mode 100644 index 000000000000..6e7fb9db46f5 --- /dev/null +++ b/drivers/media/i2c/maxim2c/remote_max96717.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Dual GMSL2/GMSL1 to CSI-2 Serializer driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + */ +#include +#include +#include +#include +#include + +#include "maxim2c_api.h" + +#define MAX96717_I2C_ADDR_DEF 0x40 + +#define MAX96717_CHIP_ID 0xBF +#define MAX96717_REG_CHIP_ID 0x0D + +static int max96717_i2c_addr_remap(maxim2c_remote_t *max96717) +{ + struct device *dev = max96717->dev; + struct i2c_client *client = max96717->client; + u16 i2c_8bit_addr = 0; + int ret = 0; + + if (max96717->ser_i2c_addr_map) { + dev_info(dev, "Serializer i2c address remap\n"); + + maxim2c_remote_i2c_addr_select(max96717, MAXIM2C_I2C_SER_DEF); + + i2c_8bit_addr = (max96717->ser_i2c_addr_map << 1); + ret = maxim2c_i2c_write_byte(client, + 0x0000, MAXIM2C_I2C_REG_ADDR_16BITS, + i2c_8bit_addr); + if (ret) { + dev_err(dev, "ser i2c address map setting error!\n"); + return ret; + } + + maxim2c_remote_i2c_addr_select(max96717, MAXIM2C_I2C_SER_MAP); + } + + if (max96717->cam_i2c_addr_map) { + dev_info(dev, "Camera i2c address remap\n"); + + i2c_8bit_addr = (max96717->cam_i2c_addr_map << 1); + ret = maxim2c_i2c_write_byte(client, + 0x0042, MAXIM2C_I2C_REG_ADDR_16BITS, + i2c_8bit_addr); + if (ret) { + dev_err(dev, "cam i2c address source setting error!\n"); + return ret; + } + + i2c_8bit_addr = (max96717->cam_i2c_addr_def << 1); + ret = maxim2c_i2c_write_byte(client, + 0x0043, MAXIM2C_I2C_REG_ADDR_16BITS, + i2c_8bit_addr); + if (ret) { + dev_err(dev, "cam i2c address destination setting error!\n"); + return ret; + } + } + + return 0; +} + +static int max96717_i2c_addr_def(maxim2c_remote_t *max96717) +{ + struct device *dev = max96717->dev; + struct i2c_client *client = max96717->client; + u16 i2c_8bit_addr = 0; + int ret = 0; + + if (max96717->ser_i2c_addr_map) { + dev_info(dev, "Serializer i2c address def\n"); + + maxim2c_remote_i2c_addr_select(max96717, MAXIM2C_I2C_SER_MAP); + + i2c_8bit_addr = (max96717->ser_i2c_addr_def << 1); + ret = maxim2c_i2c_write_byte(client, + 0x0000, MAXIM2C_I2C_REG_ADDR_16BITS, + i2c_8bit_addr); + if (ret) { + dev_err(dev, "ser i2c address def setting error!\n"); + return ret; + } + + maxim2c_remote_i2c_addr_select(max96717, MAXIM2C_I2C_SER_DEF); + } + + return 0; +} + +static int max96717_check_chipid(maxim2c_remote_t *max96717) +{ + struct device *dev = max96717->dev; + struct i2c_client *client = max96717->client; + u8 chip_id; + int ret = 0; + + // max96717 + ret = maxim2c_i2c_read_byte(client, + MAX96717_REG_CHIP_ID, MAXIM2C_I2C_REG_ADDR_16BITS, + &chip_id); + if (ret != 0) { + dev_info(dev, "Retry check chipid using map address\n"); + maxim2c_remote_i2c_addr_select(max96717, MAXIM2C_I2C_SER_MAP); + ret = maxim2c_i2c_read_byte(client, + MAX96717_REG_CHIP_ID, MAXIM2C_I2C_REG_ADDR_16BITS, + &chip_id); + if (ret != 0) { + dev_err(dev, "MAX96717 detect error, ret(%d)\n", ret); + maxim2c_remote_i2c_addr_select(max96717, MAXIM2C_I2C_SER_DEF); + + return -ENODEV; + } + + max96717_i2c_addr_def(max96717); + } + + if (chip_id != MAX96717_CHIP_ID) { + dev_err(dev, "Unexpected chip id = %02x\n", chip_id); + return -ENODEV; + } + dev_info(dev, "Detected MAX96717 chip id: 0x%02x\n", chip_id); + + return 0; +} + +static int max96717_module_init(maxim2c_remote_t *max96717) +{ + struct device *dev = max96717->dev; + struct i2c_client *client = max96717->client; + int ret = 0; + + ret = maxim2c_remote_i2c_addr_select(max96717, MAXIM2C_I2C_SER_DEF); + if (ret) + return ret; + + ret = max96717_check_chipid(max96717); + if (ret) + return ret; + + ret = max96717_i2c_addr_remap(max96717); + if (ret) + return ret; + + ret = maxim2c_i2c_run_init_seq(client, + &max96717->remote_init_seq); + if (ret) { + dev_err(dev, "remote id = %d init sequence error\n", + max96717->remote_id); + return ret; + } + + return 0; +} + +static int max96717_module_deinit(maxim2c_remote_t *max96717) +{ + int ret = 0; + + ret |= max96717_i2c_addr_def(max96717); + + return ret; +} + +static const struct maxim2c_remote_ops max96717_ops = { + .remote_init = max96717_module_init, + .remote_deinit = max96717_module_deinit, +}; + +static int max96717_parse_dt(maxim2c_remote_t *max96717) +{ + struct device *dev = max96717->dev; + struct device_node *of_node = dev->of_node; + u32 value = 0; + int ret = 0; + + dev_info(dev, "=== maxim2c remote max96717 parse dt ===\n"); + + ret = of_property_read_u32(of_node, "remote-id", &value); + if (ret == 0) { + dev_info(dev, "remote-id property: %d\n", value); + max96717->remote_id = value; + } else { + max96717->remote_id = MAXIM2C_LINK_ID_MAX; + } + + dev_info(dev, "max96717 remote id: %d\n", max96717->remote_id); + + ret = of_property_read_u32(of_node, "ser-i2c-addr-def", &value); + if (ret == 0) { + dev_info(dev, "ser-i2c-addr-def property: 0x%x", value); + max96717->ser_i2c_addr_def = value; + } else { + max96717->ser_i2c_addr_def = MAX96717_I2C_ADDR_DEF; + } + + ret = of_property_read_u32(of_node, "ser-i2c-addr-map", &value); + if (ret == 0) { + dev_info(dev, "ser-i2c-addr-map property: 0x%x", value); + max96717->ser_i2c_addr_map = value; + } + + ret = of_property_read_u32(of_node, "cam-i2c-addr-def", &value); + if (ret == 0) { + dev_info(dev, "cam-i2c-addr-def property: 0x%x", value); + max96717->cam_i2c_addr_def = value; + } + + ret = of_property_read_u32(of_node, "cam-i2c-addr-map", &value); + if (ret == 0) { + dev_info(dev, "cam-i2c-addr-map property: 0x%x", value); + max96717->cam_i2c_addr_map = value; + } + + return 0; +} + +static int max96717_probe(struct platform_device *pdev) +{ + struct i2c_client *client = to_i2c_client(pdev->dev.parent); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct maxim2c *maxim2c = v4l2_get_subdevdata(sd); + struct maxim2c_remote *max96717 = NULL; + u32 link_id = MAXIM2C_LINK_ID_MAX; + int ret = 0; + + dev_info(&pdev->dev, "max96717 serializer probe\n"); + + link_id = (uintptr_t)of_device_get_match_data(&pdev->dev); + link_id = link_id - MAXIM2C_LINK_ID_MAX; + if (link_id >= MAXIM2C_LINK_ID_MAX) { + dev_err(&pdev->dev, "max96717 probe match data error\n"); + return -EINVAL; + } + dev_info(&pdev->dev, "max96717 probe link id = %d\n", link_id); + + max96717 = devm_kzalloc(&pdev->dev, sizeof(*max96717), GFP_KERNEL); + if (!max96717) { + dev_err(&pdev->dev, "max96717 probe no memory error\n"); + return -ENOMEM; + } + + max96717->dev = &pdev->dev; + max96717->remote_ops = &max96717_ops; + max96717->local = maxim2c; + dev_set_drvdata(max96717->dev, max96717); + + max96717_parse_dt(max96717); + + if (max96717->remote_id != link_id) { + dev_err(&pdev->dev, "max96717 probe remote_id error\n"); + return -EINVAL; + } + + ret = maxim2c_remote_i2c_client_init(max96717, client); + if (ret) { + dev_err(&pdev->dev, "remote i2c client init error\n"); + return ret; + } + + ret = maxim2c_remote_device_register(maxim2c, max96717); + if (ret) { + dev_err(&pdev->dev, "remote serializer register error\n"); + return ret; + } + + maxim2c_remote_load_init_seq(max96717); + + return 0; +} + +static int max96717_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id max96717_of_table[] = { + { + .compatible = "maxim2c,link0,max96717", + .data = (const void *)(MAXIM2C_LINK_ID_MAX + MAXIM2C_LINK_ID_A) + }, { + .compatible = "maxim2c,link1,max96717", + .data = (const void *)(MAXIM2C_LINK_ID_MAX + MAXIM2C_LINK_ID_B) + }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, max96717_of_table); + +static struct platform_driver max96717_driver = { + .probe = max96717_probe, + .remove = max96717_remove, + .driver = { + .name = "maxim2c-max96717", + .of_match_table = max96717_of_table, + }, +}; + +module_platform_driver(max96717_driver); + +MODULE_AUTHOR("Cai Wenzhong "); +MODULE_DESCRIPTION("Maxim MAX96717 Serializer Driver"); +MODULE_LICENSE("GPL");