media: i2c: add Maxim Quad GMSL2/GMSL1 to CSI-2 Deserializer driver.

1. maxim deserializer support: max96712/max96722
1.1 local maxim deserlializer depend on CONFIG_VIDEO_DES_MAXIM4C

2. maxim serializer support: max9295/max96715/max96717
2.1 remote max9295 serializer depend on CONFIG_MAXIM4C_SER_MAX9295
2.2 remote max96715 serializer depend on CONFIG_MAXIM4C_SER_MAX96715
2.3 remote max96717 serializer depend on CONFIG_MAXIM4C_SER_MAX96717

Signed-off-by: Cai Wenzhong <cwz@rock-chips.com>
Change-Id: I0deab6dd47a419bed9ebf643e451d71dc8f151a4
This commit is contained in:
Cai Wenzhong
2023-08-03 12:02:11 +08:00
committed by Tao Huang
parent 383b245ce2
commit 739cc63893
23 changed files with 5975 additions and 0 deletions

View File

@@ -661,6 +661,8 @@ config VIDEO_MAX96722
To compile this driver as a module, choose M here: the
module will be called max96722.
source "drivers/media/i2c/maxim4c/Kconfig"
comment "Video and audio decoders"
config VIDEO_SAA717X

View File

@@ -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_MAXIM4C) += maxim4c/
obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o

View File

@@ -0,0 +1,44 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Maxim Quad GMSL deserializer and serializer devices
#
config VIDEO_DES_MAXIM4C
tristate "Maxim Qual 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 Qual GMSL2/GMSL1 deserializer.
To compile this driver as a module, choose M here: the
module will be called maxim4c.
config MAXIM4C_SER_MAX9295
tristate "Maxim GMSL serializer support"
depends on I2C
select VIDEO_DES_MAXIM4C
help
This driver supports the Maxim GMSL2 serializer.
To compile this driver as a module, choose M here: the
module will be called remote_max9295.
config MAXIM4C_SER_MAX96715
tristate "Maxim GMSL serializer support"
depends on I2C
select VIDEO_DES_MAXIM4C
help
This driver supports the Maxim GMSL1 serializer.
To compile this driver as a module, choose M here: the
module will be called remote_max96715.
config MAXIM4C_SER_MAX96717
tristate "Maxim GMSL serializer support"
depends on I2C
select VIDEO_DES_MAXIM4C
help
This driver supports the Maxim GMSL2 serializer.
To compile this driver as a module, choose M here: the
module will be called remote_max96717.

View File

@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_VIDEO_DES_MAXIM4C) += maxim4c.o
maxim4c-objs += maxim4c_i2c.o \
maxim4c_mipi_txphy.o \
maxim4c_video_pipe.o \
maxim4c_link.o \
maxim4c_remote.o \
maxim4c_pattern.o \
maxim4c_v4l2.o \
maxim4c_drv.o
obj-$(CONFIG_MAXIM4C_SER_MAX9295) += remote_max9295.o
obj-$(CONFIG_MAXIM4C_SER_MAX96715) += remote_max96715.o
obj-$(CONFIG_MAXIM4C_SER_MAX96717) += remote_max96717.o

View File

@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Maxim Quad GMSL Deserializer driver API function declaration
*
* Copyright (c) 2023 Rockchip Electronics Co., Ltd.
*
* Author: Cai Wenzhong <cwz@rock-chips.com>
*/
#ifndef __MAXIM4C_API_H__
#define __MAXIM4C_API_H__
#include "maxim4c_i2c.h"
#include "maxim4c_link.h"
#include "maxim4c_video_pipe.h"
#include "maxim4c_mipi_txphy.h"
#include "maxim4c_remote.h"
#include "maxim4c_pattern.h"
#include "maxim4c_drv.h"
#define MAXIM4C_NAME "maxim4c"
/* Maxim Deserializer Test Pattern */
#define MAXIM4C_TEST_PATTERN 0
/* maxim4c i2c api */
int maxim4c_i2c_write_reg(struct i2c_client *client,
u16 reg_addr, u16 reg_len, u16 val_len, u32 reg_val);
int maxim4c_i2c_read_reg(struct i2c_client *client,
u16 reg_addr, u16 reg_len, u16 val_len, u32 *reg_val);
int maxim4c_i2c_update_reg(struct i2c_client *client,
u16 reg_addr, u16 reg_len,
u32 val_len, u32 val_mask, u32 reg_val);
int maxim4c_i2c_write_byte(struct i2c_client *client,
u16 reg_addr, u16 reg_len, u8 reg_val);
int maxim4c_i2c_read_byte(struct i2c_client *client,
u16 reg_addr, u16 reg_len, u8 *reg_val);
int maxim4c_i2c_update_byte(struct i2c_client *client,
u16 reg_addr, u16 reg_len, u8 val_mask, u8 reg_val);
int maxim4c_i2c_write_array(struct i2c_client *client,
const struct maxim4c_i2c_regval *regs);
int maxim4c_i2c_load_init_seq(struct device *dev,
struct device_node *node, struct maxim4c_i2c_init_seq *init_seq);
int maxim4c_i2c_run_init_seq(struct i2c_client *client,
struct maxim4c_i2c_init_seq *init_seq);
/* maxim4c link api */
u8 maxim4c_link_get_lock_state(maxim4c_t *maxim4c, u8 link_mask);
int maxim4c_link_oneshot_reset(maxim4c_t *maxim4c, u8 link_mask);
int maxim4c_link_mask_enable(maxim4c_t *maxim4c, u8 link_mask, bool enable);
int maxim4c_link_wait_linklock(maxim4c_t *maxim4c, u8 link_mask);
int maxim4c_link_select_remote_enable(maxim4c_t *maxim4c, u8 link_mask);
int maxim4c_link_select_remote_control(maxim4c_t *maxim4c, u8 link_mask);
int maxim4c_link_hw_init(maxim4c_t *maxim4c);
void maxim4c_link_data_init(maxim4c_t *maxim4c);
int maxim4c_link_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node);
/* maxim4c video pipe api */
int maxim4c_video_pipe_hw_init(maxim4c_t *maxim4c);
int maxim4c_video_pipe_mask_enable(maxim4c_t *maxim4c, u8 video_pipe_mask, bool enable);
int maxim4c_video_pipe_linkid_enable(maxim4c_t *maxim4c, u8 link_id, bool enable);
void maxim4c_video_pipe_data_init(maxim4c_t *maxim4c);
int maxim4c_video_pipe_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node);
/* maxim4c mipi txphy api */
int maxim4c_mipi_txphy_hw_init(maxim4c_t *maxim4c);
void maxim4c_mipi_txphy_data_init(maxim4c_t *maxim4c);
int maxim4c_mipi_txphy_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node);
int maxim4c_mipi_txphy_enable(maxim4c_t *maxim4c, bool enable);
int maxim4c_dphy_dpll_predef_set(maxim4c_t *maxim4c, s64 link_freq_hz);
int maxim4c_mipi_csi_output(maxim4c_t *maxim4c, bool enable);
/* maxim4c remote api */
int maxim4c_remote_mfd_add_devices(maxim4c_t *maxim4c);
int maxim4c_remote_devices_init(maxim4c_t *maxim4c, u8 link_init_mask);
int maxim4c_remote_devices_deinit(maxim4c_t *maxim4c, u8 link_init_mask);
int maxim4c_remote_load_init_seq(maxim4c_remote_t *remote_device);
int maxim4c_remote_device_register(maxim4c_t *maxim4c,
maxim4c_remote_t *remote_device);
/* maxim4c v4l2 subdev api */
int maxim4c_v4l2_subdev_init(maxim4c_t *maxim4c);
void maxim4c_v4l2_subdev_deinit(maxim4c_t *maxim4c);
/* maxim4c pattern api */
int maxim4c_pattern_init(maxim4c_t *maxim4c);
int maxim4c_pattern_enable(maxim4c_t *maxim4c, bool enable);
#endif /* __MAXIM4C_API_H__ */

View File

@@ -0,0 +1,698 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Maxim Quad GMSL2/GMSL1 to CSI-2 Deserializer driver
*
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
*
* Author: Cai Wenzhong <cwz@rock-chips.com>
*
* V2.0.00 maxim serdes qual GMSL2/GMSL1 driver framework.
* 1. local deserializer support: max96712/max96722
* 2. remote serializer support: max9295/max96715/max96717
* 3. support deserializer and serializer auto adaptive
* 4. support deserializer output test pattern
* 5. support remote serializer I2c address mapping
* 6. support remote serializer hot plug detection and recovery
*
*/
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/compat.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/pm_runtime.h>
#include <linux/workqueue.h>
#include <linux/rk-camera-module.h>
#include <linux/gpio/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/regulator/consumer.h>
#include <media/media-entity.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#include "maxim4c_api.h"
#define DRIVER_VERSION KERNEL_VERSION(2, 0x00, 0x00)
#define MAXIM4C_XVCLK_FREQ 25000000
/* device compatible */
#define MAXIM4C_MAX96712_COMPAT "maxim4c,max96712"
#define MAXIM4C_MAX96722_COMPAT "maxim4c,max96722"
static int maxim4c_check_local_chipid(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
int ret = 0, loop = 0;
u8 chipid = 0;
for (loop = 0; loop < 5; loop++) {
ret = maxim4c_i2c_read_byte(client,
MAXIM4C_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_16BITS,
&chipid);
if (ret == 0) {
if (chipid == maxim4c->chipid) {
if (chipid == MAX96712_CHIP_ID) {
dev_info(dev, "MAX96712 is Detected\n");
return 0;
}
if (chipid == MAX96722_CHIP_ID) {
dev_info(dev, "MAX96722 is Detected\n");
return 0;
}
} else {
dev_err(dev, "Unexpected maxim chipid(%02x)\n", chipid);
return -ENODEV;
}
}
dev_info(dev, "retry (%d) to check local chipid", loop + 1);
msleep(10);
}
dev_err(dev, "maxim check chipid error, ret(%d)\n", ret);
return -ENODEV;
}
static irqreturn_t maxim4c_hot_plug_detect_irq_handler(int irq, void *dev_id)
{
struct maxim4c *maxim4c = dev_id;
struct device *dev = &maxim4c->client->dev;
int lock_gpio_level = 0;
mutex_lock(&maxim4c->mutex);
if (maxim4c->streaming) {
lock_gpio_level = gpiod_get_value_cansleep(maxim4c->lock_gpio);
if (lock_gpio_level == 0) {
dev_info(dev, "serializer hot plug out\n");
maxim4c->hot_plug_state = MAXIM4C_HOT_PLUG_OUT;
} else {
dev_info(dev, "serializer hot plug in\n");
maxim4c->hot_plug_state = MAXIM4C_HOT_PLUG_IN;
}
queue_delayed_work(maxim4c->hot_plug_work.state_check_wq,
&maxim4c->hot_plug_work.state_d_work,
msecs_to_jiffies(50));
}
mutex_unlock(&maxim4c->mutex);
return IRQ_HANDLED;
}
static void maxim4c_lock_irq_init(maxim4c_t *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
int ret = 0;
if (!IS_ERR(maxim4c->lock_gpio)) {
maxim4c->hot_plug_irq = gpiod_to_irq(maxim4c->lock_gpio);
if (maxim4c->hot_plug_irq < 0) {
dev_err(dev, "failed to get hot plug irq\n");
} else {
ret = devm_request_threaded_irq(dev,
maxim4c->hot_plug_irq,
NULL,
maxim4c_hot_plug_detect_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"maxim4c_hot_plug",
maxim4c);
if (ret) {
dev_err(dev, "failed to request hot plug irq (%d)\n", ret);
maxim4c->hot_plug_irq = -1;
} else {
disable_irq(maxim4c->hot_plug_irq);
}
}
}
}
static void maxim4c_hot_plug_state_check_work(struct work_struct *work)
{
struct maxim4c_hot_plug_work *hot_plug_work =
container_of(work, struct maxim4c_hot_plug_work, state_d_work.work);
struct maxim4c *maxim4c =
container_of(hot_plug_work, struct maxim4c, hot_plug_work);
struct device *dev = &maxim4c->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(&maxim4c->mutex);
if (maxim4c->streaming == 0) {
mutex_unlock(&maxim4c->mutex);
return;
}
link_enable_mask = maxim4c->gmsl_link.link_enable_mask;
last_lock_state = maxim4c->link_lock_state;
if ((maxim4c->hot_plug_state == MAXIM4C_HOT_PLUG_OUT)
&& (last_lock_state == link_enable_mask)) {
maxim4c_link_select_remote_control(maxim4c, 0);
}
curr_lock_state = maxim4c_link_get_lock_state(maxim4c, 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);
maxim4c->link_lock_state = curr_lock_state;
}
if (link_lock_change & MAXIM4C_LINK_MASK_A) {
link_id = MAXIM4C_LINK_ID_A;
if (curr_lock_state & MAXIM4C_LINK_MASK_A) {
dev_info(dev, "link A plug in\n");
maxim4c_remote_devices_init(maxim4c, MAXIM4C_LINK_MASK_A);
maxim4c_video_pipe_linkid_enable(maxim4c, link_id, true);
} else {
dev_info(dev, "link A plug out\n");
maxim4c_video_pipe_linkid_enable(maxim4c, link_id, false);
}
}
if (link_lock_change & MAXIM4C_LINK_MASK_B) {
link_id = MAXIM4C_LINK_ID_B;
if (curr_lock_state & MAXIM4C_LINK_MASK_B) {
dev_info(dev, "link B plug in\n");
maxim4c_remote_devices_init(maxim4c, MAXIM4C_LINK_MASK_B);
maxim4c_video_pipe_linkid_enable(maxim4c, link_id, true);
} else {
dev_info(dev, "link B plug out\n");
maxim4c_video_pipe_linkid_enable(maxim4c, link_id, false);
}
}
if (link_lock_change & MAXIM4C_LINK_MASK_C) {
link_id = MAXIM4C_LINK_ID_C;
if (curr_lock_state & MAXIM4C_LINK_MASK_C) {
dev_info(dev, "link C plug in\n");
maxim4c_remote_devices_init(maxim4c, MAXIM4C_LINK_MASK_C);
maxim4c_video_pipe_linkid_enable(maxim4c, link_id, true);
} else {
dev_info(dev, "link C plug out\n");
maxim4c_video_pipe_linkid_enable(maxim4c, link_id, false);
}
}
if (link_lock_change & MAXIM4C_LINK_MASK_D) {
link_id = MAXIM4C_LINK_ID_D;
if (curr_lock_state & MAXIM4C_LINK_MASK_D) {
dev_info(dev, "link D plug in\n");
maxim4c_remote_devices_init(maxim4c, MAXIM4C_LINK_MASK_D);
maxim4c_video_pipe_linkid_enable(maxim4c, link_id, true);
} else {
dev_info(dev, "link D plug out\n");
maxim4c_video_pipe_linkid_enable(maxim4c, link_id, false);
}
}
if (curr_lock_state == link_enable_mask) {
// remote control mask enable
maxim4c_link_select_remote_control(maxim4c, link_enable_mask);
} else {
queue_delayed_work(maxim4c->hot_plug_work.state_check_wq,
&maxim4c->hot_plug_work.state_d_work,
msecs_to_jiffies(100));
}
mutex_unlock(&maxim4c->mutex);
}
static int maxim4c_lock_state_work_init(maxim4c_t *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
INIT_DELAYED_WORK(&maxim4c->hot_plug_work.state_d_work,
maxim4c_hot_plug_state_check_work);
maxim4c->hot_plug_work.state_check_wq =
create_singlethread_workqueue("maxim4c work queue");
if (maxim4c->hot_plug_work.state_check_wq == NULL) {
dev_err(dev, "failed to create hot plug work queue\n");
return -ENOMEM;
}
return 0;
}
static int maxim4c_lock_state_work_deinit(maxim4c_t *maxim4c)
{
if (maxim4c->hot_plug_work.state_check_wq) {
cancel_delayed_work_sync(&maxim4c->hot_plug_work.state_d_work);
destroy_workqueue(maxim4c->hot_plug_work.state_check_wq);
maxim4c->hot_plug_work.state_check_wq = NULL;
}
return 0;
}
/* Calculate the delay in us by clock rate and clock cycles */
static inline u32 maxim4c_cal_delay(u32 cycles)
{
return DIV_ROUND_UP(cycles, MAXIM4C_XVCLK_FREQ / 1000 / 1000);
}
static int maxim4c_local_device_power_on(maxim4c_t *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
if (!IS_ERR(maxim4c->power_gpio)) {
dev_info(dev, "local device power gpio on\n");
gpiod_set_value_cansleep(maxim4c->power_gpio, 1);
usleep_range(5000, 10000);
}
return 0;
}
static void maxim4c_local_device_power_off(maxim4c_t *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
if (!IS_ERR(maxim4c->power_gpio)) {
dev_info(dev, "local device power gpio off\n");
gpiod_set_value_cansleep(maxim4c->power_gpio, 0);
}
}
static int maxim4c_remote_device_power_on(maxim4c_t *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
// remote PoC enable
if (!IS_ERR(maxim4c->pocen_gpio)) {
dev_info(dev, "remote device poc gpio on\n");
gpiod_set_value_cansleep(maxim4c->pocen_gpio, 1);
usleep_range(5000, 10000);
}
return 0;
}
static int maxim4c_remote_device_power_off(maxim4c_t *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
// remote PoC enable
if (!IS_ERR(maxim4c->pocen_gpio)) {
dev_info(dev, "remote device poc gpio off\n");
gpiod_set_value_cansleep(maxim4c->pocen_gpio, 0);
}
return 0;
}
static int maxim4c_runtime_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
return maxim4c_remote_device_power_on(maxim4c);
}
static int maxim4c_runtime_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
return maxim4c_remote_device_power_off(maxim4c);
}
static const struct dev_pm_ops maxim4c_pm_ops = {
SET_RUNTIME_PM_OPS(
maxim4c_runtime_suspend, maxim4c_runtime_resume, NULL)
};
static void maxim4c_module_data_init(maxim4c_t *maxim4c)
{
maxim4c_link_data_init(maxim4c);
maxim4c_video_pipe_data_init(maxim4c);
maxim4c_mipi_txphy_data_init(maxim4c);
}
static int maxim4c_extra_init_seq_parse(maxim4c_t *maxim4c, struct device_node *node)
{
struct device *dev = &maxim4c->client->dev;
struct device_node *init_seq_node = NULL;
struct maxim4c_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)) {
if (!of_device_is_available(init_seq_node)) {
dev_info(dev, "%pOF is disabled\n", init_seq_node);
return 0;
}
dev_info(dev, "load extra-init-sequence\n");
init_seq = &maxim4c->extra_init_seq;
maxim4c_i2c_load_init_seq(dev,
init_seq_node, init_seq);
of_node_put(init_seq_node);
}
return 0;
}
static int maxim4c_module_parse_dt(maxim4c_t *maxim4c)
{
struct device *dev = &maxim4c->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))
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 */
maxim4c_link_parse_dt(maxim4c, node);
/* video pipe parse dt */
maxim4c_video_pipe_parse_dt(maxim4c, node);
/* mipi txphy parse dt */
maxim4c_mipi_txphy_parse_dt(maxim4c, node);
/* extra init seq parse dt */
maxim4c_extra_init_seq_parse(maxim4c, node);
of_node_put(node);
return 0;
}
static int maxim4c_run_extra_init_seq(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
int ret = 0;
ret = maxim4c_i2c_run_init_seq(client,
&maxim4c->extra_init_seq);
if (ret) {
dev_err(dev, "extra init sequence error\n");
return ret;
}
return 0;
}
static int maxim4c_module_hw_previnit(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
int ret = 0;
// All links disable at beginning.
ret = maxim4c_i2c_write_byte(client,
0x0006, MAXIM4C_I2C_REG_ADDR_16BITS,
0xF0);
if (ret)
return ret;
// MIPI CSI output disable.
ret = maxim4c_i2c_write_byte(client,
0x040B, MAXIM4C_I2C_REG_ADDR_16BITS,
0x00);
if (ret)
return ret;
// MIPI TXPHY standby
ret = maxim4c_i2c_update_byte(client,
0x08A2, MAXIM4C_I2C_REG_ADDR_16BITS,
0xF0, 0x00);
if (ret)
return ret;
return 0;
}
static int maxim4c_module_hw_postinit(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
int ret = 0;
// video pipe disable all
ret |= maxim4c_i2c_write_byte(client,
0x00F4, MAXIM4C_I2C_REG_ADDR_16BITS,
0);
// remote control disable all
ret |= maxim4c_link_select_remote_control(maxim4c, 0);
return ret;
}
static int maxim4c_module_hw_init(maxim4c_t *maxim4c)
{
int ret = 0;
ret = maxim4c_module_hw_previnit(maxim4c);
if (ret)
return ret;
ret = maxim4c_link_hw_init(maxim4c);
if (ret)
return ret;
ret = maxim4c_video_pipe_hw_init(maxim4c);
if (ret)
return ret;
ret = maxim4c_mipi_txphy_hw_init(maxim4c);
if (ret)
return ret;
ret = maxim4c_run_extra_init_seq(maxim4c);
if (ret)
return ret;
ret = maxim4c_module_hw_postinit(maxim4c);
if (ret)
return ret;
return 0;
}
static int maxim4c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device_node *node = dev->of_node;
maxim4c_t *maxim4c = NULL;
const u32 *chip_id = NULL;
int ret = 0;
dev_info(dev, "driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16,
(DRIVER_VERSION & 0xff00) >> 8, DRIVER_VERSION & 0x00ff);
chip_id = of_device_get_match_data(dev);
if (chip_id == NULL) {
dev_err(dev, "maxim4c driver get match data error\n");
return -EINVAL;
}
if (*chip_id == MAX96712_CHIP_ID) {
dev_info(dev, "maxim4c driver for max96712");
} else if (*chip_id == MAX96722_CHIP_ID) {
dev_info(dev, "maxim4c driver for max96722");
} else {
dev_err(dev, "maxim4c driver unknown chip");
return -EINVAL;
}
maxim4c = devm_kzalloc(dev, sizeof(*maxim4c), GFP_KERNEL);
if (!maxim4c)
return -ENOMEM;
maxim4c->client = client;
maxim4c->chipid = *chip_id;
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
&maxim4c->module_index);
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
&maxim4c->module_facing);
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
&maxim4c->module_name);
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
&maxim4c->len_name);
if (ret) {
dev_err(dev, "could not get module information!\n");
return -EINVAL;
}
maxim4c->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW);
if (IS_ERR(maxim4c->power_gpio))
dev_warn(dev, "Failed to get power-gpios, maybe no use\n");
maxim4c->pocen_gpio = devm_gpiod_get(dev, "pocen", GPIOD_OUT_LOW);
if (IS_ERR(maxim4c->pocen_gpio))
dev_warn(dev, "Failed to get pocen-gpios\n");
maxim4c->lock_gpio = devm_gpiod_get(dev, "lock", GPIOD_IN);
if (IS_ERR(maxim4c->lock_gpio))
dev_warn(dev, "Failed to get lock-gpios\n");
mutex_init(&maxim4c->mutex);
ret = maxim4c_local_device_power_on(maxim4c);
if (ret)
goto err_destroy_mutex;
ret = maxim4c_check_local_chipid(maxim4c);
if (ret)
goto err_power_off;
// client->dev->driver_data = subdev
// subdev->dev->driver_data = maxim4c
ret = maxim4c_v4l2_subdev_init(maxim4c);
if (ret)
goto err_power_off;
#if MAXIM4C_TEST_PATTERN
ret = maxim4c_pattern_init(maxim4c);
if (ret)
goto err_power_off;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_idle(dev);
return 0;
#endif /* MAXIM4C_TEST_PATTERN */
maxim4c_module_data_init(maxim4c);
maxim4c_module_parse_dt(maxim4c);
ret = maxim4c_module_hw_init(maxim4c);
if (ret)
goto err_subdev_deinit;
ret = maxim4c_remote_mfd_add_devices(maxim4c);
if (ret)
goto err_subdev_deinit;
maxim4c_lock_irq_init(maxim4c);
maxim4c_lock_state_work_init(maxim4c);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_idle(dev);
return 0;
err_subdev_deinit:
maxim4c_v4l2_subdev_deinit(maxim4c);
err_power_off:
maxim4c_local_device_power_off(maxim4c);
err_destroy_mutex:
mutex_destroy(&maxim4c->mutex);
return ret;
}
static int maxim4c_remove(struct i2c_client *client)
{
maxim4c_t *maxim4c = i2c_get_clientdata(client);
maxim4c_lock_state_work_deinit(maxim4c);
maxim4c_v4l2_subdev_deinit(maxim4c);
mutex_destroy(&maxim4c->mutex);
pm_runtime_disable(&client->dev);
if (!pm_runtime_status_suspended(&client->dev))
maxim4c_local_device_power_off(maxim4c);
pm_runtime_set_suspended(&client->dev);
return 0;
}
#if IS_ENABLED(CONFIG_OF)
static const u32 max96712_chip_id = MAX96712_CHIP_ID;
static const u32 max96722_chip_id = MAX96722_CHIP_ID;
static const struct of_device_id maxim4c_of_match[] = {
{
.compatible = MAXIM4C_MAX96712_COMPAT,
.data = &max96712_chip_id,
}, {
.compatible = MAXIM4C_MAX96722_COMPAT,
.data = &max96722_chip_id,
},
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, maxim4c_of_match);
#endif
static const struct i2c_device_id maxim4c_match_id[] = {
{ MAXIM4C_MAX96712_COMPAT, 0 },
{ MAXIM4C_MAX96722_COMPAT, 0 },
{},
};
static struct i2c_driver maxim4c_i2c_driver = {
.driver = {
.name = MAXIM4C_NAME,
.pm = &maxim4c_pm_ops,
.of_match_table = of_match_ptr(maxim4c_of_match),
},
.probe = &maxim4c_probe,
.remove = &maxim4c_remove,
.id_table = maxim4c_match_id,
};
module_i2c_driver(maxim4c_i2c_driver);
MODULE_AUTHOR("Cai wenzhong <cwz@rock-chips.com>");
MODULE_DESCRIPTION("Maxim qual gmsl deserializer driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,107 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023 Rockchip Electronics Co., Ltd.
*
*/
#ifndef __MAXIM4C_DRV_H__
#define __MAXIM4C_DRV_H__
#include <linux/workqueue.h>
#include <linux/rk-camera-module.h>
#include <media/media-entity.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#include "maxim4c_i2c.h"
#include "maxim4c_link.h"
#include "maxim4c_mipi_txphy.h"
#include "maxim4c_remote.h"
#include "maxim4c_pattern.h"
#define MAXIM4C_REG_CHIP_ID 0x0D
#define MAX96712_CHIP_ID 0xA0
#define MAX96722_CHIP_ID 0xA1
enum {
MAXIM4C_HOT_PLUG_OUT = 0,
MAXIM4C_HOT_PLUG_IN,
};
struct maxim4c_hot_plug_work {
struct workqueue_struct *state_check_wq;
struct delayed_work state_d_work;
u32 hot_plug_state;
};
struct maxim4c_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 maxim4c {
struct i2c_client *client;
struct clk *xvclk;
struct gpio_desc *power_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 maxim4c_hot_plug_work hot_plug_work;
struct maxim4c_mode supported_mode;
const struct maxim4c_mode *cur_mode;
u32 cfg_modes_num;
u32 module_index;
const char *module_facing;
const char *module_name;
const char *len_name;
maxim4c_gmsl_link_t gmsl_link;
maxim4c_video_pipe_t video_pipe;
maxim4c_mipi_txphy_t mipi_txphy;
struct maxim4c_pattern pattern;
struct maxim4c_i2c_init_seq extra_init_seq;
maxim4c_remote_t *remote_device[MAXIM4C_LINK_ID_MAX];
} maxim4c_t;
#endif /* __MAXIM4C_DRV_H__ */

View File

@@ -0,0 +1,407 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Maxim Quad GMSL Deserializer I2C read/write driver
*
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
*
* Author: Cai Wenzhong <cwz@rock-chips.com>
*
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include "maxim4c_i2c.h"
/* Write registers up to 4 at a time */
int maxim4c_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(maxim4c_i2c_write_reg);
/* Read registers up to 4 at a time */
int maxim4c_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 *)&reg_addr_be;
/* Write register address */
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = reg_len;
msgs[0].buf = &reg_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%x from 0x%x 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(maxim4c_i2c_read_reg);
/* Update registers up to 4 at a time */
int maxim4c_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 = maxim4c_i2c_read_reg(client, reg_addr, reg_len, val_len, &value);
if (ret)
return ret;
value &= ~val_mask;
value |= (reg_val & val_mask);
ret = maxim4c_i2c_write_reg(client, reg_addr, reg_len, val_len, value);
return ret;
}
EXPORT_SYMBOL(maxim4c_i2c_update_reg);
int maxim4c_i2c_write_byte(struct i2c_client *client,
u16 reg_addr, u16 reg_len, u8 reg_val)
{
int ret = 0;
ret = maxim4c_i2c_write_reg(client,
reg_addr, reg_len,
MAXIM4C_I2C_REG_VALUE_08BITS, reg_val);
return ret;
}
EXPORT_SYMBOL(maxim4c_i2c_write_byte);
int maxim4c_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 = maxim4c_i2c_read_reg(client,
reg_addr, reg_len,
MAXIM4C_I2C_REG_VALUE_08BITS, &value);
*reg_val = *value_be_p;
return ret;
}
EXPORT_SYMBOL(maxim4c_i2c_read_byte);
int maxim4c_i2c_update_byte(struct i2c_client *client,
u16 reg_addr, u16 reg_len, u8 val_mask, u8 reg_val)
{
u8 value;
int ret;
ret = maxim4c_i2c_read_byte(client, reg_addr, reg_len, &value);
if (ret)
return ret;
value &= ~val_mask;
value |= (reg_val & val_mask);
ret = maxim4c_i2c_write_byte(client, reg_addr, reg_len, value);
return ret;
}
EXPORT_SYMBOL(maxim4c_i2c_update_byte);
int maxim4c_i2c_write_array(struct i2c_client *client,
const struct maxim4c_i2c_regval *regs)
{
u32 i = 0;
int ret = 0;
for (i = 0; (ret == 0) && (regs[i].reg_addr != MAXIM4C_REG_NULL); i++) {
if (regs[i].val_mask != 0)
ret = maxim4c_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 = maxim4c_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(maxim4c_i2c_write_array);
static int maxim4c_i2c_parse_init_seq(struct device *dev,
const u8 *seq_data, int data_len, struct maxim4c_i2c_init_seq *init_seq)
{
struct maxim4c_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 maxim4c_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 = MAXIM4C_REG_NULL;
return 0;
}
int maxim4c_i2c_load_init_seq(struct device *dev,
struct device_node *node, struct maxim4c_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 = maxim4c_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(maxim4c_i2c_load_init_seq);
int maxim4c_i2c_run_init_seq(struct i2c_client *client,
struct maxim4c_i2c_init_seq *init_seq)
{
int ret = 0;
if (init_seq == NULL || init_seq->reg_init_seq == NULL)
return 0;
ret = maxim4c_i2c_write_array(client,
init_seq->reg_init_seq);
return ret;
}
EXPORT_SYMBOL(maxim4c_i2c_run_init_seq);

View File

@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023 Rockchip Electronics Co., Ltd.
*
*/
#ifndef __MAXIM4C_I2C_H__
#define __MAXIM4C_I2C_H__
#include <linux/i2c.h>
/* register address: 8bit or 16bit */
#define MAXIM4C_I2C_REG_ADDR_08BITS 1
#define MAXIM4C_I2C_REG_ADDR_16BITS 2
/* register value: 8bit or 16bit or 24bit */
#define MAXIM4C_I2C_REG_VALUE_08BITS 1
#define MAXIM4C_I2C_REG_VALUE_16BITS 2
#define MAXIM4C_I2C_REG_VALUE_24BITS 3
/* I2C Device ID */
enum {
MAXIM4C_I2C_DES_DEF, /* Deserializer I2C address: Default */
MAXIM4C_I2C_SER_DEF, /* Serializer I2C address: Default */
MAXIM4C_I2C_SER_MAP, /* Serializer I2C address: Mapping */
MAXIM4C_I2C_CAM_DEF, /* Camera I2C address: Default */
MAXIM4C_I2C_CAM_MAP, /* Camera I2C address: Mapping */
MAXIM4C_I2C_DEV_MAX,
};
/* i2c register array end */
#define MAXIM4C_REG_NULL 0xFFFF
struct maxim4c_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 maxim4c_i2c_init_seq {
struct maxim4c_i2c_regval *reg_init_seq;
u32 reg_seq_size;
u32 seq_item_size;
u32 reg_len;
u32 val_len;
};
#endif /* __MAXIM4C_I2C_H__ */

View File

@@ -0,0 +1,753 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Maxim Quad GMSL Deserializer Link driver
*
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
*
* Author: Cai Wenzhong <cwz@rock-chips.com>
*
*/
#include <linux/delay.h>
#include "maxim4c_api.h"
static int maxim4c_link_enable_vdd_ldo1(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
int ret = 0;
/* IF VDD = 1.2V: Enable REG_ENABLE and REG_MNL
* CTRL0: Enable REG_ENABLE
* CTRL2: Enable REG_MNL
*/
ret |= maxim4c_i2c_update_byte(client,
0x0017, MAXIM4C_I2C_REG_ADDR_16BITS, BIT(2), BIT(2));
ret |= maxim4c_i2c_update_byte(client,
0x0019, MAXIM4C_I2C_REG_ADDR_16BITS, BIT(4), BIT(4));
return ret;
}
static int maxim4c_link_enable_vdd_ldo2(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
int ret = 0;
ret |= maxim4c_i2c_write_byte(client,
0x06C2, MAXIM4C_I2C_REG_ADDR_16BITS,
0x10);
// delay 10ms
msleep(10);
return ret;
}
static int maxim4c_link_set_rate(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
struct maxim4c_link_cfg *link_cfg = NULL;
u8 link_rate = 0;
int ret = 0;
/* Link A/B rate setting */
link_rate = 0; /* default Transmitter Rate is 187.5Mbps */
link_cfg = &gmsl_link->link_cfg[MAXIM4C_LINK_ID_A];
if (link_cfg->link_enable) {
/* Link A: Receiver Rate */
if (link_cfg->link_rx_rate == MAXIM4C_LINK_RX_RATE_3GBPS)
link_rate |= (0x1 << 0);
else
link_rate |= (0x2 << 0);
}
link_cfg = &gmsl_link->link_cfg[MAXIM4C_LINK_ID_B];
if (link_cfg->link_enable) {
/* Link B: Receiver Rate */
if (link_cfg->link_rx_rate == MAXIM4C_LINK_RX_RATE_3GBPS)
link_rate |= (0x1 << 4);
else
link_rate |= (0x2 << 4);
}
if (link_rate != 0) {
ret |= maxim4c_i2c_write_byte(client,
0x0010, MAXIM4C_I2C_REG_ADDR_16BITS,
link_rate);
}
/* Link C/D rate setting */
link_rate = 0;
link_cfg = &gmsl_link->link_cfg[MAXIM4C_LINK_ID_C];
if (link_cfg->link_enable) {
/* Link C: Receiver Rate */
if (link_cfg->link_rx_rate == MAXIM4C_LINK_RX_RATE_3GBPS)
link_rate |= (0x1 << 0);
else
link_rate |= (0x2 << 0);
}
link_cfg = &gmsl_link->link_cfg[MAXIM4C_LINK_ID_D];
if (link_cfg->link_enable) {
/* Link D: Receiver Rate */
if (link_cfg->link_rx_rate == MAXIM4C_LINK_RX_RATE_3GBPS)
link_rate |= (0x1 << 4);
else
link_rate |= (0x2 << 4);
}
if (link_rate != 0) {
ret |= maxim4c_i2c_write_byte(client,
0x0011, MAXIM4C_I2C_REG_ADDR_16BITS,
link_rate);
}
return ret;
}
static int maxim4c_link_run_init_seq(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
struct maxim4c_link_cfg *link_cfg = NULL;
struct maxim4c_i2c_init_seq *init_seq = NULL;
int i = 0;
int ret = 0;
// link init sequence
for (i = 0; i < MAXIM4C_LINK_ID_MAX; i++) {
link_cfg = &gmsl_link->link_cfg[i];
init_seq = &link_cfg->link_init_seq;
ret = maxim4c_i2c_run_init_seq(client, init_seq);
if (ret) {
dev_err(dev, "link id = %d init sequence error\n", i);
return ret;
}
}
return 0;
}
static int maxim4c_link_status_init(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
struct maxim4c_link_cfg *link_cfg = NULL;
u8 link_type = 0, link_enable = 0;
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 = 0x0F;
gmsl_link->link_locked_mask = 0;
link_type = 0xF0; /* default GMSL2 */
link_enable = 0; /* default disable */
for (link_idx = 0; link_idx < MAXIM4C_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 == MAXIM4C_GMSL1) {
gmsl_link->link_type_mask &= ~BIT(link_idx);
link_type &= ~BIT(4 + link_idx);
}
}
}
ret = maxim4c_i2c_write_byte(client,
0x0006, MAXIM4C_I2C_REG_ADDR_16BITS,
link_type | link_enable);
reg_mask = BIT(1) | BIT(0);
reg_value = 0;
for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
reg_addr = 0x0B04 + 0x100 * link_idx;
ret |= maxim4c_i2c_update_byte(client,
reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
}
if (gmsl_link->i2c_ctrl_port == MAXIM4C_I2C_PORT2) {
reg_mask = 0x0F;
reg_value = 0x0F;
ret |= maxim4c_i2c_update_byte(client,
0x000E, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
}
reg_mask = 0xFF;
reg_value = 0xFF;
ret |= maxim4c_i2c_update_byte(client,
0x0003, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
return ret;
}
u8 maxim4c_link_get_lock_state(maxim4c_t *maxim4c, u8 link_mask)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
maxim4c_gmsl_link_t *gmsl_link = &maxim4c->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 & MAXIM4C_LINK_MASK_A) {
link_type = gmsl_link->link_cfg[MAXIM4C_LINK_ID_A].link_type;
if (link_type == MAXIM4C_GMSL2) {
// GMSL2 Link A
maxim4c_i2c_read_byte(client,
0x001A, MAXIM4C_I2C_REG_ADDR_16BITS,
&link_lock);
if (link_lock & BIT(3)) {
lock_state |= MAXIM4C_LINK_MASK_A;
dev_dbg(dev, "GMSL2 Link A locked\n");
}
} else {
// GMSL1 Link A
maxim4c_i2c_read_byte(client,
0x0BCB, MAXIM4C_I2C_REG_ADDR_16BITS,
&link_lock);
if (link_lock & BIT(0)) {
lock_state |= MAXIM4C_LINK_MASK_A;
dev_dbg(dev, "GMSL1 Link A locked\n");
}
}
// record link lock
if (lock_state & MAXIM4C_LINK_MASK_A)
gmsl_link->link_locked_mask |= MAXIM4C_LINK_MASK_A;
else
gmsl_link->link_locked_mask &= ~MAXIM4C_LINK_MASK_A;
}
// Link B
if (link_mask & MAXIM4C_LINK_MASK_B) {
link_type = gmsl_link->link_cfg[MAXIM4C_LINK_ID_B].link_type;
if (link_type == MAXIM4C_GMSL2) {
// GMSL2 Link B
maxim4c_i2c_read_byte(client,
0x000A, MAXIM4C_I2C_REG_ADDR_16BITS,
&link_lock);
if (link_lock & BIT(3)) {
lock_state |= MAXIM4C_LINK_MASK_B;
dev_dbg(dev, "GMSL2 Link B locked\n");
}
} else {
// GMSL1 Link B
maxim4c_i2c_read_byte(client,
0x0CCB, MAXIM4C_I2C_REG_ADDR_16BITS,
&link_lock);
if (link_lock & BIT(0)) {
lock_state |= MAXIM4C_LINK_MASK_B;
dev_dbg(dev, "GMSL1 Link B locked\n");
}
}
// record link lock
if (lock_state & MAXIM4C_LINK_MASK_B)
gmsl_link->link_locked_mask |= MAXIM4C_LINK_MASK_B;
else
gmsl_link->link_locked_mask &= ~MAXIM4C_LINK_MASK_B;
}
// Link C
if (link_mask & MAXIM4C_LINK_MASK_C) {
link_type = gmsl_link->link_cfg[MAXIM4C_LINK_ID_C].link_type;
if (link_type == MAXIM4C_GMSL2) {
// GMSL2 Link C
maxim4c_i2c_read_byte(client,
0x000B, MAXIM4C_I2C_REG_ADDR_16BITS,
&link_lock);
if (link_lock & BIT(3)) {
lock_state |= MAXIM4C_LINK_MASK_C;
dev_dbg(dev, "GMSL2 Link C locked\n");
}
} else {
// GMSL1 Link C
maxim4c_i2c_read_byte(client,
0x0DCB, MAXIM4C_I2C_REG_ADDR_16BITS,
&link_lock);
if (link_lock & BIT(0)) {
lock_state |= MAXIM4C_LINK_MASK_C;
dev_dbg(dev, "GMSL1 Link C locked\n");
}
}
// record link lock
if (lock_state & MAXIM4C_LINK_MASK_C)
gmsl_link->link_locked_mask |= MAXIM4C_LINK_MASK_C;
else
gmsl_link->link_locked_mask &= ~MAXIM4C_LINK_MASK_C;
}
// Link D
if (link_mask & MAXIM4C_LINK_MASK_D) {
link_type = gmsl_link->link_cfg[MAXIM4C_LINK_ID_D].link_type;
if (link_type == MAXIM4C_GMSL2) {
// GMSL2 Link D
maxim4c_i2c_read_byte(client,
0x000C, MAXIM4C_I2C_REG_ADDR_16BITS,
&link_lock);
if (link_lock & BIT(3)) {
lock_state |= MAXIM4C_LINK_MASK_D;
dev_dbg(dev, "GMSL2 Link D locked\n");
}
} else {
// GMSL1 Link D
maxim4c_i2c_read_byte(client,
0x0ECB, MAXIM4C_I2C_REG_ADDR_16BITS,
&link_lock);
if (link_lock & BIT(0)) {
lock_state |= MAXIM4C_LINK_MASK_D;
dev_dbg(dev, "GMSL1 Link D locked\n");
}
}
// record link lock
if (lock_state & MAXIM4C_LINK_MASK_D)
gmsl_link->link_locked_mask |= MAXIM4C_LINK_MASK_D;
else
gmsl_link->link_locked_mask &= ~MAXIM4C_LINK_MASK_D;
}
return lock_state;
}
EXPORT_SYMBOL(maxim4c_link_get_lock_state);
int maxim4c_link_oneshot_reset(struct maxim4c *maxim4c, u8 link_mask)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
struct maxim4c_link_cfg *link_cfg = NULL;
u8 oneshot_reset = 0;
int ret = 0, link_idx = 0;
dev_dbg(dev, "%s, link_mask = 0x%x\n", __func__, link_mask);
oneshot_reset = 0;
for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
link_cfg = &gmsl_link->link_cfg[link_idx];
if (link_cfg->link_enable && (link_mask & BIT(link_idx)))
oneshot_reset |= BIT(link_idx);
}
if (oneshot_reset != 0) {
// One-Shot Reset
ret = maxim4c_i2c_write_byte(client,
0x0018, MAXIM4C_I2C_REG_ADDR_16BITS,
oneshot_reset);
}
return ret;
}
EXPORT_SYMBOL(maxim4c_link_oneshot_reset);
int maxim4c_link_mask_enable(struct maxim4c *maxim4c, u8 link_mask, bool enable)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
struct maxim4c_link_cfg *link_cfg = NULL;
u8 reg_mask = 0, reg_value = 0;
int ret = 0, link_idx = 0;
dev_dbg(dev, "%s, link_mask = 0x%x, enable = %d\n",
__func__, link_mask, enable);
reg_mask = 0;
for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
link_cfg = &gmsl_link->link_cfg[link_idx];
if (link_cfg->link_enable && (link_mask & BIT(link_idx)))
reg_mask |= BIT(link_idx);
}
if (reg_mask != 0) {
reg_value = enable ? reg_mask : 0;
ret = maxim4c_i2c_update_byte(client,
0x0006, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
}
return ret;
}
EXPORT_SYMBOL(maxim4c_link_mask_enable);
int maxim4c_link_wait_linklock(struct maxim4c *maxim4c, u8 link_mask)
{
struct i2c_client *client = maxim4c->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++) {
for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
link_bit_mask = BIT(link_idx);
if ((link_mask & link_bit_mask)
&& ((lock_state & link_bit_mask) == 0)) {
if (maxim4c_link_get_lock_state(maxim4c, 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);
maxim4c->link_lock_state = lock_state;
return 0;
}
msleep(10);
time_ms += 10;
}
if ((lock_state & link_mask) != 0) {
dev_info(dev, "Partial links are locked: 0x%x, time_ms = %d\n",
lock_state, time_ms);
maxim4c->link_lock_state = lock_state;
return 0;
} else {
dev_err(dev, "Failed to detect remote link, time_ms = %d!\n", time_ms);
maxim4c->link_lock_state = 0;
return -ENODEV;
}
}
EXPORT_SYMBOL(maxim4c_link_wait_linklock);
int maxim4c_link_select_remote_enable(struct maxim4c *maxim4c, u8 link_mask)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
u8 link_enable = 0, link_reset = 0, link_bit_mask = 0;
int ret = 0, link_idx = 0;
dev_dbg(dev, "%s, link_mask = 0x%x\n", __func__, link_mask);
ret = 0;
link_enable = 0;
link_reset = 0;
for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
link_bit_mask = BIT(link_idx);
if (link_mask & link_bit_mask) {
if (gmsl_link->link_cfg[link_idx].link_enable) {
link_enable |= BIT(link_idx);
link_reset |= BIT(link_idx);
} else {
link_mask &= ~link_bit_mask;
}
}
}
if (link_mask != 0) {
// One-Shot Reset
ret |= maxim4c_i2c_write_byte(client,
0x0018, MAXIM4C_I2C_REG_ADDR_16BITS,
link_reset);
// Link Enable
ret |= maxim4c_i2c_update_byte(client,
0x0006, MAXIM4C_I2C_REG_ADDR_16BITS,
link_enable, link_enable);
ret |= maxim4c_link_wait_linklock(maxim4c, link_mask);
}
return ret;
}
EXPORT_SYMBOL(maxim4c_link_select_remote_enable);
int maxim4c_link_select_remote_control(struct maxim4c *maxim4c, u8 link_mask)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
struct maxim4c_link_cfg *link_cfg = NULL;
u8 link_enable = 0, link_type = 0;
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);
// GMSL1 Link forward control channel
for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
link_cfg = &gmsl_link->link_cfg[link_idx];
link_enable = link_cfg->link_enable;
link_type = link_cfg->link_type;
if (link_enable && (link_type == MAXIM4C_GMSL1)) {
reg_mask = BIT(1) | BIT(0);
if (link_mask & BIT(link_idx))
// GMSL1: Enable forward control channel transmitter
reg_value = BIT(1) | BIT(0);
else
// GMSL1: Disable forward control channel transmitter
reg_value = 0;
reg_addr = 0x0B04 + 0x100 * link_idx;
ret |= maxim4c_i2c_update_byte(client,
reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
link_mask &= ~BIT(link_idx);
}
}
// GMSL2 Link
if (gmsl_link->i2c_ctrl_port == MAXIM4C_I2C_PORT2) {
reg_mask = 0x0F;
reg_value = ~link_mask;
ret |= maxim4c_i2c_update_byte(client,
0x000E, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
} else if (gmsl_link->i2c_ctrl_port == MAXIM4C_I2C_PORT1) {
reg_mask = 0xFF;
reg_value = 0;
for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
if (link_mask & BIT(link_idx))
reg_value |= BIT(1 + 2 * link_idx);
}
reg_value = ~reg_value;
ret |= maxim4c_i2c_update_byte(client,
0x0003, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
} else {
reg_mask = 0xFF;
reg_value = 0;
for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
if (link_mask & BIT(link_idx))
reg_value |= BIT(0 + 2 * link_idx);
}
reg_value = ~reg_value;
ret |= maxim4c_i2c_update_byte(client,
0x0003, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
}
return ret;
}
EXPORT_SYMBOL(maxim4c_link_select_remote_control);
static int maxim4c_gmsl_link_config_parse_dt(struct device *dev,
maxim4c_gmsl_link_t *gmsl_link,
struct device_node *parent_node)
{
struct device_node *node = NULL;
struct device_node *init_seq_node = NULL;
struct maxim4c_i2c_init_seq *init_seq = NULL;
struct maxim4c_link_cfg *link_cfg = NULL;
struct maxim4c_remote_info *remote_info;
const char *link_cfg_name = "gmsl-link-config";
const char *prop_str = NULL;
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 >= MAXIM4C_LINK_ID_MAX) {
dev_err(dev, "Too many matching %s node\n", 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 >= MAXIM4C_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;
}
/* remote info */
remote_info = &link_cfg->remote_info;
ret = of_property_read_string(node, "remote-name", &prop_str);
if (ret == 0) {
dev_info(dev, "remote-name property: %s", prop_str);
remote_info->remote_name = prop_str;
}
ret = of_property_read_string(node, "remote-compatible", &prop_str);
if (ret == 0) {
dev_info(dev, "remote-compatible property: %s", prop_str);
remote_info->remote_compatible = prop_str;
}
/* 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;
maxim4c_i2c_load_init_seq(dev,
init_seq_node, init_seq);
of_node_put(init_seq_node);
}
sub_idx++;
}
}
return 0;
}
int maxim4c_link_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node)
{
struct device *dev = &maxim4c->client->dev;
struct device_node *node = NULL;
maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
u32 value = 0;
int ret = 0;
dev_info(dev, "=== maxim4c link parse dt ===\n");
node = of_get_child_by_name(of_node, "gmsl-links");
if (IS_ERR_OR_NULL(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;
}
/* vdd ldo2 enable */
ret = of_property_read_u32(node, "link-vdd-ldo2-en", &value);
if (ret == 0) {
dev_info(dev, "link-vdd-ldo2-en property: %d\n", value);
gmsl_link->link_vdd_ldo2_en = value;
}
ret = maxim4c_gmsl_link_config_parse_dt(dev, gmsl_link, node);
of_node_put(node);
return ret;
}
EXPORT_SYMBOL(maxim4c_link_parse_dt);
int maxim4c_link_hw_init(maxim4c_t *maxim4c)
{
maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
int ret = 0;
// All links disable at beginning.
ret = maxim4c_link_status_init(maxim4c);
if (ret)
return ret;
if (gmsl_link->link_vdd_ldo1_en)
ret |= maxim4c_link_enable_vdd_ldo1(maxim4c);
if (gmsl_link->link_vdd_ldo2_en)
ret |= maxim4c_link_enable_vdd_ldo2(maxim4c);
// Link Rate Setting
ret |= maxim4c_link_set_rate(maxim4c);
if (ret)
return ret;
// link init sequence
ret = maxim4c_link_run_init_seq(maxim4c);
return ret;
}
EXPORT_SYMBOL(maxim4c_link_hw_init);
void maxim4c_link_data_init(maxim4c_t *maxim4c)
{
maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
struct maxim4c_link_cfg *link_cfg = NULL;
int i = 0;
gmsl_link->link_vdd_ldo1_en = 0;
gmsl_link->link_vdd_ldo2_en = 0;
gmsl_link->i2c_ctrl_port = MAXIM4C_I2C_PORT0;
for (i = 0; i < MAXIM4C_LINK_ID_MAX; i++) {
link_cfg = &gmsl_link->link_cfg[i];
link_cfg->link_enable = 0;
link_cfg->link_type = MAXIM4C_GMSL2;
if (maxim4c->chipid == MAX96722_CHIP_ID)
link_cfg->link_rx_rate = MAXIM4C_LINK_RX_RATE_3GBPS;
else
link_cfg->link_rx_rate = MAXIM4C_LINK_RX_RATE_6GBPS;
link_cfg->link_tx_rate = MAXIM4C_LINK_TX_RATE_187_5MPS;
link_cfg->remote_info.remote_name = NULL;
link_cfg->remote_info.remote_compatible = NULL;
link_cfg->link_init_seq.reg_init_seq = NULL;
}
}
EXPORT_SYMBOL(maxim4c_link_data_init);

View File

@@ -0,0 +1,92 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023 Rockchip Electronics Co., Ltd.
*
*/
#ifndef __MAXIM4C_LINK_H__
#define __MAXIM4C_LINK_H__
#include "maxim4c_i2c.h"
/* Link cable */
enum maxim4c_link_cable {
MAXIM4C_CABLE_COAX = 0,
MAXIM4C_CABLE_STP,
};
/* Link Type */
enum maxim4c_link_type {
MAXIM4C_GMSL1 = 0,
MAXIM4C_GMSL2,
};
/* Link Mode */
enum maxim4c_link_mode {
MAXIM4C_GMSL_PIXEL = 0,
MAXIM4C_GMSL_TUNNEL,
};
/* I2C Remote Control Port */
enum {
MAXIM4C_I2C_PORT0 = 0,
MAXIM4C_I2C_PORT1,
MAXIM4C_I2C_PORT2,
MAXIM4C_I2C_PORT_MAX,
};
/* Link SIO ID: 0 ~ 3 */
enum {
MAXIM4C_LINK_ID_A = 0,
MAXIM4C_LINK_ID_B,
MAXIM4C_LINK_ID_C,
MAXIM4C_LINK_ID_D,
MAXIM4C_LINK_ID_MAX,
};
/* Link Bit Mask: bit0 ~ bit3 */
#define MAXIM4C_LINK_MASK_A BIT(MAXIM4C_LINK_ID_A)
#define MAXIM4C_LINK_MASK_B BIT(MAXIM4C_LINK_ID_B)
#define MAXIM4C_LINK_MASK_C BIT(MAXIM4C_LINK_ID_C)
#define MAXIM4C_LINK_MASK_D BIT(MAXIM4C_LINK_ID_D)
#define MAXIM4C_LINK_MASK_ALL GENMASK(MAXIM4C_LINK_ID_D, MAXIM4C_LINK_ID_A)
/* Link Receiver Rate */
enum maxim4c_link_rx_rate {
MAXIM4C_LINK_RX_RATE_3GBPS = 0,
MAXIM4C_LINK_RX_RATE_6GBPS,
};
/* Link Transmitter Rate */
enum maxim4c_link_tx_rate {
MAXIM4C_LINK_TX_RATE_187_5MPS = 0,
};
struct maxim4c_remote_info {
const char *remote_name;
const char *remote_compatible;
};
struct maxim4c_link_cfg {
u8 link_enable;
u8 link_type;
u8 link_rx_rate;
u8 link_tx_rate;
struct maxim4c_remote_info remote_info;
struct maxim4c_i2c_init_seq link_init_seq;
};
typedef struct maxim4c_gmsl_link {
u8 link_enable_mask;
u8 link_type_mask;
u8 link_locked_mask;
u8 link_vdd_ldo1_en;
u8 link_vdd_ldo2_en;
u8 i2c_ctrl_port;
struct maxim4c_link_cfg link_cfg[MAXIM4C_LINK_ID_MAX];
} maxim4c_gmsl_link_t;
#endif /* __MAXIM4C_LINK_H__ */

View File

@@ -0,0 +1,528 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Maxim Quad GMSL Deserializer MIPI txphy driver
*
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
*
* Author: Cai Wenzhong <cwz@rock-chips.com>
*
*/
#include <linux/iopoll.h>
#include "maxim4c_api.h"
static int maxim4c_txphy_auto_init_deskew(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
struct maxim4c_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 < MAXIM4C_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 = 0x0903 + 0x40 * phy_idx;
ret |= maxim4c_i2c_write_byte(client,
reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
phy_cfg->auto_deskew);
}
}
return ret;
}
static int maxim4c_mipi_txphy_lane_mapping(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
struct maxim4c_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[MAXIM4C_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[MAXIM4C_TXPHY_ID_B];
if (phy_cfg->phy_enable) {
reg_mask |= 0xF0;
reg_value |= (phy_cfg->data_lane_map << 4);
}
if (reg_mask != 0) {
ret |= maxim4c_i2c_update_byte(client,
0x08A3, MAXIM4C_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[MAXIM4C_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[MAXIM4C_TXPHY_ID_D];
if (phy_cfg->phy_enable) {
reg_mask |= 0xF0;
reg_value |= (phy_cfg->data_lane_map << 4);
}
if (reg_mask != 0) {
ret |= maxim4c_i2c_update_byte(client,
0x08A4, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
}
return ret;
}
static int maxim4c_mipi_txphy_type_vcx_lane_num(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
struct maxim4c_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 < MAXIM4C_TXPHY_ID_MAX; phy_idx++) {
phy_cfg = &mipi_txphy->phy_cfg[phy_idx];
if (phy_cfg->phy_enable == 0)
continue;
reg_mask = 0xF0;
reg_value = 0;
if (phy_cfg->phy_type == MAXIM4C_TXPHY_TYPE_CPHY)
reg_value |= BIT(5);
if (phy_cfg->vc_ext_en)
reg_value |= BIT(4);
reg_value |= ((phy_cfg->data_lane_num - 1) << 6);
reg_addr = 0x090A + 0x40 * phy_idx;
ret |= maxim4c_i2c_update_byte(client,
reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
}
return ret;
}
int maxim4c_mipi_txphy_enable(maxim4c_t *maxim4c, bool enable)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->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 < MAXIM4C_TXPHY_ID_MAX; phy_idx++) {
if (mipi_txphy->phy_cfg[phy_idx].phy_enable)
reg_value |= BIT(4 + phy_idx);
}
}
ret |= maxim4c_i2c_update_byte(client,
0x08A2, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
return ret;
}
int maxim4c_dphy_dpll_predef_set(maxim4c_t *maxim4c, s64 link_freq_hz)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
struct maxim4c_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 < MAXIM4C_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 != MAXIM4C_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 |= maxim4c_i2c_write_byte(client,
reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
0xf4);
// Set dpll data rate
reg_addr = 0x0415 + 0x03 * phy_idx;
ret |= maxim4c_i2c_update_byte(client,
reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
0x3F, dpll_val);
// Release reset to DPLL (config_soft_rst_n = 1)
reg_addr = 0x1C00 + 0x100 * phy_idx;
ret |= maxim4c_i2c_write_byte(client,
reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
0xf5);
}
if (ret) {
dev_err(dev, "DPLL predef set error!\n");
return ret;
}
ret = read_poll_timeout(maxim4c_i2c_read_byte, ret,
!(ret < 0) && (dpll_lock & dpll_mask),
1000, 10000, false,
client,
0x0400, MAXIM4C_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;
}
}
EXPORT_SYMBOL(maxim4c_dphy_dpll_predef_set);
int maxim4c_mipi_csi_output(maxim4c_t *maxim4c, bool enable)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
u8 reg_mask = 0, reg_value = 0;
int ret = 0;
dev_dbg(dev, "%s: enable = %d\n", __func__, enable);
/* Bit1 of the register 0x040B: 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 |= maxim4c_i2c_update_byte(client,
0x040B, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
return ret;
}
EXPORT_SYMBOL(maxim4c_mipi_csi_output);
static int maxim4c_mipi_txphy_config_parse_dt(struct device *dev,
maxim4c_mipi_txphy_t *mipi_txphy,
struct device_node *parent_node)
{
struct device_node *node = NULL;
struct maxim4c_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 >= MAXIM4C_TXPHY_ID_MAX) {
dev_err(dev, "Too many matching %s node\n", 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 >= MAXIM4C_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, "clock-mode", &value);
if (ret == 0) {
dev_info(dev, "clock-mode property: %d", value);
phy_cfg->clock_mode = value;
}
sub_idx++;
}
}
return 0;
}
int maxim4c_mipi_txphy_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node)
{
struct device *dev = &maxim4c->client->dev;
struct device_node *node = NULL;
maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
u32 value = 0;
int ret = 0;
dev_info(dev, "=== maxim4c mipi txphy parse dt ===\n");
node = of_get_child_by_name(of_node, "mipi-txphys");
if (IS_ERR_OR_NULL(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 = of_property_read_u32(node, "phy-force-clk0-en", &value);
if (ret == 0) {
dev_info(dev, "phy-force-clk0-en property: %d\n", value);
mipi_txphy->force_clk0_en = value;
}
ret = of_property_read_u32(node, "phy-force-clk3-en", &value);
if (ret == 0) {
dev_info(dev, "phy-force-clk3-en property: %d\n", value);
mipi_txphy->force_clk3_en = value;
}
ret = maxim4c_mipi_txphy_config_parse_dt(dev, mipi_txphy, node);
of_node_put(node);
return ret;
}
EXPORT_SYMBOL(maxim4c_mipi_txphy_parse_dt);
int maxim4c_mipi_txphy_hw_init(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
struct maxim4c_txphy_cfg *phy_cfg = NULL;
u8 mode = 0;
int ret = 0, i = 0;
mode = 0;
switch (mipi_txphy->phy_mode) {
case MAXIM4C_TXPHY_MODE_4X2LANES:
mode |= BIT(0);
// clock master
for (i = 0; i < MAXIM4C_TXPHY_ID_MAX; i++) {
if (mipi_txphy->phy_cfg[i].phy_enable)
mipi_txphy->phy_cfg[i].clock_master = 1;
}
break;
case MAXIM4C_TXPHY_MODE_1X4LANES_2X2LANES:
mode |= BIT(3);
// MIPI master clock setting
if (mipi_txphy->force_clk0_en != 0)
mode |= BIT(5);
// clock master
phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_B];
if (phy_cfg->phy_enable)
phy_cfg->clock_master = 1;
phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_C];
if (phy_cfg->phy_enable)
phy_cfg->clock_master = 1;
phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_D];
if (phy_cfg->phy_enable)
phy_cfg->clock_master = 1;
break;
case MAXIM4C_TXPHY_MODE_2X2LANES_1X4LANES:
mode |= BIT(4);
// MIPI master clock setting
if (mipi_txphy->force_clk3_en != 0)
mode |= BIT(6);
// clock master
phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_A];
if (phy_cfg->phy_enable)
phy_cfg->clock_master = 1;
phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_B];
if (phy_cfg->phy_enable)
phy_cfg->clock_master = 1;
phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_C];
if (phy_cfg->phy_enable)
phy_cfg->clock_master = 1;
break;
case MAXIM4C_TXPHY_MODE_2X4LANES:
default:
mode |= BIT(2);
// MIPI master clock setting
if (mipi_txphy->force_clk0_en != 0)
mode |= BIT(5);
if (mipi_txphy->force_clk3_en != 0)
mode |= BIT(6);
// clock master
phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_B];
if (phy_cfg->phy_enable)
phy_cfg->clock_master = 1;
phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_C];
if (phy_cfg->phy_enable)
phy_cfg->clock_master = 1;
break;
}
// MIPI clocks running mode
if (mipi_txphy->force_clock_out_en != 0)
mode |= BIT(7);
// MIPI TXPHY Mode setting
ret |= maxim4c_i2c_write_byte(client,
0x08A0, MAXIM4C_I2C_REG_ADDR_16BITS,
mode);
// mipi txphy data lane mapping
ret |= maxim4c_mipi_txphy_lane_mapping(maxim4c);
// mipi txphy type, lane number, virtual channel extension
ret |= maxim4c_mipi_txphy_type_vcx_lane_num(maxim4c);
// mipi txphy auto init deskew
ret |= maxim4c_txphy_auto_init_deskew(maxim4c);
return ret;
}
EXPORT_SYMBOL(maxim4c_mipi_txphy_hw_init);
void maxim4c_mipi_txphy_data_init(maxim4c_t *maxim4c)
{
maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
struct maxim4c_txphy_cfg *phy_cfg = NULL;
int i = 0;
mipi_txphy->phy_mode = MAXIM4C_TXPHY_MODE_2X4LANES;
mipi_txphy->force_clock_out_en = 1;
mipi_txphy->force_clk0_en = 0;
mipi_txphy->force_clk3_en = 0;
for (i = 0; i < MAXIM4C_TXPHY_ID_MAX; i++) {
phy_cfg = &mipi_txphy->phy_cfg[i];
phy_cfg->phy_enable = 0;
phy_cfg->phy_type = MAXIM4C_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->clock_master = 0;
phy_cfg->clock_mode = MAXIM4C_TXPHY_DPLL_PREDEF;
}
}
EXPORT_SYMBOL(maxim4c_mipi_txphy_data_init);

View File

@@ -0,0 +1,67 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023 Rockchip Electronics Co., Ltd.
*
*/
#ifndef __MAXIM4C_MIPI_TXPHY_H__
#define __MAXIM4C_MIPI_TXPHY_H__
/* MIPI TXPHY ID: 0 ~ 3 */
enum {
MAXIM4C_TXPHY_ID_A = 0,
MAXIM4C_TXPHY_ID_B,
MAXIM4C_TXPHY_ID_C,
MAXIM4C_TXPHY_ID_D,
MAXIM4C_TXPHY_ID_MAX,
};
/* MIPI TXPHY Bit Mask: bit0 ~ bit3 */
#define MAXIM4C_TXPHY_MASK_A BIT(MAXIM4C_TXPHY_ID_A)
#define MAXIM4C_TXPHY_MASK_B BIT(MAXIM4C_TXPHY_ID_B)
#define MAXIM4C_TXPHY_MASK_C BIT(MAXIM4C_TXPHY_ID_C)
#define MAXIM4C_TXPHY_MASK_D BIT(MAXIM4C_TXPHY_ID_D)
#define MAXIM4C_TXPHY_MASK_ALL GENMASK(MAXIM4C_TXPHY_ID_D, MAXIM4C_TXPHY_ID_A)
/* MIPI TXPHY Type */
enum {
MAXIM4C_TXPHY_TYPE_DPHY = 0,
MAXIM4C_TXPHY_TYPE_CPHY,
};
/* MIPI TXPHY Mode */
enum {
MAXIM4C_TXPHY_MODE_2X4LANES = 0, /* PortA: 1x4Lanes, PortB: 1x4Lanes */
MAXIM4C_TXPHY_MODE_4X2LANES, /* PortA: 2x2Lanes, PortB: 2x2Lanes */
MAXIM4C_TXPHY_MODE_1X4LANES_2X2LANES, /* PortA: 1x4Lanes, PortB: 2x2Lanes */
MAXIM4C_TXPHY_MODE_2X2LANES_1X4LANES, /* PortA: 2x2Lanes, PortB: 1x4Lanes */
};
/* MIPI TXPHY DPLL */
enum {
MAXIM4C_TXPHY_DPLL_PREDEF = 0,
MAXIM4C_TXPHY_DPLL_FINE_TUNING,
};
struct maxim4c_txphy_cfg {
u8 phy_enable;
u8 phy_type;
u8 auto_deskew;
u8 data_lane_num;
u8 data_lane_map;
u8 vc_ext_en;
u8 clock_master;
u8 clock_mode;
};
typedef struct maxim4c_mipi_txphy {
u8 phy_mode; /* mipi txphy mode */
u8 force_clock_out_en; /* Force all MIPI clocks running */
u8 force_clk0_en; /* DPHY0 enabled as clock */
u8 force_clk3_en; /* DPHY3 enabled as clock */
struct maxim4c_txphy_cfg phy_cfg[MAXIM4C_TXPHY_ID_MAX];
} maxim4c_mipi_txphy_t;
#endif /* __MAXIM4C_MIPI_TXPHY_H__ */

View File

@@ -0,0 +1,328 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Maxim Quad GMSL Deserializer Test Pattern Driver
*
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
*
* Author: Cai Wenzhong <cwz@rock-chips.com>
*
*/
#include "maxim4c_api.h"
#define PATTERN_WIDTH 1920
#define PATTERN_HEIGHT 1080
/* pattern generator: 0 or 1 */
enum {
PATTERN_GENERATOR_0 = 0,
PATTERN_GENERATOR_1,
};
/* pattern mode: checkerboard or gradient */
enum {
PATTERN_CHECKERBOARD = 0,
PATTERN_GRADIENT,
};
/* pattern pclk: 25M or 75M 0r 150M or 375M */
enum {
PATTERN_PCLK_25M = 0,
PATTERN_PCLK_75M,
PATTERN_PCLK_150M,
PATTERN_PCLK_375M,
};
static const struct maxim4c_mode maxim4c_pattern_mode = {
.width = PATTERN_WIDTH,
.height = PATTERN_HEIGHT,
.max_fps = {
.numerator = 10000,
.denominator = 300000,
},
.link_freq_idx = 15,
.bus_fmt = MEDIA_BUS_FMT_RGB888_1X24,
.bpp = 24,
.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
};
/* VPG0 or VPG1 register */
#define VPGx_REG(x, reg) ((reg) + 0x30 * (x))
int maxim4c_pattern_enable(maxim4c_t *maxim4c, bool enable)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
struct maxim4c_pattern *pattern = &maxim4c->pattern;
u32 vpgx;
u32 pattern_mode;
u8 reg_mask = 0, reg_val = 0;
int ret = 0;
dev_info(dev, "video pattern: enable = %d\n", enable);
vpgx = pattern->pattern_generator;
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 = maxim4c_i2c_update_byte(client,
VPGx_REG(vpgx, 0x1051), MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_val);
return ret;
}
EXPORT_SYMBOL(maxim4c_pattern_enable);
static int maxim4c_pattern_previnit(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
int ret = 0;
// All links disable at beginning.
ret = maxim4c_i2c_write_byte(client,
0x0006, MAXIM4C_I2C_REG_ADDR_16BITS,
0xF0);
if (ret)
return ret;
// MIPI CSI output disable.
ret = maxim4c_i2c_write_byte(client,
0x040B, MAXIM4C_I2C_REG_ADDR_16BITS,
0x00);
if (ret)
return ret;
// MIPI TXPHY standby
ret = maxim4c_i2c_update_byte(client,
0x08A2, MAXIM4C_I2C_REG_ADDR_16BITS,
0xF0, 0x00);
if (ret)
return ret;
return 0;
}
static int maxim4c_pattern_hw_init(maxim4c_t *maxim4c)
{
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 = maxim4c->client;
struct maxim4c_pattern *pattern = &maxim4c->pattern;
u32 vpgx;
u32 pattern_mode;
u32 pattern_pclk;
u16 reg_addr = 0;
u8 reg_mask = 0, reg_val = 0;
int ret = 0, i = 0;
vpgx = pattern->pattern_generator;
pattern_mode = pattern->pattern_mode;
pattern_pclk = pattern->pattern_pclk;
// PATGEN_MODE = 0, Pattern generator disabled
// use video from the serializer input
ret |= maxim4c_i2c_update_byte(client,
VPGx_REG(vpgx, 0x1051), MAXIM4C_I2C_REG_ADDR_16BITS,
BIT(5) | BIT(4), 0x00);
/* Pattern PCLK:
* 0b00 - 25MHz
* 0b01 - 75MHz
* 0b1x - (PATGEN_CLK_SRC: 0 - 150MHz, 1 - 375MHz).
*/
pattern_pclk = (pattern_pclk & 0x03);
ret |= maxim4c_i2c_write_byte(client,
0x0009, MAXIM4C_I2C_REG_ADDR_16BITS,
pattern_pclk);
if (pattern_pclk >= PATTERN_PCLK_150M) {
reg_mask = BIT(7);
if (pattern_pclk == PATTERN_PCLK_375M)
reg_val = BIT(7);
else
reg_val = 0;
if (vpgx == PATTERN_GENERATOR_0) {
for (i = 0; i < 4; i++) {
reg_addr = 0x01DC + i * 0x20;
ret |= maxim4c_i2c_update_byte(client,
reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_val);
}
} else {
for (i = 0; i < 4; i++) {
reg_addr = 0x025C + i * 0x20;
ret |= maxim4c_i2c_update_byte(client,
reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_val);
}
}
}
/* Configure Video Timing Generator for 1920x1080 @ 30 fps. */
// VS_DLY = 0
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x1052), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_24BITS, 0x000000);
// VS_HIGH = Vsw * Htot
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x1055), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_24BITS, v_sw * h_tot);
// VS_LOW = (Vactive + Vfp + Vbp) * Htot
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x1058), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_24BITS, (v_active + v_fp + v_bp) * h_tot);
// V2H = VS_DLY
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x105b), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_24BITS, 0x000000);
// HS_HIGH = Hsw
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x105e), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_16BITS, h_sw);
// HS_LOW = Hactive + Hfp + Hbp
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x1060), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_16BITS, h_active + h_fp + h_bp);
// HS_CNT = Vtot
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x1062), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_16BITS, v_tot);
// V2D = VS_DLY + Htot * (Vsw + Vbp) + (Hsw + Hbp)
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x1064), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_24BITS, h_tot * (v_sw + v_bp) + (h_sw + h_bp));
// DE_HIGH = Hactive
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x1067), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_16BITS, h_active);
// DE_LOW = Hfp + Hsw + Hbp
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x1069), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_16BITS, h_fp + h_sw + h_bp);
// DE_CNT = Vactive
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x106b), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_16BITS, v_active);
/* Generate VS, HS and DE in free-running mode, Invert HS and VS. */
ret |= maxim4c_i2c_write_byte(client,
VPGx_REG(vpgx, 0x1050), MAXIM4C_I2C_REG_ADDR_16BITS,
0xfb);
/* Configure Video Pattern Generator. */
if (pattern_mode == PATTERN_CHECKERBOARD) {
/* Set checkerboard pattern size. */
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x1074), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_24BITS, 0x3c3c3c);
/* Set checkerboard pattern colors. */
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x106e), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_24BITS, 0xfecc00);
ret |= maxim4c_i2c_write_reg(client,
VPGx_REG(vpgx, 0x1071), MAXIM4C_I2C_REG_ADDR_16BITS,
MAXIM4C_I2C_REG_VALUE_24BITS, 0x006aa7);
} else {
/* Set gradient increment. */
ret |= maxim4c_i2c_write_byte(client,
VPGx_REG(vpgx, 0x106d), MAXIM4C_I2C_REG_ADDR_16BITS,
0x10);
}
return ret;
}
int maxim4c_pattern_init(maxim4c_t *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
struct device_node *node = NULL;
struct maxim4c_mode *supported_mode = NULL;
struct maxim4c_pattern *pattern = NULL;
int ret = 0;
// maxim serdes local
node = of_get_child_by_name(dev->of_node, "serdes-local-device");
if (IS_ERR_OR_NULL(node))
return -ENODEV;
if (!of_device_is_available(node)) {
dev_info(dev, "%pOF is disabled\n", node);
of_node_put(node);
return -ENODEV;
}
maxim4c_mipi_txphy_data_init(maxim4c);
/* mipi txphy parse dt */
ret = maxim4c_mipi_txphy_parse_dt(maxim4c, node);
if (ret)
return ret;
ret = maxim4c_pattern_previnit(maxim4c);
if (ret)
return ret;
ret = maxim4c_mipi_txphy_hw_init(maxim4c);
if (ret)
return ret;
maxim4c->cfg_modes_num = 1;
maxim4c->cur_mode = &maxim4c->supported_mode;
supported_mode = &maxim4c->supported_mode;
// init using def mode
memcpy(supported_mode, &maxim4c_pattern_mode, sizeof(struct maxim4c_mode));
// pattern generator and mode init
pattern = &maxim4c->pattern;
pattern->pattern_generator = PATTERN_GENERATOR_0;
pattern->pattern_mode = PATTERN_CHECKERBOARD;
pattern->pattern_pclk = PATTERN_PCLK_75M;
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_info(dev, "link_freq_idx = %d is too low\n",
supported_mode->link_freq_idx);
break;
case PATTERN_PCLK_375M:
supported_mode->max_fps.denominator = 1500000;
if (supported_mode->link_freq_idx < 22)
dev_info(dev, "link_freq_idx = %d is too low\n",
supported_mode->link_freq_idx);
break;
}
dev_info(dev, "video pattern: generator = %d, mode = %d, pclk = %d\n",
pattern->pattern_generator, pattern->pattern_mode, pattern->pattern_pclk);
ret = maxim4c_pattern_hw_init(maxim4c);
return ret;
}
EXPORT_SYMBOL(maxim4c_pattern_init);

View File

@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023 Rockchip Electronics Co., Ltd.
*
*/
#ifndef __MAXIM4C_PATTERN_H__
#define __MAXIM4C_PATTERN_H__
struct maxim4c_pattern {
u32 pattern_generator;
u32 pattern_mode;
u32 pattern_pclk;
};
#endif /* __MAXIM4C_PATTERN_H__ */

View File

@@ -0,0 +1,249 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Maxim Quad GMSL Deserializer Remode Device Manage
*
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
*
* Author: Cai Wenzhong <cwz@rock-chips.com>
*
*/
#include <linux/module.h>
#include <linux/mfd/core.h>
#include "maxim4c_api.h"
static struct mfd_cell maxim4c_remote_devs[MAXIM4C_LINK_ID_MAX];
static int maxim4c_remote_mfd_devs_init(maxim4c_t *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
struct maxim4c_link_cfg *link_cfg = NULL;
struct mfd_cell *remote_mfd_dev = NULL;
const char *remote_name = NULL, *remote_compatible = NULL;
int link_idx = 0, nr_mfd_cell = 0;
remote_mfd_dev = maxim4c_remote_devs;
nr_mfd_cell = 0;
for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
link_cfg = &gmsl_link->link_cfg[link_idx];
if (link_cfg->link_enable == 0)
continue;
remote_name = link_cfg->remote_info.remote_name;
remote_compatible = link_cfg->remote_info.remote_compatible;
if (remote_compatible == NULL) {
dev_err(dev, "%s: link id = %d, remote compatible = NULL",
__func__, link_idx);
continue;
}
if (remote_name == NULL) {
dev_err(dev, "%s: link id = %d, remote name = NULL",
__func__, link_idx);
continue;
}
remote_mfd_dev->name = remote_name;
remote_mfd_dev->of_compatible = remote_compatible;
remote_mfd_dev++;
nr_mfd_cell++;
}
return nr_mfd_cell;
}
int maxim4c_remote_mfd_add_devices(maxim4c_t *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
int nr_mfd_cell = 0, ret = 0;
nr_mfd_cell = maxim4c_remote_mfd_devs_init(maxim4c);
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,
maxim4c_remote_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(maxim4c_remote_mfd_add_devices);
int maxim4c_remote_devices_init(maxim4c_t *maxim4c, u8 link_init_mask)
{
struct device *dev = &maxim4c->client->dev;
struct maxim4c_remote *remote_device = NULL;
const struct maxim4c_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 < MAXIM4C_LINK_ID_MAX; i++) {
link_enable = maxim4c->gmsl_link.link_cfg[i].link_enable;
if (link_enable == 0) {
dev_info(dev, "link id = %d is disabled\n", i);
continue;
}
if ((link_init_mask & BIT(i)) == 0) {
dev_info(dev, "link id = %d init mask is disabled\n", i);
continue;
}
remote_device = maxim4c->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 = maxim4c_link_get_lock_state(maxim4c, link_mask);
if (link_locked != link_mask) {
dev_info(dev, "link id = %d is unlocked\n", i);
continue;
}
maxim4c_link_select_remote_control(maxim4c, link_mask);
if (remote_ops->remote_init)
ret |= remote_ops->remote_init(remote_device);
}
return ret;
}
EXPORT_SYMBOL(maxim4c_remote_devices_init);
int maxim4c_remote_devices_deinit(maxim4c_t *maxim4c, u8 link_init_mask)
{
struct device *dev = &maxim4c->client->dev;
struct maxim4c_remote *remote_device = NULL;
const struct maxim4c_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 < MAXIM4C_LINK_ID_MAX; i++) {
link_enable = maxim4c->gmsl_link.link_cfg[i].link_enable;
if (link_enable == 0) {
dev_info(dev, "link id = %d is disabled\n", i);
continue;
}
if ((link_init_mask & BIT(i)) == 0) {
dev_info(dev, "link id = %d init mask is disabled\n", i);
continue;
}
remote_device = maxim4c->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 = maxim4c_link_get_lock_state(maxim4c, link_mask);
if (link_locked != link_mask) {
dev_info(dev, "link id = %d is unlocked\n", i);
continue;
}
maxim4c_link_select_remote_control(maxim4c, link_mask);
if (remote_ops->remote_deinit)
ret |= remote_ops->remote_deinit(remote_device);
}
return ret;
}
EXPORT_SYMBOL(maxim4c_remote_devices_deinit);
int maxim4c_remote_load_init_seq(maxim4c_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)) {
ret = maxim4c_i2c_load_init_seq(dev, node,
&remote_device->remote_init_seq);
of_node_put(node);
} else {
ret = 0;
dev_info(dev, "no node remote-init-sequence\n");
}
return ret;
}
EXPORT_SYMBOL(maxim4c_remote_load_init_seq);
int maxim4c_remote_device_register(maxim4c_t *maxim4c,
maxim4c_remote_t *remote_device)
{
struct device *dev = NULL;
u8 remote_id;
if ((maxim4c == 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 < MAXIM4C_LINK_ID_MAX) {
if (maxim4c->remote_device[remote_id] == NULL) {
remote_device->remote_enable = 1;
maxim4c->remote_device[remote_id] = remote_device;
dev_dbg(dev, "%s: remote_id = %d is success\n",
__func__, remote_id);
return 0;
} else {
dev_err(dev, "%s: remote_id = %d is conflict\n",
__func__, remote_id);
return -EINVAL;
}
} else {
dev_err(dev, "%s: remote_id = %d is error\n",
__func__, remote_id);
return -EINVAL;
}
}
EXPORT_SYMBOL(maxim4c_remote_device_register);

View File

@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023 Rockchip Electronics Co., Ltd.
*
*/
#ifndef __MAXIM4C_REMOTE_H__
#define __MAXIM4C_REMOTE_H__
#include "maxim4c_i2c.h"
struct maxim4c_remote;
struct maxim4c_remote_ops {
int (*remote_init)(struct maxim4c_remote *remote);
int (*remote_deinit)(struct maxim4c_remote *remote);
};
typedef struct maxim4c_remote {
struct i2c_client *client;
struct device *dev;
void *local;
const struct maxim4c_remote_ops *remote_ops;
struct maxim4c_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;
} maxim4c_remote_t;
#endif /* __MAXIM4C_REMOTE_H__ */

View File

@@ -0,0 +1,980 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Maxim Quad GMSL Deserializer V4L2 driver
*
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
*
* Author: Cai Wenzhong <cwz@rock-chips.com>
*
*/
#include <linux/interrupt.h>
#include <linux/of_graph.h>
#include <linux/pm_runtime.h>
#include <linux/compat.h>
#include <linux/rk-camera-module.h>
#include <media/media-entity.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#include "maxim4c_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 maxim4c_mode maxim4c_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 maxim4c_support_mode_init(maxim4c_t *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
struct device_node *node = NULL;
struct maxim4c_mode *mode = NULL;
u32 value = 0, vc_array[PAD_MAX];
int ret = 0, i = 0, array_size = 0;
dev_info(dev, "=== maxim4c support mode init ===\n");
maxim4c->cfg_modes_num = 1;
maxim4c->cur_mode = &maxim4c->supported_mode;
mode = &maxim4c->supported_mode;
// init using def mode
memcpy(mode, &maxim4c_def_mode, sizeof(struct maxim4c_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 maxim4c_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *try_fmt =
v4l2_subdev_get_try_format(sd, fh->pad, 0);
const struct maxim4c_mode *def_mode = &maxim4c->supported_mode;
mutex_lock(&maxim4c->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(&maxim4c->mutex);
/* No crop or compose */
return 0;
}
#endif
static int maxim4c_s_power(struct v4l2_subdev *sd, int on)
{
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
struct i2c_client *client = maxim4c->client;
int ret = 0;
mutex_lock(&maxim4c->mutex);
/* If the power state is not modified - no work to do. */
if (maxim4c->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;
}
maxim4c->power_on = true;
} else {
pm_runtime_put(&client->dev);
maxim4c->power_on = false;
}
unlock_and_return:
mutex_unlock(&maxim4c->mutex);
return ret;
}
static void maxim4c_get_module_inf(struct maxim4c *maxim4c,
struct rkmodule_inf *inf)
{
memset(inf, 0, sizeof(*inf));
strscpy(inf->base.sensor, MAXIM4C_NAME, sizeof(inf->base.sensor));
strscpy(inf->base.module, maxim4c->module_name,
sizeof(inf->base.module));
strscpy(inf->base.lens, maxim4c->len_name, sizeof(inf->base.lens));
}
static void maxim4c_get_vicap_rst_inf(struct maxim4c *maxim4c,
struct rkmodule_vicap_reset_info *rst_info)
{
struct i2c_client *client = maxim4c->client;
rst_info->is_reset = maxim4c->hot_plug;
maxim4c->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 maxim4c_set_vicap_rst_inf(struct maxim4c *maxim4c,
struct rkmodule_vicap_reset_info rst_info)
{
maxim4c->is_reset = rst_info.is_reset;
}
static long maxim4c_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
struct rkmodule_csi_dphy_param *dphy_param;
long ret = 0;
dev_dbg(&maxim4c->client->dev, "ioctl cmd = 0x%08x\n", cmd);
switch (cmd) {
case RKMODULE_GET_MODULE_INFO:
maxim4c_get_module_inf(maxim4c, (struct rkmodule_inf *)arg);
break;
case RKMODULE_GET_VICAP_RST_INFO:
maxim4c_get_vicap_rst_inf(maxim4c,
(struct rkmodule_vicap_reset_info *)arg);
break;
case RKMODULE_SET_VICAP_RST_INFO:
maxim4c_set_vicap_rst_inf(maxim4c,
*(struct rkmodule_vicap_reset_info *)arg);
break;
case RKMODULE_SET_CSI_DPHY_PARAM:
dphy_param = (struct rkmodule_csi_dphy_param *)arg;
if (dphy_param->vendor == rk3588_dcphy_param.vendor)
rk3588_dcphy_param = *dphy_param;
dev_dbg(&maxim4c->client->dev, "set dcphy param\n");
break;
case RKMODULE_GET_CSI_DPHY_PARAM:
dphy_param = (struct rkmodule_csi_dphy_param *)arg;
if (dphy_param->vendor == rk3588_dcphy_param.vendor)
*dphy_param = rk3588_dcphy_param;
dev_dbg(&maxim4c->client->dev, "get dcphy param\n");
break;
default:
ret = -ENOIOCTLCMD;
break;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long maxim4c_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 = maxim4c_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 = maxim4c_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 = maxim4c_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 = maxim4c_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 = maxim4c_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 __maxim4c_start_stream(struct maxim4c *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
int ret = 0;
s64 link_freq_hz = 0;
u8 link_mask = 0, link_freq_idx = 0;
u8 video_pipe_mask = 0;
link_mask = maxim4c->gmsl_link.link_enable_mask;
video_pipe_mask = maxim4c->video_pipe.pipe_enable_mask;
// disable all remote control
ret = maxim4c_link_select_remote_control(maxim4c, 0);
if (ret) {
dev_err(dev, "link disable remote control error\n");
return ret;
}
// disable all video pipe
ret = maxim4c_video_pipe_mask_enable(maxim4c, video_pipe_mask, false);
if (ret) {
dev_err(dev, "video pipe disable error\n");
return ret;
}
ret = maxim4c_link_select_remote_enable(maxim4c, link_mask);
if (ret) {
dev_err(dev, "link select enable error, mask = 0x%x\n", link_mask);
return ret;
}
link_mask = maxim4c->gmsl_link.link_locked_mask;
ret = maxim4c_remote_devices_init(maxim4c, link_mask);
if (ret) {
dev_err(dev, "remote devices init error\n");
return ret;
}
// mipi txphy enable setting: standby or enable
ret = maxim4c_mipi_txphy_enable(maxim4c, true);
if (ret) {
dev_err(dev, "mipi txphy enable error\n");
return ret;
}
// mipi txphy dpll setting
link_freq_idx = maxim4c->cur_mode->link_freq_idx;
link_freq_hz = link_freq_items[link_freq_idx];
ret = maxim4c_dphy_dpll_predef_set(maxim4c, link_freq_hz);
if (ret) {
dev_err(dev, "mipi txphy dpll setting error\n");
return ret;
}
// enable video pipe
ret = maxim4c_video_pipe_mask_enable(maxim4c, video_pipe_mask, true);
if (ret) {
dev_err(dev, "video pipe enable error\n");
return ret;
}
ret = maxim4c_link_select_remote_control(maxim4c, link_mask);
if (ret) {
dev_err(dev, "remote control enable error\n");
return ret;
}
/* In case these controls are set before streaming */
mutex_unlock(&maxim4c->mutex);
ret = v4l2_ctrl_handler_setup(&maxim4c->ctrl_handler);
mutex_lock(&maxim4c->mutex);
if (ret)
return ret;
#if MAXIM4C_TEST_PATTERN
ret = maxim4c_pattern_enable(maxim4c, true);
if (ret) {
dev_err(dev, "test pattern setting error\n");
return ret;
}
#endif /* MAXIM4C_TEST_PATTERN */
ret = maxim4c_mipi_csi_output(maxim4c, true);
if (ret) {
dev_err(dev, "mipi csi output error\n");
return ret;
}
if (maxim4c->hot_plug_irq > 0)
enable_irq(maxim4c->hot_plug_irq);
return 0;
}
static int __maxim4c_stop_stream(struct maxim4c *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
u8 link_mask = 0, pipe_mask = 0;
int ret = 0;
link_mask = maxim4c->gmsl_link.link_enable_mask;
pipe_mask = maxim4c->video_pipe.pipe_enable_mask;
if (maxim4c->hot_plug_irq > 0)
disable_irq(maxim4c->hot_plug_irq);
if (maxim4c->hot_plug_work.state_check_wq)
cancel_delayed_work_sync(&maxim4c->hot_plug_work.state_d_work);
ret |= maxim4c_mipi_csi_output(maxim4c, false);
ret |= maxim4c_mipi_txphy_enable(maxim4c, false);
#if MAXIM4C_TEST_PATTERN
ret |= maxim4c_pattern_enable(maxim4c, false);
#endif /* MAXIM4C_TEST_PATTERN */
ret |= maxim4c_video_pipe_mask_enable(maxim4c, pipe_mask, false);
ret |= maxim4c_remote_devices_deinit(maxim4c, link_mask);
ret |= maxim4c_link_select_remote_control(maxim4c, 0);
ret |= maxim4c_link_mask_enable(maxim4c, link_mask, false);
if (ret) {
dev_err(dev, "stop stream error\n");
return ret;
}
return 0;
}
static int maxim4c_s_stream(struct v4l2_subdev *sd, int on)
{
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
struct i2c_client *client = maxim4c->client;
int ret = 0;
dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on,
maxim4c->cur_mode->width, maxim4c->cur_mode->height,
DIV_ROUND_CLOSEST(maxim4c->cur_mode->max_fps.denominator,
maxim4c->cur_mode->max_fps.numerator));
mutex_lock(&maxim4c->mutex);
on = !!on;
if (on == maxim4c->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 = __maxim4c_start_stream(maxim4c);
if (ret) {
v4l2_err(sd, "start stream failed while write regs\n");
pm_runtime_put(&client->dev);
goto unlock_and_return;
}
} else {
__maxim4c_stop_stream(maxim4c);
pm_runtime_put(&client->dev);
}
maxim4c->streaming = on;
unlock_and_return:
mutex_unlock(&maxim4c->mutex);
return ret;
}
static int maxim4c_g_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *fi)
{
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
const struct maxim4c_mode *mode = maxim4c->cur_mode;
mutex_lock(&maxim4c->mutex);
fi->interval = mode->max_fps;
mutex_unlock(&maxim4c->mutex);
return 0;
}
static int maxim4c_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
const struct maxim4c_mode *mode = maxim4c->cur_mode;
if (code->index != 0)
return -EINVAL;
code->code = mode->bus_fmt;
return 0;
}
static int maxim4c_enum_frame_sizes(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
if (fse->index >= maxim4c->cfg_modes_num)
return -EINVAL;
if (fse->code != maxim4c->supported_mode.bus_fmt)
return -EINVAL;
fse->min_width = maxim4c->supported_mode.width;
fse->max_width = maxim4c->supported_mode.width;
fse->max_height = maxim4c->supported_mode.height;
fse->min_height = maxim4c->supported_mode.height;
return 0;
}
static int
maxim4c_enum_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_interval_enum *fie)
{
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
if (fie->index >= maxim4c->cfg_modes_num)
return -EINVAL;
fie->code = maxim4c->supported_mode.bus_fmt;
fie->width = maxim4c->supported_mode.width;
fie->height = maxim4c->supported_mode.height;
fie->interval = maxim4c->supported_mode.max_fps;
return 0;
}
static int maxim4c_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
const struct maxim4c_mode *mode = maxim4c->cur_mode;
mutex_lock(&maxim4c->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(&maxim4c->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(&maxim4c->mutex);
return 0;
}
static int maxim4c_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
struct device *dev = &maxim4c->client->dev;
const struct maxim4c_mode *mode = NULL;
u64 link_freq = 0, pixel_rate = 0;
u8 data_lanes;
mutex_lock(&maxim4c->mutex);
mode = &maxim4c->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(&maxim4c->mutex);
return -ENOTTY;
#endif
} else {
if (maxim4c->streaming) {
mutex_unlock(&maxim4c->mutex);
return -EBUSY;
}
maxim4c->cur_mode = mode;
__v4l2_ctrl_s_ctrl(maxim4c->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 = maxim4c->bus_cfg.bus.mipi_csi2.num_data_lanes;
pixel_rate = (u32)link_freq / mode->bpp * 2 * data_lanes;
__v4l2_ctrl_s_ctrl_int64(maxim4c->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(&maxim4c->mutex);
return 0;
}
static int maxim4c_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) {
sel->r.left = 0;
sel->r.width = maxim4c->cur_mode->width;
sel->r.top = 0;
sel->r.height = maxim4c->cur_mode->height;
return 0;
}
return -EINVAL;
}
static int maxim4c_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_mbus_config *config)
{
struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
u32 val = 0;
u8 data_lanes = maxim4c->bus_cfg.bus.mipi_csi2.num_data_lanes;
val |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
val |= (1 << (data_lanes - 1));
switch (data_lanes) {
case 4:
val |= V4L2_MBUS_CSI2_CHANNEL_3;
fallthrough;
case 3:
val |= V4L2_MBUS_CSI2_CHANNEL_2;
fallthrough;
case 2:
val |= V4L2_MBUS_CSI2_CHANNEL_1;
fallthrough;
case 1:
default:
val |= V4L2_MBUS_CSI2_CHANNEL_0;
break;
}
config->type = V4L2_MBUS_CSI2_DPHY;
config->flags = val;
return 0;
}
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
static const struct v4l2_subdev_internal_ops maxim4c_internal_ops = {
.open = maxim4c_open,
};
#endif
static const struct v4l2_subdev_core_ops maxim4c_core_ops = {
.s_power = maxim4c_s_power,
.ioctl = maxim4c_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = maxim4c_compat_ioctl32,
#endif
};
static const struct v4l2_subdev_video_ops maxim4c_video_ops = {
.s_stream = maxim4c_s_stream,
.g_frame_interval = maxim4c_g_frame_interval,
};
static const struct v4l2_subdev_pad_ops maxim4c_pad_ops = {
.enum_mbus_code = maxim4c_enum_mbus_code,
.enum_frame_size = maxim4c_enum_frame_sizes,
.enum_frame_interval = maxim4c_enum_frame_interval,
.get_fmt = maxim4c_get_fmt,
.set_fmt = maxim4c_set_fmt,
.get_selection = maxim4c_get_selection,
.get_mbus_config = maxim4c_g_mbus_config,
};
static const struct v4l2_subdev_ops maxim4c_subdev_ops = {
.core = &maxim4c_core_ops,
.video = &maxim4c_video_ops,
.pad = &maxim4c_pad_ops,
};
static int maxim4c_initialize_controls(struct maxim4c *maxim4c)
{
struct device *dev = &maxim4c->client->dev;
const struct maxim4c_mode *mode;
struct v4l2_ctrl_handler *handler;
u64 link_freq = 0, pixel_rate = 0;
u8 data_lanes;
int ret = 0;
handler = &maxim4c->ctrl_handler;
ret = v4l2_ctrl_handler_init(handler, 2);
if (ret)
return ret;
handler->lock = &maxim4c->mutex;
mode = maxim4c->cur_mode;
maxim4c->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(maxim4c->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 = maxim4c->bus_cfg.bus.mipi_csi2.num_data_lanes;
pixel_rate = (u32)link_freq / mode->bpp * 2 * data_lanes;
maxim4c->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;
}
maxim4c->subdev.ctrl_handler = handler;
return 0;
err_free_handler:
v4l2_ctrl_handler_free(handler);
return ret;
}
static int maxim4c_mipi_data_lanes_parse(maxim4c_t *maxim4c)
{
struct device *dev = &maxim4c->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),
&maxim4c->bus_cfg);
if (ret) {
dev_err(dev, "Failed to get bus config\n");
return -EINVAL;
}
mipi_data_lanes = maxim4c->bus_cfg.bus.mipi_csi2.num_data_lanes;
dev_info(dev, "mipi csi2 phy data lanes = %d\n", mipi_data_lanes);
return 0;
}
int maxim4c_v4l2_subdev_init(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
struct v4l2_subdev *sd = NULL;
char facing[2];
int ret = 0;
maxim4c_mipi_data_lanes_parse(maxim4c);
maxim4c_support_mode_init(maxim4c);
sd = &maxim4c->subdev;
v4l2_i2c_subdev_init(sd, client, &maxim4c_subdev_ops);
ret = maxim4c_initialize_controls(maxim4c);
if (ret)
goto err_free_handler;
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
sd->internal_ops = &maxim4c_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
#endif
#if defined(CONFIG_MEDIA_CONTROLLER)
maxim4c->pad.flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&sd->entity, 1, &maxim4c->pad);
if (ret < 0)
goto err_free_handler;
#endif
v4l2_set_subdevdata(sd, maxim4c);
memset(facing, 0, sizeof(facing));
if (strcmp(maxim4c->module_facing, "back") == 0)
facing[0] = 'b';
else
facing[0] = 'f';
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
maxim4c->module_index, facing, MAXIM4C_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(&maxim4c->ctrl_handler);
return ret;
}
EXPORT_SYMBOL(maxim4c_v4l2_subdev_init);
void maxim4c_v4l2_subdev_deinit(maxim4c_t *maxim4c)
{
struct v4l2_subdev *sd = &maxim4c->subdev;
v4l2_async_unregister_subdev(sd);
#if defined(CONFIG_MEDIA_CONTROLLER)
media_entity_cleanup(&sd->entity);
#endif
v4l2_ctrl_handler_free(&maxim4c->ctrl_handler);
}
EXPORT_SYMBOL(maxim4c_v4l2_subdev_deinit);

View File

@@ -0,0 +1,346 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Maxim Quad GMSL Deserializer Video Pipe driver
*
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
*
* Author: Cai Wenzhong <cwz@rock-chips.com>
*
*/
#include "maxim4c_api.h"
static int maxim4c_video_pipe_select(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
u8 reg_mask = 0, reg_value = 0;
u16 reg_addr = 0;
int i = 0, shift = 0;
int ret = 0;
// video pipe selection
reg_mask = 0;
reg_value = 0;
for (i = 0; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
video_pipe_cfg = &video_pipe->pipe_cfg[i];
shift = (i % 2) ? 4 : 0;
if (video_pipe_cfg->pipe_enable) {
reg_mask |= (0xF << shift);
reg_value |= ((video_pipe_cfg->pipe_idx & 0x3) << (0 + shift));
reg_value |= ((video_pipe_cfg->link_idx & 0x3) << (2 + shift));
}
if ((i % 2 == 1) && (reg_mask != 0)) {
reg_addr = 0x00F0 + (i / 2);
ret |= maxim4c_i2c_update_byte(client,
reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
// Prepare for next register
reg_mask = 0;
reg_value = 0;
}
}
return ret;
}
static int maxim4c_video_pipe_run_init_seq(maxim4c_t *maxim4c)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
struct maxim4c_i2c_init_seq *init_seq = NULL;
int i = 0;
int ret = 0;
// video pipe init sequence
for (i = 0; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
video_pipe_cfg = &video_pipe->pipe_cfg[i];
init_seq = &video_pipe_cfg->pipe_init_seq;
ret = maxim4c_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 = maxim4c_i2c_run_init_seq(client, init_seq);
if (ret) {
dev_err(dev, "pipe parallel init sequence error\n");
return ret;
}
return 0;
}
static int maxim4c_video_pipe_config_parse_dt(struct device *dev,
maxim4c_video_pipe_t *video_pipe,
struct device_node *parent_node)
{
struct device_node *node = NULL;
struct device_node *init_seq_node = NULL;
struct maxim4c_i2c_init_seq *init_seq = NULL;
struct maxim4c_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 >= MAXIM4C_PIPE_O_ID_MAX) {
dev_err(dev, "Too many matching %s node\n",
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 >= MAXIM4C_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;
maxim4c_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;
maxim4c_i2c_load_init_seq(dev, init_seq_node, init_seq);
of_node_put(init_seq_node);
}
of_node_put(node);
}
return 0;
}
int maxim4c_video_pipe_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node)
{
struct device *dev = &maxim4c->client->dev;
struct device_node *node = NULL;
maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
int ret = 0;
dev_info(dev, "=== maxim4c video pipe parse dt ===\n");
node = of_get_child_by_name(of_node, "video-pipes");
if (IS_ERR_OR_NULL(node))
return -ENODEV;
if (!of_device_is_available(node)) {
dev_info(dev, "%pOF is disabled\n", node);
of_node_put(node);
return -ENODEV;
}
ret = maxim4c_video_pipe_config_parse_dt(dev, video_pipe, node);
of_node_put(node);
return ret;
}
EXPORT_SYMBOL(maxim4c_video_pipe_parse_dt);
int maxim4c_video_pipe_mask_enable(maxim4c_t *maxim4c, u8 video_pipe_mask, bool enable)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
struct maxim4c_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 < MAXIM4C_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 |= maxim4c_i2c_update_byte(client,
0x00F4, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
}
return ret;
}
EXPORT_SYMBOL(maxim4c_video_pipe_mask_enable);
int maxim4c_video_pipe_linkid_enable(maxim4c_t *maxim4c, u8 link_id, bool enable)
{
struct i2c_client *client = maxim4c->client;
struct device *dev = &client->dev;
maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
struct maxim4c_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 < MAXIM4C_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 = maxim4c_i2c_update_byte(client,
0x00F4, MAXIM4C_I2C_REG_ADDR_16BITS,
reg_mask, reg_value);
}
return ret;
}
EXPORT_SYMBOL(maxim4c_video_pipe_linkid_enable);
void maxim4c_video_pipe_data_init(maxim4c_t *maxim4c)
{
maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
struct maxim4c_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 < 4; i++) {
video_pipe_cfg = &video_pipe->pipe_cfg[i];
video_pipe_cfg->pipe_enable = 0;
video_pipe_cfg->pipe_idx = MAXIM4C_PIPE_I_ID_Z;
video_pipe_cfg->link_idx = (i % 4);
video_pipe_cfg->pipe_init_seq.reg_init_seq = NULL;
}
for (i = 4; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
video_pipe_cfg = &video_pipe->pipe_cfg[i];
video_pipe_cfg->pipe_enable = 0;
video_pipe_cfg->pipe_idx = MAXIM4C_PIPE_I_ID_X;
video_pipe_cfg->link_idx = (i % 4);
video_pipe_cfg->pipe_init_seq.reg_init_seq = NULL;
}
}
EXPORT_SYMBOL(maxim4c_video_pipe_data_init);
int maxim4c_video_pipe_hw_init(maxim4c_t *maxim4c)
{
u8 pipe_enable_mask = 0;
int ret = 0;
ret = maxim4c_video_pipe_select(maxim4c);
if (ret)
return ret;
pipe_enable_mask = maxim4c->video_pipe.pipe_enable_mask;
ret = maxim4c_video_pipe_mask_enable(maxim4c, pipe_enable_mask, true);
if (ret)
return ret;
ret = maxim4c_video_pipe_run_init_seq(maxim4c);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL(maxim4c_video_pipe_hw_init);

View File

@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023 Rockchip Electronics Co., Ltd.
*
*/
#ifndef __MAXIM4C_VIDEO_PIPE_H__
#define __MAXIM4C_VIDEO_PIPE_H__
#include "maxim4c_i2c.h"
/* Maxim4c Video Pipe In ID: 0 ~ 3 */
enum {
MAXIM4C_PIPE_I_ID_X = 0,
MAXIM4C_PIPE_I_ID_Y,
MAXIM4C_PIPE_I_ID_Z,
MAXIM4C_PIPE_I_ID_U,
MAXIM4C_PIPE_I_ID_MAX,
};
/* Maxim4c Video Pipe Out ID: 0 ~ 7 */
enum {
MAXIM4C_PIPE_O_ID_0 = 0,
MAXIM4C_PIPE_O_ID_1,
MAXIM4C_PIPE_O_ID_2,
MAXIM4C_PIPE_O_ID_3,
MAXIM4C_PIPE_O_ID_4,
MAXIM4C_PIPE_O_ID_5,
MAXIM4C_PIPE_O_ID_6,
MAXIM4C_PIPE_O_ID_7,
MAXIM4C_PIPE_O_ID_MAX,
};
/* Maxim4c Video Pipe Out Config */
struct maxim4c_pipe_cfg {
u8 pipe_enable;
u8 pipe_idx;
u8 link_idx;
struct maxim4c_i2c_init_seq pipe_init_seq;
};
typedef struct maxim4c_video_pipe {
u8 pipe_enable_mask;
struct maxim4c_pipe_cfg pipe_cfg[MAXIM4C_PIPE_O_ID_MAX];
struct maxim4c_i2c_init_seq parallel_init_seq;
} maxim4c_video_pipe_t;
#endif /* __MAXIM4C_VIDEO_PIPE_H__ */

View File

@@ -0,0 +1,351 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Maxim Quad GMSL2/GMSL1 to CSI-2 Serializer driver
*
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
*
* Author: Cai Wenzhong <cwz@rock-chips.com>
*
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include "maxim4c_api.h"
#define MAX9295_I2C_ADDR_DEF 0x40
#define MAX9295_CHIP_ID 0x91
#define MAX9295_REG_CHIP_ID 0x0D
static int max9295_i2c_addr_select(maxim4c_remote_t *max9295, u32 i2c_id)
{
struct device *dev = max9295->dev;
struct i2c_client *client = max9295->client;
if (i2c_id == MAXIM4C_I2C_SER_DEF) {
client->addr = max9295->ser_i2c_addr_def;
dev_info(dev, "select default i2c addr = 0x%x\n", client->addr);
} else if (i2c_id == MAXIM4C_I2C_SER_MAP) {
client->addr = max9295->ser_i2c_addr_map;
dev_info(dev, "select mapping i2c addr = 0x%x\n", client->addr);
} else {
dev_err(dev, "i2c select id = %d error\n", i2c_id);
return -EINVAL;
}
return 0;
}
static int max9295_i2c_addr_remap(maxim4c_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");
max9295_i2c_addr_select(max9295, MAXIM4C_I2C_SER_DEF);
i2c_8bit_addr = (max9295->ser_i2c_addr_map << 1);
ret = maxim4c_i2c_write_byte(client,
0x0000, MAXIM4C_I2C_REG_ADDR_16BITS,
i2c_8bit_addr);
if (ret) {
dev_err(dev, "ser i2c address map setting error!\n");
return ret;
}
max9295_i2c_addr_select(max9295, MAXIM4C_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 = maxim4c_i2c_write_byte(client,
0x0042, MAXIM4C_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 = maxim4c_i2c_write_byte(client,
0x0043, MAXIM4C_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(maxim4c_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");
max9295_i2c_addr_select(max9295, MAXIM4C_I2C_SER_MAP);
i2c_8bit_addr = (max9295->ser_i2c_addr_def << 1);
ret = maxim4c_i2c_write_byte(client,
0x0000, MAXIM4C_I2C_REG_ADDR_16BITS,
i2c_8bit_addr);
if (ret) {
dev_err(dev, "ser i2c address def setting error!\n");
return ret;
}
max9295_i2c_addr_select(max9295, MAXIM4C_I2C_SER_DEF);
}
return 0;
}
static int max9295_check_chipid(maxim4c_remote_t *max9295)
{
struct device *dev = max9295->dev;
struct i2c_client *client = max9295->client;
u8 chip_id;
int ret = 0;
// max9295
ret = maxim4c_i2c_read_byte(client,
MAX9295_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_16BITS,
&chip_id);
if (ret != 0) {
dev_info(dev, "Retry check chipid using map address\n");
max9295_i2c_addr_select(max9295, MAXIM4C_I2C_SER_MAP);
ret = maxim4c_i2c_read_byte(client,
MAX9295_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_16BITS,
&chip_id);
if (ret != 0) {
dev_err(dev, "MAX9295 detect error, ret(%d)\n", ret);
max9295_i2c_addr_select(max9295, MAXIM4C_I2C_SER_DEF);
return -ENODEV;
}
max9295_i2c_addr_def(max9295);
}
if (chip_id != MAX9295_CHIP_ID) {
dev_err(dev, "Unexpected MAX9295 chip id(%02x)\n", chip_id);
return -ENODEV;
}
dev_info(dev, "Detected MAX9295 chipid: 0x%02x\n", chip_id);
return ret;
}
static int max9295_soft_power_down(maxim4c_remote_t *max9295)
{
struct device *dev = max9295->dev;
struct i2c_client *client = max9295->client;
int ret = 0;
ret = maxim4c_i2c_write_byte(client,
0x10, MAXIM4C_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(maxim4c_remote_t *max9295)
{
struct device *dev = max9295->dev;
struct i2c_client *client = max9295->client;
int ret = 0;
ret = max9295_i2c_addr_select(max9295, MAXIM4C_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 = maxim4c_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(maxim4c_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 maxim4c_remote_ops max9295_ops = {
.remote_init = max9295_module_init,
.remote_deinit = max9295_module_deinit,
};
static int max9295_parse_dt(maxim4c_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, "=== maxim4c 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 = MAXIM4C_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_i2c_client_init(maxim4c_remote_t *max9295,
struct i2c_client *local_client)
{
struct device *dev = max9295->dev;
struct i2c_client *remote_client = NULL;
u16 remote_client_addr = 0;
if (max9295->ser_i2c_addr_map)
remote_client_addr = max9295->ser_i2c_addr_map;
else
remote_client_addr = max9295->ser_i2c_addr_def;
remote_client = devm_i2c_new_dummy_device(&local_client->dev,
local_client->adapter, remote_client_addr);
if (IS_ERR(remote_client)) {
dev_err(dev, "failed to alloc i2c client.\n");
return -PTR_ERR(remote_client);
}
remote_client->addr = max9295->ser_i2c_addr_def;
max9295->client = remote_client;
i2c_set_clientdata(remote_client, max9295);
dev_info(dev, "remote i2c client init, i2c_addr = 0x%x\n",
remote_client_addr);
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 maxim4c *maxim4c = v4l2_get_subdevdata(sd);
struct maxim4c_remote *max9295 = NULL;
int ret = 0;
dev_info(&pdev->dev, "max9295 serializer probe\n");
max9295 = devm_kzalloc(&pdev->dev, sizeof(*max9295), GFP_KERNEL);
if (!max9295)
return -ENOMEM;
max9295->dev = &pdev->dev;
max9295->remote_ops = &max9295_ops;
max9295->local = maxim4c;
dev_set_drvdata(max9295->dev, max9295);
max9295_parse_dt(max9295);
max9295_i2c_client_init(max9295, client);
ret = maxim4c_remote_device_register(maxim4c, max9295);
if (ret)
return ret;
maxim4c_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 = "maxim4c,max9295", },
{ /* Sentinel */ },
};
MODULE_DEVICE_TABLE(of, max9295_of_table);
static struct platform_driver max9295_driver = {
.probe = max9295_probe,
.remove = max9295_remove,
.driver = {
.name = "max9295",
.of_match_table = max9295_of_table,
},
};
module_platform_driver(max9295_driver);
MODULE_AUTHOR("Cai Wenzhong <cwz@rock-chips.com>");
MODULE_DESCRIPTION("Maxim MAX9295 Serializer Driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,410 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Maxim Quad GMSL2/GMSL1 to CSI-2 Serializer driver
*
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
*
* Author: Cai Wenzhong <cwz@rock-chips.com>
*
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include "maxim4c_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(maxim4c_remote_t *max96715, u32 mode)
{
struct device *dev = max96715->dev;
struct i2c_client *client = max96715->client;
u8 reg_mask = 0, reg_value = 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);
else
reg_value = BIT(7);
ret |= maxim4c_i2c_update_byte(client,
0x04, MAXIM4C_I2C_REG_ADDR_08BITS,
reg_mask, reg_value);
mdelay(5);
return ret;
}
static int max96715_i2c_addr_select(maxim4c_remote_t *max96715, u32 i2c_id)
{
struct device *dev = max96715->dev;
struct i2c_client *client = max96715->client;
if (i2c_id == MAXIM4C_I2C_SER_DEF) {
client->addr = max96715->ser_i2c_addr_def;
dev_info(dev, "select default i2c addr = 0x%x\n", client->addr);
} else if (i2c_id == MAXIM4C_I2C_SER_MAP) {
client->addr = max96715->ser_i2c_addr_map;
dev_info(dev, "select mapping i2c addr = 0x%x\n", client->addr);
} else {
dev_err(dev, "i2c select id = %d error\n", i2c_id);
return -EINVAL;
}
return 0;
}
static int max96715_i2c_addr_remap(maxim4c_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");
max96715_i2c_addr_select(max96715, MAXIM4C_I2C_SER_DEF);
i2c_8bit_addr = (max96715->ser_i2c_addr_map << 1);
ret = maxim4c_i2c_write_byte(client,
0x00, MAXIM4C_I2C_REG_ADDR_08BITS,
i2c_8bit_addr);
if (ret) {
dev_err(dev, "ser i2c address map setting error!\n");
return ret;
}
max96715_i2c_addr_select(max96715, MAXIM4C_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 = maxim4c_i2c_write_byte(client,
0x09, MAXIM4C_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 = maxim4c_i2c_write_byte(client,
0x0A, MAXIM4C_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(maxim4c_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");
max96715_i2c_addr_select(max96715, MAXIM4C_I2C_SER_MAP);
i2c_8bit_addr = (max96715->ser_i2c_addr_def << 1);
ret = maxim4c_i2c_write_byte(client,
0x00, MAXIM4C_I2C_REG_ADDR_08BITS,
i2c_8bit_addr);
if (ret) {
dev_err(dev, "ser i2c address def setting error!\n");
return ret;
}
max96715_i2c_addr_select(max96715, MAXIM4C_I2C_SER_DEF);
}
return 0;
}
static int max96715_check_chipid(maxim4c_remote_t *max96715)
{
struct device *dev = max96715->dev;
struct i2c_client *client = max96715->client;
u8 chip_id;
int ret = 0;
// max96715
ret = maxim4c_i2c_read_byte(client,
MAX96715_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_08BITS,
&chip_id);
if (ret != 0) {
dev_info(dev, "Retry check chipid using map address\n");
max96715_i2c_addr_select(max96715, MAXIM4C_I2C_SER_MAP);
ret = maxim4c_i2c_read_byte(client,
MAX96715_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_08BITS,
&chip_id);
if (ret != 0) {
dev_err(dev, "MAX96715 detect error, ret(%d)\n", ret);
max96715_i2c_addr_select(max96715, MAXIM4C_I2C_SER_DEF);
return -ENODEV;
}
max96715_i2c_addr_def(max96715);
}
if (chip_id != MAX96715_CHIP_ID) {
dev_err(dev, "Unexpected MAX96715 chip id(%02x)\n", chip_id);
return -ENODEV;
}
dev_info(dev, "Detected MAX96715 chipid: 0x%02x\n", chip_id);
return ret;
}
static int max96715_soft_power_down(maxim4c_remote_t *max96715)
{
struct device *dev = max96715->dev;
struct i2c_client *client = max96715->client;
int ret = 0;
ret = maxim4c_i2c_write_byte(client,
0x13, MAXIM4C_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(maxim4c_remote_t *max96715)
{
struct device *dev = max96715->dev;
struct i2c_client *client = max96715->client;
struct maxim4c *maxim4c = max96715->local;
int ret = 0;
ret = max96715_i2c_addr_select(max96715, MAXIM4C_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
if (maxim4c->hot_plug_irq > 0)
disable_irq(maxim4c->hot_plug_irq);
ret = max96715_link_mode_select(max96715, LINK_MODE_CONFIG);
if (ret) {
if (maxim4c->hot_plug_irq > 0)
enable_irq(maxim4c->hot_plug_irq);
return ret;
}
#endif
ret = maxim4c_i2c_run_init_seq(client,
&max96715->remote_init_seq);
if (ret) {
dev_err(dev, "remote id = %d init sequence error\n",
max96715->remote_id);
if (maxim4c->hot_plug_irq > 0)
enable_irq(maxim4c->hot_plug_irq);
return ret;
}
#if MAX96715_MODE_SWITCH
ret = max96715_link_mode_select(max96715, LINK_MODE_VIDEO);
if (maxim4c->hot_plug_irq > 0)
enable_irq(maxim4c->hot_plug_irq);
if (ret)
return ret;
#endif
return 0;
}
static int max96715_module_deinit(maxim4c_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 maxim4c_remote_ops max96715_ops = {
.remote_init = max96715_module_init,
.remote_deinit = max96715_module_deinit,
};
static int max96715_parse_dt(maxim4c_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, "=== maxim4c 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 = MAXIM4C_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_i2c_client_init(maxim4c_remote_t *max96715,
struct i2c_client *local_client)
{
struct device *dev = max96715->dev;
struct i2c_client *remote_client = NULL;
u16 remote_client_addr = 0;
if (max96715->ser_i2c_addr_map)
remote_client_addr = max96715->ser_i2c_addr_map;
else
remote_client_addr = max96715->ser_i2c_addr_def;
remote_client = devm_i2c_new_dummy_device(&local_client->dev,
local_client->adapter, remote_client_addr);
if (IS_ERR(remote_client)) {
dev_err(dev, "failed to alloc i2c client.\n");
return -PTR_ERR(remote_client);
}
remote_client->addr = max96715->ser_i2c_addr_def;
max96715->client = remote_client;
i2c_set_clientdata(remote_client, max96715);
dev_info(dev, "remote i2c client init, i2c_addr = 0x%x\n",
remote_client_addr);
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 maxim4c *maxim4c = v4l2_get_subdevdata(sd);
struct maxim4c_remote *max96715 = NULL;
int ret = 0;
dev_info(&pdev->dev, "max96715 serializer probe\n");
max96715 = devm_kzalloc(&pdev->dev, sizeof(*max96715), GFP_KERNEL);
if (!max96715)
return -ENOMEM;
max96715->dev = &pdev->dev;
max96715->remote_ops = &max96715_ops;
max96715->local = maxim4c;
dev_set_drvdata(max96715->dev, max96715);
max96715_parse_dt(max96715);
max96715_i2c_client_init(max96715, client);
ret = maxim4c_remote_device_register(maxim4c, max96715);
if (ret)
return ret;
maxim4c_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 = "maxim4c,max96715", },
{ /* Sentinel */ },
};
MODULE_DEVICE_TABLE(of, max96715_of_table);
static struct platform_driver max96715_driver = {
.probe = max96715_probe,
.remove = max96715_remove,
.driver = {
.name = "max96715",
.of_match_table = max96715_of_table,
},
};
module_platform_driver(max96715_driver);
MODULE_AUTHOR("Cai Wenzhong <cwz@rock-chips.com>");
MODULE_DESCRIPTION("Maxim MAX96715 Serializer Driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,350 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Maxim Quad GMSL2/GMSL1 to CSI-2 Serializer driver
*
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
*
* Author: Cai Wenzhong <cwz@rock-chips.com>
*
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include "maxim4c_api.h"
#define MAX96717_I2C_ADDR_DEF 0x40
#define MAX96717_CHIP_ID 0xBF
#define MAX96717_REG_CHIP_ID 0x0D
static int max96717_i2c_addr_select(maxim4c_remote_t *max96717, u32 i2c_id)
{
struct device *dev = max96717->dev;
struct i2c_client *client = max96717->client;
if (i2c_id == MAXIM4C_I2C_SER_DEF) {
client->addr = max96717->ser_i2c_addr_def;
dev_info(dev, "select default i2c addr = 0x%x\n", client->addr);
} else if (i2c_id == MAXIM4C_I2C_SER_MAP) {
client->addr = max96717->ser_i2c_addr_map;
dev_info(dev, "select mapping i2c addr = 0x%x\n", client->addr);
} else {
dev_err(dev, "i2c select id = %d error\n", i2c_id);
return -EINVAL;
}
return 0;
}
static int max96717_i2c_addr_remap(maxim4c_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");
max96717_i2c_addr_select(max96717, MAXIM4C_I2C_SER_DEF);
i2c_8bit_addr = (max96717->ser_i2c_addr_map << 1);
ret = maxim4c_i2c_write_byte(client,
0x0000, MAXIM4C_I2C_REG_ADDR_16BITS,
i2c_8bit_addr);
if (ret) {
dev_err(dev, "ser i2c address map setting error!\n");
return ret;
}
max96717_i2c_addr_select(max96717, MAXIM4C_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 = maxim4c_i2c_write_byte(client,
0x0042, MAXIM4C_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 = maxim4c_i2c_write_byte(client,
0x0043, MAXIM4C_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(maxim4c_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");
max96717_i2c_addr_select(max96717, MAXIM4C_I2C_SER_MAP);
i2c_8bit_addr = (max96717->ser_i2c_addr_def << 1);
ret = maxim4c_i2c_write_byte(client,
0x0000, MAXIM4C_I2C_REG_ADDR_16BITS,
i2c_8bit_addr);
if (ret) {
dev_err(dev, "ser i2c address def setting error!\n");
return ret;
}
max96717_i2c_addr_select(max96717, MAXIM4C_I2C_SER_DEF);
}
return 0;
}
static int max96717_check_chipid(maxim4c_remote_t *max96717)
{
struct device *dev = max96717->dev;
struct i2c_client *client = max96717->client;
u8 chip_id;
int ret = 0;
// max96717
ret = maxim4c_i2c_read_byte(client,
MAX96717_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_16BITS,
&chip_id);
if (ret != 0) {
dev_info(dev, "Retry check chipid using map address\n");
max96717_i2c_addr_select(max96717, MAXIM4C_I2C_SER_MAP);
ret = maxim4c_i2c_read_byte(client,
MAX96717_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_16BITS,
&chip_id);
if (ret != 0) {
dev_err(dev, "MAX96717 detect error, ret(%d)\n", ret);
max96717_i2c_addr_select(max96717, MAXIM4C_I2C_SER_DEF);
return -ENODEV;
}
max96717_i2c_addr_def(max96717);
}
if (chip_id != MAX96717_CHIP_ID) {
dev_err(dev, "Unexpected MAX96717 chip id(%02x)\n", chip_id);
return -ENODEV;
}
dev_info(dev, "Detected MAX96717 chipid: 0x%02x\n", chip_id);
return ret;
}
static int max96717_soft_power_down(maxim4c_remote_t *max96717)
{
struct device *dev = max96717->dev;
struct i2c_client *client = max96717->client;
int ret = 0;
ret = maxim4c_i2c_write_byte(client,
0x10, MAXIM4C_I2C_REG_ADDR_16BITS,
BIT(7));
if (ret) {
dev_err(dev, "soft power down setting error!\n");
return ret;
}
return 0;
}
static int max96717_module_init(maxim4c_remote_t *max96717)
{
struct device *dev = max96717->dev;
struct i2c_client *client = max96717->client;
int ret = 0;
ret = max96717_i2c_addr_select(max96717, MAXIM4C_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 = maxim4c_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(maxim4c_remote_t *max96717)
{
int ret = 0;
#if 0
ret |= max96717_i2c_addr_def(max96717);
#endif
ret |= max96717_soft_power_down(max96717);
return ret;
}
static const struct maxim4c_remote_ops max96717_ops = {
.remote_init = max96717_module_init,
.remote_deinit = max96717_module_deinit,
};
static int max96717_parse_dt(maxim4c_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, "=== maxim4c 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 = MAXIM4C_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_i2c_client_init(maxim4c_remote_t *max96717,
struct i2c_client *local_client)
{
struct device *dev = max96717->dev;
struct i2c_client *remote_client = NULL;
u16 remote_client_addr = 0;
if (max96717->ser_i2c_addr_map)
remote_client_addr = max96717->ser_i2c_addr_map;
else
remote_client_addr = max96717->ser_i2c_addr_def;
remote_client = devm_i2c_new_dummy_device(&local_client->dev,
local_client->adapter, remote_client_addr);
if (IS_ERR(remote_client)) {
dev_err(dev, "failed to alloc i2c client.\n");
return -PTR_ERR(remote_client);
}
remote_client->addr = max96717->ser_i2c_addr_def;
max96717->client = remote_client;
i2c_set_clientdata(remote_client, max96717);
dev_info(dev, "remote i2c client init, i2c_addr = 0x%x\n",
remote_client_addr);
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 maxim4c *maxim4c = v4l2_get_subdevdata(sd);
struct maxim4c_remote *max96717 = NULL;
int ret = 0;
dev_info(&pdev->dev, "max96717 serializer probe\n");
max96717 = devm_kzalloc(&pdev->dev, sizeof(*max96717), GFP_KERNEL);
if (!max96717)
return -ENOMEM;
max96717->dev = &pdev->dev;
max96717->remote_ops = &max96717_ops;
max96717->local = maxim4c;
dev_set_drvdata(max96717->dev, max96717);
max96717_parse_dt(max96717);
max96717_i2c_client_init(max96717, client);
ret = maxim4c_remote_device_register(maxim4c, max96717);
if (ret)
return ret;
maxim4c_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 = "maxim4c,max96717", },
{ /* Sentinel */ },
};
MODULE_DEVICE_TABLE(of, max96717_of_table);
static struct platform_driver max96717_driver = {
.probe = max96717_probe,
.remove = max96717_remove,
.driver = {
.name = "max96717",
.of_match_table = max96717_of_table,
},
};
module_platform_driver(max96717_driver);
MODULE_AUTHOR("Cai Wenzhong <cwz@rock-chips.com>");
MODULE_DESCRIPTION("Maxim MAX96717 Serializer Driver");
MODULE_LICENSE("GPL");