diff --git a/arch/arm64/boot/dts/rockchip/rk3576-vehicle-evb-maxim-max96712-dphy0.dtsi b/arch/arm64/boot/dts/rockchip/rk3576-vehicle-evb-maxim-max96712-dphy0.dtsi index 7f75d32d4fca..86391296e55b 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-vehicle-evb-maxim-max96712-dphy0.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576-vehicle-evb-maxim-max96712-dphy0.dtsi @@ -135,7 +135,6 @@ max-fps-denominator = <300000>; bpp = <16>; link-freq-idx = <20>; - vc-array = <0x10 0x20 0x40 0x80>; // VC0~3: bit4~7 }; /* support mode config end */ diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy0.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy0.dtsi index 673ff5a9fcbb..ac2e44ba0ed8 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy0.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy0.dtsi @@ -135,7 +135,6 @@ max-fps-denominator = <300000>; bpp = <16>; link-freq-idx = <24>; - vc-array = <0x10 0x20 0x40 0x80>; // VC0~3: bit4~7 }; /* support mode config end */ diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy1.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy1.dtsi index 1f3f2ee29ad4..09ef939e378b 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy1.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy1.dtsi @@ -135,7 +135,6 @@ max-fps-denominator = <300000>; bpp = <16>; link-freq-idx = <24>; - vc-array = <0x10 0x20 0x40 0x80>; // VC0~3: bit4~7 }; /* support mode config end */ diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy0.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy0.dtsi index 2472a3247ad9..32a78b4bd911 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy0.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy0.dtsi @@ -135,7 +135,6 @@ max-fps-denominator = <300000>; bpp = <16>; link-freq-idx = <20>; - vc-array = <0x10 0x20 0x40 0x80>; // VC0~3: bit4~7 }; /* support mode config end */ diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3-dummy.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3-dummy.dtsi index af3a69f9c89d..3e5394deaabc 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3-dummy.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3-dummy.dtsi @@ -135,7 +135,6 @@ max-fps-denominator = <300000>; bpp = <16>; link-freq-idx = <20>; - vc-array = <0x10 0x20 0x40 0x80>; // VC0~3: bit4~7 }; /* support mode config end */ diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3-os04a10.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3-os04a10.dtsi index aeba292020e9..7797329b63a7 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3-os04a10.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3-os04a10.dtsi @@ -144,7 +144,6 @@ vts-def = <0x0cb0>; bpp = <10>; link-freq-idx = <24>; - vc-array = <0x10 0x20 0x40 0x80>; // VC0~3: bit4~7 }; /* support mode config end */ diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3.dtsi index 7bd80caaba2d..0da1ada760a9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3.dtsi @@ -135,7 +135,6 @@ max-fps-denominator = <300000>; bpp = <16>; link-freq-idx = <20>; - vc-array = <0x10 0x20 0x40 0x80>; // VC0~3: bit4~7 }; /* support mode config end */ diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dcphy0.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dcphy0.dtsi index 665c4b2f0458..111d7968daaf 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dcphy0.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dcphy0.dtsi @@ -135,7 +135,6 @@ max-fps-denominator = <300000>; bpp = <16>; link-freq-idx = <24>; - vc-array = <0x10 0x20 0x40 0x80>; // VC0~3: bit4~7 }; /* support mode config end */ diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy0.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy0.dtsi index 908aac609958..62b65ab277f5 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy0.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy0.dtsi @@ -135,7 +135,6 @@ max-fps-denominator = <300000>; bpp = <16>; link-freq-idx = <20>; - vc-array = <0x10 0x20 0x40 0x80>; // VC0~3: bit4~7 }; /* support mode config end */ diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy3-dummy.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy3-dummy.dtsi index 5b0e27c6072f..2787893fe31a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy3-dummy.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy3-dummy.dtsi @@ -136,7 +136,6 @@ max-fps-denominator = <300000>; bpp = <16>; link-freq-idx = <20>; - vc-array = <0x10 0x20 0x40 0x80>; // VC0~3: bit4~7 }; /* support mode config end */ diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy3.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy3.dtsi index e4a2da22bb24..8983bb26000b 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy3.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy3.dtsi @@ -135,7 +135,6 @@ max-fps-denominator = <300000>; bpp = <16>; link-freq-idx = <20>; - vc-array = <0x10 0x20 0x40 0x80>; // VC0~3: bit4~7 }; /* support mode config end */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 4be03b33e8c1..04bcf57a8e94 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -9395,6 +9395,16 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_sta */ if (vop2->version == VOP_VERSION_RK3588) VOP_MODULE_SET(vop2, vp, dsp_background, 0x80000000); + + /* + * For RK3576 VP0 enable ACM[bypass = 0] will lead to timing error, + * so enable ACM by default. + */ + if (vop2->version <= VOP_VERSION_RK3576 && + vp_data->feature & VOP_FEATURE_POST_ACM) { + writel(0, vop2->acm_regs + RK3528_ACM_CTRL); + VOP_MODULE_SET(vop2, vp, acm_bypass_en, 0); + } if (is_vop3(vop2)) vop3_setup_pipe_dly(vp, NULL); diff --git a/drivers/media/i2c/lt6911uxe.c b/drivers/media/i2c/lt6911uxe.c index 3336f9fb00b1..524d7e120a97 100644 --- a/drivers/media/i2c/lt6911uxe.c +++ b/drivers/media/i2c/lt6911uxe.c @@ -13,6 +13,7 @@ * V0.0X01.0X04 * 1.fix some errors. * 2.add dphy timing reg. + * V0.0X01.0X05 add dual mipi mode support * */ // #define DEBUG @@ -40,7 +41,7 @@ #include #include -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x04) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x05) static int debug; module_param(debug, int, 0644); @@ -185,6 +186,8 @@ struct lt6911uxe { const char *len_name; const struct lt6911uxe_mode *cur_mode; const struct lt6911uxe_mode *support_modes; + struct rkmodule_multi_dev_info multi_dev_info; + struct rkmodule_csi_dphy_param dphy_param; u32 cfg_num; struct v4l2_fwnode_endpoint bus_cfg; bool nosignal; @@ -196,6 +199,7 @@ struct lt6911uxe { u32 module_index; u32 audio_sampling_rate; int lane_in_use; + bool dual_mipi_port; }; static const struct v4l2_dv_timings_cap lt6911uxe_timings_cap = { @@ -1090,12 +1094,6 @@ static int lt6911uxe_s_dv_timings(struct v4l2_subdev *sd, return 0; } - if (!v4l2_valid_dv_timings(timings, - <6911uxe_timings_cap, NULL, NULL)) { - v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); - return -ERANGE; - } - lt6911uxe->timings = *timings; enable_stream(sd, false); @@ -1375,6 +1373,7 @@ static long lt6911uxe_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); long ret = 0; struct rkmodule_csi_dphy_param *dphy_param; + struct rkmodule_capture_info *capture_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -1396,6 +1395,17 @@ static long lt6911uxe_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) dev_dbg(<6911uxe->i2c_client->dev, "sensor get dphy param\n"); break; + case RKMODULE_GET_CAPTURE_MODE: + capture_info = (struct rkmodule_capture_info *)arg; + if (lt6911uxe->dual_mipi_port) { + v4l2_dbg(1, debug, sd, "enable dual mipi mode\n"); + capture_info->mode = RKMODULE_MULTI_DEV_COMBINE_ONE; + capture_info->multi_dev = lt6911uxe->multi_dev_info; + } else { + capture_info->mode = 0; + capture_info->multi_dev = lt6911uxe->multi_dev_info; + } + break; default: ret = -ENOIOCTLCMD; break; @@ -1434,6 +1444,7 @@ static long lt6911uxe_compat_ioctl32(struct v4l2_subdev *sd, long ret; int *seq; struct rkmodule_csi_dphy_param *dphy_param; + struct rkmodule_capture_info *capture_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -1495,6 +1506,21 @@ static long lt6911uxe_compat_ioctl32(struct v4l2_subdev *sd, } kfree(dphy_param); break; + case RKMODULE_GET_CAPTURE_MODE: + capture_info = kzalloc(sizeof(*capture_info), GFP_KERNEL); + if (!capture_info) { + ret = -ENOMEM; + return ret; + } + + ret = lt6911uxe_ioctl(sd, cmd, capture_info); + if (!ret) { + ret = copy_to_user(up, capture_info, sizeof(*capture_info)); + if (ret) + ret = -EFAULT; + } + kfree(capture_info); + break; default: ret = -ENOIOCTLCMD; break; @@ -1763,6 +1789,39 @@ static int lt6911uxe_check_chip_id(struct lt6911uxe *lt6911uxe) return ret; } +static int lt6911uxe_get_multi_dev_info(struct lt6911uxe *lt6911uxe) +{ + struct device *dev = <6911uxe->i2c_client->dev; + struct device_node *node = dev->of_node; + struct device_node *multi_info_np; + + lt6911uxe->dual_mipi_port = false; + multi_info_np = of_get_child_by_name(node, "multi-dev-info"); + if (!multi_info_np) { + dev_info(dev, "failed to get multi dev info\n"); + return -EINVAL; + } + + of_property_read_u32(multi_info_np, "dev-idx-l", + <6911uxe->multi_dev_info.dev_idx[0]); + of_property_read_u32(multi_info_np, "dev-idx-r", + <6911uxe->multi_dev_info.dev_idx[1]); + of_property_read_u32(multi_info_np, "combine-idx", + <6911uxe->multi_dev_info.combine_idx[0]); + of_property_read_u32(multi_info_np, "pixel-offset", + <6911uxe->multi_dev_info.pixel_offset); + of_property_read_u32(multi_info_np, "dev-num", + <6911uxe->multi_dev_info.dev_num); + + lt6911uxe->dual_mipi_port = true; + dev_info(dev, + "multi dev left: mipi%d, multi dev right: mipi%d, combile mipi%d, dev num: %d\n", + lt6911uxe->multi_dev_info.dev_idx[0], lt6911uxe->multi_dev_info.dev_idx[1], + lt6911uxe->multi_dev_info.combine_idx[0], lt6911uxe->multi_dev_info.dev_num); + + return 0; +} + static int lt6911uxe_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1795,6 +1854,11 @@ static int lt6911uxe_probe(struct i2c_client *client, lt6911uxe->timings = default_timing; lt6911uxe->cur_mode = <6911uxe->support_modes[0]; + + err = lt6911uxe_get_multi_dev_info(lt6911uxe); + if (err) + v4l2_info(sd, "get multi dev info failed, not use dual mipi mode\n"); + err = lt6911uxe_check_chip_id(lt6911uxe); if (err < 0) return err; diff --git a/drivers/media/i2c/maxim/local/maxim2c/maxim2c_drv.c b/drivers/media/i2c/maxim/local/maxim2c/maxim2c_drv.c index b3ac21b502ee..ae4ebd53c28d 100644 --- a/drivers/media/i2c/maxim/local/maxim2c/maxim2c_drv.c +++ b/drivers/media/i2c/maxim/local/maxim2c/maxim2c_drv.c @@ -47,6 +47,11 @@ * 1. unified use __v4l2_ctrl_handler_setup in the xxx_start_stream * 2. support subscribe hot plug detect v4l2 event * + * V3.06.00 + * 1. support multi-channel information configuration + * 2. mode vc initialization when vc-array isn't configured + * 3. fix the issue of mutex deadlock during hot plug + * */ #include #include @@ -74,7 +79,7 @@ #include "maxim2c_api.h" -#define DRIVER_VERSION KERNEL_VERSION(3, 0x05, 0x00) +#define DRIVER_VERSION KERNEL_VERSION(3, 0x06, 0x00) #define MAXIM2C_NAME "maxim2c" @@ -143,24 +148,27 @@ static irqreturn_t maxim2c_hot_plug_detect_irq_handler(int irq, void *dev_id) int lock_gpio_level = 0; mutex_lock(&maxim2c->mutex); - if (maxim2c->streaming) { - lock_gpio_level = gpiod_get_value_cansleep(maxim2c->lock_gpio); - if (lock_gpio_level == 0) { - dev_info(dev, "serializer hot plug out\n"); + if (maxim2c->streaming == 0) { + mutex_unlock(&maxim2c->mutex); + return IRQ_HANDLED; + } - maxim2c->hot_plug_state = MAXIM2C_HOT_PLUG_OUT; - } else { - dev_info(dev, "serializer hot plug in\n"); + lock_gpio_level = gpiod_get_value_cansleep(maxim2c->lock_gpio); + if (lock_gpio_level == 0) { + dev_info(dev, "serializer hot plug out\n"); - maxim2c->hot_plug_state = MAXIM2C_HOT_PLUG_IN; - } + maxim2c->hot_plug_state = MAXIM2C_HOT_PLUG_OUT; + } else { + dev_info(dev, "serializer hot plug in\n"); - queue_delayed_work(maxim2c->hot_plug_work.state_check_wq, - &maxim2c->hot_plug_work.state_d_work, - msecs_to_jiffies(100)); + maxim2c->hot_plug_state = MAXIM2C_HOT_PLUG_IN; } mutex_unlock(&maxim2c->mutex); + queue_delayed_work(maxim2c->hot_plug_work.state_check_wq, + &maxim2c->hot_plug_work.state_d_work, + msecs_to_jiffies(100)); + return IRQ_HANDLED; } @@ -226,6 +234,7 @@ static void maxim2c_hot_plug_state_check_work(struct work_struct *work) maxim2c_hot_plug_event_report(maxim2c, curr_lock_state); maxim2c->link_lock_state = curr_lock_state; } + mutex_unlock(&maxim2c->mutex); if (link_lock_change & MAXIM2C_LINK_MASK_A) { link_id = MAXIM2C_LINK_ID_A; @@ -287,8 +296,6 @@ static void maxim2c_hot_plug_state_check_work(struct work_struct *work) &maxim2c->hot_plug_work.state_d_work, msecs_to_jiffies(200)); } - - mutex_unlock(&maxim2c->mutex); } int maxim2c_hot_plug_detect_work_start(maxim2c_t *maxim2c) diff --git a/drivers/media/i2c/maxim/local/maxim2c/maxim2c_drv.h b/drivers/media/i2c/maxim/local/maxim2c/maxim2c_drv.h index c7dc362bef75..b961cb0f0750 100644 --- a/drivers/media/i2c/maxim/local/maxim2c/maxim2c_drv.h +++ b/drivers/media/i2c/maxim/local/maxim2c/maxim2c_drv.h @@ -49,6 +49,21 @@ struct maxim2c_hot_plug_work { u32 hot_plug_state; }; +struct maxim2c_vc_info { + u32 enable; // 0: disable, !0: enable + + u32 width; + u32 height; + u32 bus_fmt; + + /* + * the following are optional parameters, user-defined data types + * default 0: invalid parameter + */ + u32 data_type; + u32 data_bit; +}; + struct maxim2c_mode { u32 width; u32 height; @@ -61,6 +76,7 @@ struct maxim2c_mode { u32 bpp; const struct regval *reg_list; u32 vc[PAD_MAX]; + struct maxim2c_vc_info vc_info[PAD_MAX]; struct v4l2_rect crop_rect; }; diff --git a/drivers/media/i2c/maxim/local/maxim2c/maxim2c_v4l2.c b/drivers/media/i2c/maxim/local/maxim2c/maxim2c_v4l2.c index fb20341e67c5..09f46e9db148 100644 --- a/drivers/media/i2c/maxim/local/maxim2c/maxim2c_v4l2.c +++ b/drivers/media/i2c/maxim/local/maxim2c/maxim2c_v4l2.c @@ -103,6 +103,7 @@ static int maxim2c_support_mode_init(maxim2c_t *maxim2c) struct device_node *node = NULL; struct maxim2c_mode *mode = NULL; u32 value = 0, vc_array[PAD_MAX], crop_array[4]; + struct maxim2c_vc_info vc_info[PAD_MAX]; int ret = 0, i = 0, array_size = 0; dev_info(dev, "=== maxim2c support mode init ===\n"); @@ -213,10 +214,67 @@ static int maxim2c_support_mode_init(maxim2c_t *maxim2c) dev_info(dev, "vc-array[%d] property: 0x%x\n", i, vc_array[i]); mode->vc[i] = vc_array[i]; } + } else { + /* default vc config */ +#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE + for (i = 0; i < PAD_MAX; i++) + mode->vc[i] = i; +#else + switch (PAD_MAX) { + case 4: + mode->vc[3] = V4L2_MBUS_CSI2_CHANNEL_3; + fallthrough; + case 3: + mode->vc[2] = V4L2_MBUS_CSI2_CHANNEL_2; + fallthrough; + case 2: + mode->vc[1] = V4L2_MBUS_CSI2_CHANNEL_1; + fallthrough; + case 1: + default: + mode->vc[0] = V4L2_MBUS_CSI2_CHANNEL_0; + break; + } +#endif } for (i = 0; i < PAD_MAX; i++) dev_info(dev, "support mode: vc[%d] = 0x%x\n", i, mode->vc[i]); + /* vc info */ + array_size = of_property_count_u32_elems(node, "vc-info"); + if ((array_size > 0) && + (array_size % sizeof(struct maxim2c_vc_info) == 0) && + (array_size <= sizeof(struct maxim2c_vc_info) * PAD_MAX)) { + + memset((char *)vc_info, 0, sizeof(vc_info)); + + ret = of_property_read_u32_array(node, "vc-info", (u32 *)vc_info, array_size); + if (ret == 0) { + /* */ + for (i = 0; i < PAD_MAX; i++) { + dev_info(dev, "vc-info[%d] property:\n", i); + dev_info(dev, " vc-info[%d].enable = %d:\n", i, vc_info[i].enable); + + dev_info(dev, " vc-info[%d].width = %d:\n", i, vc_info[i].width); + dev_info(dev, " vc-info[%d].height = %d:\n", i, vc_info[i].height); + dev_info(dev, " vc-info[%d].bus_fmt = %d:\n", i, vc_info[i].bus_fmt); + + dev_info(dev, " vc-info[%d].data_type = %d:\n", i, vc_info[i].data_type); + dev_info(dev, " vc-info[%d].data_bit = %d:\n", i, vc_info[i].data_bit); + + mode->vc_info[i].enable = vc_info[i].enable; + + mode->vc_info[i].width = vc_info[i].width; + mode->vc_info[i].height = vc_info[i].height; + mode->vc_info[i].bus_fmt = vc_info[i].bus_fmt; + + mode->vc_info[i].data_type = vc_info[i].data_type; + mode->vc_info[i].data_bit = vc_info[i].data_bit; + + } + } + } + /* crop rect */ array_size = of_property_read_variable_u32_array(node, "crop-rect", crop_array, 1, 4); @@ -332,11 +390,52 @@ static void maxim2c_set_vicap_rst_inf(maxim2c_t *maxim2c, maxim2c->is_reset = rst_info.is_reset; } +static int maxim2c_get_channel_info(maxim2c_t *maxim2c, struct rkmodule_channel_info *ch_info) +{ + const struct maxim2c_mode *mode = maxim2c->cur_mode; + struct device *dev = &maxim2c->client->dev; + + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + + if (mode->vc_info[ch_info->index].enable) { + ch_info->vc = mode->vc[ch_info->index]; + + ch_info->width = mode->vc_info[ch_info->index].width; + ch_info->height = mode->vc_info[ch_info->index].height; + ch_info->bus_fmt = mode->vc_info[ch_info->index].bus_fmt; + + /* optional parameters, default 0: invalid parameter */ + ch_info->data_type = mode->vc_info[ch_info->index].data_type; + ch_info->data_bit = mode->vc_info[ch_info->index].data_bit; + } else { + ch_info->vc = mode->vc[ch_info->index]; + + ch_info->width = mode->width; + ch_info->height = mode->height; + ch_info->bus_fmt = mode->bus_fmt; + } + + dev_info(dev, "get channel info, ch_info->index = %d\n", ch_info->index); + + dev_info(dev, " ch_info->vc = 0x%x\n", ch_info->vc); + + dev_info(dev, " ch_info->width = %d\n", ch_info->width); + dev_info(dev, " ch_info->height = %d\n", ch_info->height); + dev_info(dev, " ch_info->bus_fmt = 0x%x\n", ch_info->bus_fmt); + + dev_info(dev, " ch_info->data_type = 0x%x:\n", ch_info->data_type); + dev_info(dev, " ch_info->data_bit = %d\n", ch_info->data_bit); + + return 0; +} + static long maxim2c_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { maxim2c_t *maxim2c = v4l2_get_subdevdata(sd); struct rkmodule_csi_dphy_param *dphy_param; struct rkmodule_capture_info *capture_info; + struct rkmodule_channel_info *ch_info; long ret = 0; dev_dbg(&maxim2c->client->dev, "ioctl cmd = 0x%08x\n", cmd); @@ -370,6 +469,10 @@ static long maxim2c_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) else capture_info->mode = RKMODULE_CAPTURE_MODE_NONE; break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = maxim2c_get_channel_info(maxim2c, ch_info); + break; default: ret = -ENOIOCTLCMD; break; @@ -387,6 +490,7 @@ static long maxim2c_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd, struct rkmodule_vicap_reset_info *vicap_rst_inf; struct rkmodule_csi_dphy_param *dphy_param; struct rkmodule_capture_info *capture_info; + struct rkmodule_channel_info *ch_info; long ret = 0; switch (cmd) { @@ -479,6 +583,21 @@ static long maxim2c_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd, } kfree(capture_info); break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + + ret = maxim2c_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; default: ret = -ENOIOCTLCMD; break; @@ -892,8 +1011,17 @@ static int maxim2c_get_selection(struct v4l2_subdev *sd, #endif { maxim2c_t *maxim2c = v4l2_get_subdevdata(sd); + int i = 0; if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + /* if multiple channel info enable, get_selection isn't support */ + for (i = 0; i < PAD_MAX; i++) { + if (maxim2c->cur_mode->vc_info[i].enable) { + v4l2_warn(sd, "Multi-channel enable, get_selection isn't support\n"); + return -EINVAL; + } + } + sel->r.left = maxim2c->cur_mode->crop_rect.left; sel->r.width = maxim2c->cur_mode->crop_rect.width; sel->r.top = maxim2c->cur_mode->crop_rect.top; diff --git a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.c b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.c index 18ff79e79cd4..11a2c49324d8 100644 --- a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.c +++ b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.c @@ -70,6 +70,11 @@ * 1. unified use __v4l2_ctrl_handler_setup in the xxx_start_stream * 2. support subscribe hot plug detect v4l2 event * + * V3.06.00 + * 1. support multi-channel information configuration + * 2. mode vc initialization when vc-array isn't configured + * 3. fix the issue of mutex deadlock during hot plug + * */ #include #include @@ -97,7 +102,7 @@ #include "maxim4c_api.h" -#define DRIVER_VERSION KERNEL_VERSION(3, 0x05, 0x00) +#define DRIVER_VERSION KERNEL_VERSION(3, 0x06, 0x00) #define MAXIM4C_NAME "maxim4c" @@ -166,24 +171,27 @@ static irqreturn_t maxim4c_hot_plug_detect_irq_handler(int irq, void *dev_id) 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"); + if (maxim4c->streaming == 0) { + mutex_unlock(&maxim4c->mutex); + return IRQ_HANDLED; + } - maxim4c->hot_plug_state = MAXIM4C_HOT_PLUG_OUT; - } else { - dev_info(dev, "serializer hot plug in\n"); + 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_IN; - } + maxim4c->hot_plug_state = MAXIM4C_HOT_PLUG_OUT; + } else { + dev_info(dev, "serializer hot plug in\n"); - queue_delayed_work(maxim4c->hot_plug_work.state_check_wq, - &maxim4c->hot_plug_work.state_d_work, - msecs_to_jiffies(100)); + maxim4c->hot_plug_state = MAXIM4C_HOT_PLUG_IN; } mutex_unlock(&maxim4c->mutex); + queue_delayed_work(maxim4c->hot_plug_work.state_check_wq, + &maxim4c->hot_plug_work.state_d_work, + msecs_to_jiffies(100)); + return IRQ_HANDLED; } @@ -249,6 +257,7 @@ static void maxim4c_hot_plug_state_check_work(struct work_struct *work) maxim4c_hot_plug_event_report(maxim4c, curr_lock_state); maxim4c->link_lock_state = curr_lock_state; } + mutex_unlock(&maxim4c->mutex); if (link_lock_change & MAXIM4C_LINK_MASK_A) { link_id = MAXIM4C_LINK_ID_A; @@ -362,8 +371,6 @@ static void maxim4c_hot_plug_state_check_work(struct work_struct *work) &maxim4c->hot_plug_work.state_d_work, msecs_to_jiffies(200)); } - - mutex_unlock(&maxim4c->mutex); } int maxim4c_hot_plug_detect_work_start(maxim4c_t *maxim4c) diff --git a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.h b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.h index 2ecea27f22bd..18ec7828d0f7 100644 --- a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.h +++ b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.h @@ -49,6 +49,21 @@ struct maxim4c_hot_plug_work { u32 hot_plug_state; }; +struct maxim4c_vc_info { + u32 enable; // 0: disable, !0: enable + + u32 width; + u32 height; + u32 bus_fmt; + + /* + * the following are optional parameters, user-defined data types + * default 0: invalid parameter + */ + u32 data_type; + u32 data_bit; +}; + struct maxim4c_mode { u32 width; u32 height; @@ -61,6 +76,7 @@ struct maxim4c_mode { u32 bpp; const struct regval *reg_list; u32 vc[PAD_MAX]; + struct maxim4c_vc_info vc_info[PAD_MAX]; struct v4l2_rect crop_rect; }; diff --git a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_v4l2.c b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_v4l2.c index 7ad74a107bb7..c0906470039b 100644 --- a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_v4l2.c +++ b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_v4l2.c @@ -103,6 +103,7 @@ static int maxim4c_support_mode_init(maxim4c_t *maxim4c) struct device_node *node = NULL; struct maxim4c_mode *mode = NULL; u32 value = 0, vc_array[PAD_MAX], crop_array[4]; + struct maxim4c_vc_info vc_info[PAD_MAX]; int ret = 0, i = 0, array_size = 0; dev_info(dev, "=== maxim4c support mode init ===\n"); @@ -213,10 +214,67 @@ static int maxim4c_support_mode_init(maxim4c_t *maxim4c) dev_info(dev, "vc-array[%d] property: 0x%x\n", i, vc_array[i]); mode->vc[i] = vc_array[i]; } + } else { + /* default vc config */ +#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE + for (i = 0; i < PAD_MAX; i++) + mode->vc[i] = i; +#else + switch (PAD_MAX) { + case 4: + mode->vc[3] = V4L2_MBUS_CSI2_CHANNEL_3; + fallthrough; + case 3: + mode->vc[2] = V4L2_MBUS_CSI2_CHANNEL_2; + fallthrough; + case 2: + mode->vc[1] = V4L2_MBUS_CSI2_CHANNEL_1; + fallthrough; + case 1: + default: + mode->vc[0] = V4L2_MBUS_CSI2_CHANNEL_0; + break; + } +#endif } for (i = 0; i < PAD_MAX; i++) dev_info(dev, "support mode: vc[%d] = 0x%x\n", i, mode->vc[i]); + /* vc info */ + array_size = of_property_count_u32_elems(node, "vc-info"); + if ((array_size > 0) && + (array_size % sizeof(struct maxim4c_vc_info) == 0) && + (array_size <= sizeof(struct maxim4c_vc_info) * PAD_MAX)) { + + memset((char *)vc_info, 0, sizeof(vc_info)); + + ret = of_property_read_u32_array(node, "vc-info", (u32 *)vc_info, array_size); + if (ret == 0) { + /* */ + for (i = 0; i < PAD_MAX; i++) { + dev_info(dev, "vc-info[%d] property:\n", i); + dev_info(dev, " vc-info[%d].enable = %d:\n", i, vc_info[i].enable); + + dev_info(dev, " vc-info[%d].width = %d:\n", i, vc_info[i].width); + dev_info(dev, " vc-info[%d].height = %d:\n", i, vc_info[i].height); + dev_info(dev, " vc-info[%d].bus_fmt = %d:\n", i, vc_info[i].bus_fmt); + + dev_info(dev, " vc-info[%d].data_type = %d:\n", i, vc_info[i].data_type); + dev_info(dev, " vc-info[%d].data_bit = %d:\n", i, vc_info[i].data_bit); + + mode->vc_info[i].enable = vc_info[i].enable; + + mode->vc_info[i].width = vc_info[i].width; + mode->vc_info[i].height = vc_info[i].height; + mode->vc_info[i].bus_fmt = vc_info[i].bus_fmt; + + mode->vc_info[i].data_type = vc_info[i].data_type; + mode->vc_info[i].data_bit = vc_info[i].data_bit; + + } + } + } + /* crop rect */ array_size = of_property_read_variable_u32_array(node, "crop-rect", crop_array, 1, 4); @@ -332,11 +390,52 @@ static void maxim4c_set_vicap_rst_inf(maxim4c_t *maxim4c, maxim4c->is_reset = rst_info.is_reset; } +static int maxim4c_get_channel_info(maxim4c_t *maxim4c, struct rkmodule_channel_info *ch_info) +{ + const struct maxim4c_mode *mode = maxim4c->cur_mode; + struct device *dev = &maxim4c->client->dev; + + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + + if (mode->vc_info[ch_info->index].enable) { + ch_info->vc = mode->vc[ch_info->index]; + + ch_info->width = mode->vc_info[ch_info->index].width; + ch_info->height = mode->vc_info[ch_info->index].height; + ch_info->bus_fmt = mode->vc_info[ch_info->index].bus_fmt; + + /* optional parameters, default 0: invalid parameter */ + ch_info->data_type = mode->vc_info[ch_info->index].data_type; + ch_info->data_bit = mode->vc_info[ch_info->index].data_bit; + } else { + ch_info->vc = mode->vc[ch_info->index]; + + ch_info->width = mode->width; + ch_info->height = mode->height; + ch_info->bus_fmt = mode->bus_fmt; + } + + dev_info(dev, "get channel info, ch_info->index = %d\n", ch_info->index); + + dev_info(dev, " ch_info->vc = 0x%x\n", ch_info->vc); + + dev_info(dev, " ch_info->width = %d\n", ch_info->width); + dev_info(dev, " ch_info->height = %d\n", ch_info->height); + dev_info(dev, " ch_info->bus_fmt = 0x%x\n", ch_info->bus_fmt); + + dev_info(dev, " ch_info->data_type = 0x%x:\n", ch_info->data_type); + dev_info(dev, " ch_info->data_bit = %d\n", ch_info->data_bit); + + return 0; +} + static long maxim4c_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { maxim4c_t *maxim4c = v4l2_get_subdevdata(sd); struct rkmodule_csi_dphy_param *dphy_param; struct rkmodule_capture_info *capture_info; + struct rkmodule_channel_info *ch_info; long ret = 0; dev_dbg(&maxim4c->client->dev, "ioctl cmd = 0x%08x\n", cmd); @@ -370,6 +469,10 @@ static long maxim4c_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) else capture_info->mode = RKMODULE_CAPTURE_MODE_NONE; break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = maxim4c_get_channel_info(maxim4c, ch_info); + break; default: ret = -ENOIOCTLCMD; break; @@ -387,6 +490,7 @@ static long maxim4c_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd, struct rkmodule_vicap_reset_info *vicap_rst_inf; struct rkmodule_csi_dphy_param *dphy_param; struct rkmodule_capture_info *capture_info; + struct rkmodule_channel_info *ch_info; long ret = 0; switch (cmd) { @@ -479,6 +583,21 @@ static long maxim4c_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd, } kfree(capture_info); break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + + ret = maxim4c_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; default: ret = -ENOIOCTLCMD; break; @@ -892,8 +1011,18 @@ static int maxim4c_get_selection(struct v4l2_subdev *sd, #endif { maxim4c_t *maxim4c = v4l2_get_subdevdata(sd); + int i = 0; if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + /* if multiple channel info enable, get_selection isn't support */ + for (i = 0; i < PAD_MAX; i++) { + if (maxim4c->cur_mode->vc_info[i].enable) { + v4l2_warn(sd, + "Multi-channel enable, get_selection isn't support\n"); + return -EINVAL; + } + } + sel->r.left = maxim4c->cur_mode->crop_rect.left; sel->r.width = maxim4c->cur_mode->crop_rect.width; sel->r.top = maxim4c->cur_mode->crop_rect.top; diff --git a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c index d54e17e56a8b..2350333aa333 100644 --- a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c +++ b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c @@ -796,8 +796,8 @@ static void hdmirx_get_color_space(struct rk_hdmirx_dev *hdmirx_dev) */ hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PH2_1); val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PB3_0); - EC2_0 = (val & EXTEND_COLORIMETRY) >> 20; - C1_C0 = (val & COLORIMETRY_MASK) >> 14; + EC2_0 = (val & EXTEND_COLORIMETRY) >> 28; + C1_C0 = (val & COLORIMETRY_MASK) >> 22; if (hdmirx_dev->pix_fmt == HDMIRX_RGB888) { if (EC2_0 == HDMIRX_ADOBE_RGB || EC2_0 == HDMIRX_BT2020_RGB_OR_YCC) @@ -827,14 +827,14 @@ static void hdmirx_get_color_space(struct rk_hdmirx_dev *hdmirx_dev) static bool IsColorRangeLimitFormat(uint32_t width, uint32_t height, bool interlace) { - if (((width == 720) && (height == 240) && (interlace == false)) \ - || ((width == 720) && (height == 1201) && (interlace == false)) \ - || ((width == 720) && (height == 480) && (interlace == true)) \ - || ((width == 720) && (height == 576) && (interlace == true)) \ - || ((width == 1440) && (height == 480) && (interlace == true)) \ - || ((width == 1440) && (height == 576) && (interlace == true)) \ - || ((width == 1920) && (height == 1080) && (interlace == true)) \ - || ((width == 2880) && (height == 480) && (interlace == true)) \ + if (((width == 720) && (height == 240) && (interlace == false)) + || ((width == 720) && (height == 1201) && (interlace == false)) + || ((width == 720) && (height == 480) && (interlace == true)) + || ((width == 720) && (height == 576) && (interlace == true)) + || ((width == 1440) && (height == 480) && (interlace == true)) + || ((width == 1440) && (height == 576) && (interlace == true)) + || ((width == 1920) && (height == 1080) && (interlace == true)) + || ((width == 2880) && (height == 480) && (interlace == true)) || ((width == 3840) && (height == 2160) && (interlace == false))) { return true; } else {