diff --git a/drivers/media/i2c/maxim4c/maxim4c_api.h b/drivers/media/i2c/maxim4c/maxim4c_api.h index 25f261c84a44..a2968a30646f 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_api.h +++ b/drivers/media/i2c/maxim4c/maxim4c_api.h @@ -90,7 +90,9 @@ int maxim4c_remote_device_register(maxim4c_t *maxim4c, int maxim4c_v4l2_subdev_init(maxim4c_t *maxim4c); void maxim4c_v4l2_subdev_deinit(maxim4c_t *maxim4c); +/* maxim4c driver api */ int maxim4c_module_hw_init(maxim4c_t *maxim4c); +int maxim4c_hot_plug_detect_work_start(maxim4c_t *maxim4c); /* maxim4c pattern api */ int maxim4c_pattern_hw_init(maxim4c_t *maxim4c); diff --git a/drivers/media/i2c/maxim4c/maxim4c_drv.c b/drivers/media/i2c/maxim4c/maxim4c_drv.c index 65650eeb5387..c543c6297ab2 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_drv.c +++ b/drivers/media/i2c/maxim4c/maxim4c_drv.c @@ -37,6 +37,11 @@ * 5. Fix unbalanced disabling for PoC regulator * 6. MIPI VC count does not affected by data lane count * + * V2.05.00 + * 1. local device power on add some delay for i2c normal access. + * 2. enable hot plug detect for partial links are locked. + * 3. remote device hot plug init disable lock irq. + * */ #include #include @@ -66,7 +71,7 @@ #include "maxim4c_api.h" -#define DRIVER_VERSION KERNEL_VERSION(2, 0x04, 0x04) +#define DRIVER_VERSION KERNEL_VERSION(2, 0x05, 0x00) #define MAXIM4C_XVCLK_FREQ 25000000 @@ -103,8 +108,8 @@ static int maxim4c_check_local_chipid(maxim4c_t *maxim4c) return 0; } } else { + // if chipid is unexpected, retry dev_err(dev, "Unexpected maxim chipid = %02x\n", chipid); - return -ENODEV; } } } @@ -135,7 +140,7 @@ static irqreturn_t maxim4c_hot_plug_detect_irq_handler(int irq, void *dev_id) queue_delayed_work(maxim4c->hot_plug_work.state_check_wq, &maxim4c->hot_plug_work.state_d_work, - msecs_to_jiffies(50)); + msecs_to_jiffies(100)); } mutex_unlock(&maxim4c->mutex); @@ -209,8 +214,14 @@ static void maxim4c_hot_plug_state_check_work(struct work_struct *work) if (curr_lock_state & MAXIM4C_LINK_MASK_A) { dev_info(dev, "Link A plug in\n"); + if (maxim4c->hot_plug_irq > 0) + disable_irq(maxim4c->hot_plug_irq); + maxim4c_remote_devices_init(maxim4c, MAXIM4C_LINK_MASK_A); + if (maxim4c->hot_plug_irq > 0) + enable_irq(maxim4c->hot_plug_irq); + maxim4c_video_pipe_linkid_enable(maxim4c, link_id, true); } else { dev_info(dev, "Link A plug out\n"); @@ -225,8 +236,14 @@ static void maxim4c_hot_plug_state_check_work(struct work_struct *work) if (curr_lock_state & MAXIM4C_LINK_MASK_B) { dev_info(dev, "Link B plug in\n"); + if (maxim4c->hot_plug_irq > 0) + disable_irq(maxim4c->hot_plug_irq); + maxim4c_remote_devices_init(maxim4c, MAXIM4C_LINK_MASK_B); + if (maxim4c->hot_plug_irq > 0) + enable_irq(maxim4c->hot_plug_irq); + maxim4c_video_pipe_linkid_enable(maxim4c, link_id, true); } else { dev_info(dev, "Link B plug out\n"); @@ -241,8 +258,14 @@ static void maxim4c_hot_plug_state_check_work(struct work_struct *work) if (curr_lock_state & MAXIM4C_LINK_MASK_C) { dev_info(dev, "Link C plug in\n"); + if (maxim4c->hot_plug_irq > 0) + disable_irq(maxim4c->hot_plug_irq); + maxim4c_remote_devices_init(maxim4c, MAXIM4C_LINK_MASK_C); + if (maxim4c->hot_plug_irq > 0) + enable_irq(maxim4c->hot_plug_irq); + maxim4c_video_pipe_linkid_enable(maxim4c, link_id, true); } else { dev_info(dev, "Link C plug out\n"); @@ -257,8 +280,14 @@ static void maxim4c_hot_plug_state_check_work(struct work_struct *work) if (curr_lock_state & MAXIM4C_LINK_MASK_D) { dev_info(dev, "Link D plug in\n"); + if (maxim4c->hot_plug_irq > 0) + disable_irq(maxim4c->hot_plug_irq); + maxim4c_remote_devices_init(maxim4c, MAXIM4C_LINK_MASK_D); + if (maxim4c->hot_plug_irq > 0) + enable_irq(maxim4c->hot_plug_irq); + maxim4c_video_pipe_linkid_enable(maxim4c, link_id, true); } else { dev_info(dev, "Link D plug out\n"); @@ -273,12 +302,34 @@ static void maxim4c_hot_plug_state_check_work(struct work_struct *work) } else { queue_delayed_work(maxim4c->hot_plug_work.state_check_wq, &maxim4c->hot_plug_work.state_d_work, - msecs_to_jiffies(100)); + msecs_to_jiffies(200)); } mutex_unlock(&maxim4c->mutex); } +int maxim4c_hot_plug_detect_work_start(maxim4c_t *maxim4c) +{ + struct device *dev = &maxim4c->client->dev; + u8 link_lock_state = 0, link_enable_mask = 0; + + link_lock_state = maxim4c->link_lock_state; + link_enable_mask = maxim4c->gmsl_link.link_enable_mask; + + if (link_lock_state != link_enable_mask) { + dev_info(dev, "%s: link_lock = 0x%02x, link_mask = 0x%02x\n", + __func__, link_lock_state, link_enable_mask); + + maxim4c->hot_plug_state = MAXIM4C_HOT_PLUG_OUT; + + queue_delayed_work(maxim4c->hot_plug_work.state_check_wq, + &maxim4c->hot_plug_work.state_d_work, + msecs_to_jiffies(200)); + } + + return 0; +} + static int maxim4c_lock_state_work_init(maxim4c_t *maxim4c) { struct device *dev = &maxim4c->client->dev; @@ -328,7 +379,7 @@ static int maxim4c_local_device_power_on(maxim4c_t *maxim4c) gpiod_set_value_cansleep(maxim4c->pwdn_gpio, 1); - usleep_range(5000, 10000); + usleep_range(10000, 11000); } return 0; @@ -646,6 +697,8 @@ static int maxim4c_probe(struct i2c_client *client, maxim4c->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); if (IS_ERR(maxim4c->pwdn_gpio)) dev_warn(dev, "Failed to get pwdn-gpios, maybe no use\n"); + else + usleep_range(1000, 1100); maxim4c->lock_gpio = devm_gpiod_get(dev, "lock", GPIOD_IN); if (IS_ERR(maxim4c->lock_gpio)) diff --git a/drivers/media/i2c/maxim4c/maxim4c_link.c b/drivers/media/i2c/maxim4c/maxim4c_link.c index 688dfe85abee..26f986edfb00 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_link.c +++ b/drivers/media/i2c/maxim4c/maxim4c_link.c @@ -463,7 +463,17 @@ int maxim4c_link_select_remote_enable(struct maxim4c *maxim4c, u8 link_mask) 0x0006, MAXIM4C_I2C_REG_ADDR_16BITS, link_enable, link_enable); - ret |= maxim4c_link_wait_linklock(maxim4c, link_mask); + if (ret) { + dev_err(dev, "%s: link oneshot reset or enable error, link mask = 0x%x\n", + __func__, link_mask); + return ret; + } + + maxim4c_link_wait_linklock(maxim4c, link_mask); + dev_info(dev, "link_mask = 0x%02x, link_lock = 0x%02x\n", + link_mask, maxim4c->link_lock_state); + + return 0; } return ret; diff --git a/drivers/media/i2c/maxim4c/maxim4c_v4l2.c b/drivers/media/i2c/maxim4c/maxim4c_v4l2.c index a331023da0d4..908045e89abf 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_v4l2.c +++ b/drivers/media/i2c/maxim4c/maxim4c_v4l2.c @@ -532,6 +532,11 @@ static int __maxim4c_start_stream(struct maxim4c *maxim4c) if (maxim4c->hot_plug_irq > 0) enable_irq(maxim4c->hot_plug_irq); + if (maxim4c->link_lock_state != maxim4c->gmsl_link.link_enable_mask) { + dev_info(dev, "partial links are locked, start hot plug detect work.\n"); + maxim4c_hot_plug_detect_work_start(maxim4c); + } + return 0; } diff --git a/drivers/media/i2c/maxim4c/remote_max96715.c b/drivers/media/i2c/maxim4c/remote_max96715.c index 53c44d70ec5f..11aa30831acc 100644 --- a/drivers/media/i2c/maxim4c/remote_max96715.c +++ b/drivers/media/i2c/maxim4c/remote_max96715.c @@ -190,7 +190,6 @@ 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 = maxim4c_remote_i2c_addr_select(max96715, MAXIM4C_I2C_SER_DEF); @@ -206,16 +205,9 @@ static int max96715_module_init(maxim4c_remote_t *max96715) 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); - + if (ret) return ret; - } #endif ret = maxim4c_i2c_run_init_seq(client, @@ -225,16 +217,11 @@ static int max96715_module_init(maxim4c_remote_t *max96715) 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