From 28a26c7b729986a62c3ad6bfb0ba587cd6e26363 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Tue, 13 Sep 2022 08:42:43 +0000 Subject: [PATCH 01/55] media: i2c: lt6911uxc: add 4K60 dual mipi port support Signed-off-by: Jianwei Fan Change-Id: Ib70ec05ed521b924f5e10343a3399d629ee40dbe --- drivers/media/i2c/lt6911uxc.c | 224 ++++++++++++++++++++++++++-------- drivers/media/i2c/lt6911uxc.h | 5 + 2 files changed, 177 insertions(+), 52 deletions(-) diff --git a/drivers/media/i2c/lt6911uxc.c b/drivers/media/i2c/lt6911uxc.c index c801d293ecae..042c094ca284 100644 --- a/drivers/media/i2c/lt6911uxc.c +++ b/drivers/media/i2c/lt6911uxc.c @@ -6,6 +6,8 @@ * V0.0X01.0X00 first version. * V0.0X01.0X01 fix if plugin_gpio was not used. * V0.0X01.0X02 modify driver init level to late_initcall. + * V0.0X01.0X03 add 4K60 dual mipi support + * */ #include @@ -34,12 +36,16 @@ #include #include "lt6911uxc.h" -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x2) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x3) #define LT6911UXC_NAME "LT6911UXC" -#define LT6911UXC_LINK_FREQ_HIGH 400000000 -#define LT6911UXC_LINK_FREQ_LOW 200000000 -#define LT6911UXC_PIXEL_RATE 400000000 +#define LT6911UXC_LINK_FREQ_600M 600000000 +#define LT6911UXC_LINK_FREQ_400M 400000000 +#define LT6911UXC_LINK_FREQ_300M 300000000 +#define LT6911UXC_LINK_FREQ_200M 200000000 +#define LT6911UXC_LINK_FREQ_100M 100000000 +#define LT6911UXC_LINK_FREQ_60M 60000000 +#define LT6911UXC_PIXEL_RATE 600000000 #define I2C_MAX_XFER_SIZE 128 @@ -54,8 +60,12 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-2)"); static const s64 link_freq_menu_items[] = { - LT6911UXC_LINK_FREQ_HIGH, - LT6911UXC_LINK_FREQ_LOW, + LT6911UXC_LINK_FREQ_600M, + LT6911UXC_LINK_FREQ_400M, + LT6911UXC_LINK_FREQ_300M, + LT6911UXC_LINK_FREQ_200M, + LT6911UXC_LINK_FREQ_100M, + LT6911UXC_LINK_FREQ_60M, }; struct lt6911uxc { @@ -78,6 +88,7 @@ struct lt6911uxc { struct v4l2_dv_timings timings; struct v4l2_fwnode_bus_mipi_csi2 bus; struct v4l2_subdev sd; + struct rkmodule_multi_dev_info multi_dev_info; const char *len_name; const char *module_facing; const char *module_name; @@ -100,13 +111,14 @@ struct lt6911uxc_mode { u32 hts_def; u32 vts_def; u32 exp_def; + u32 mipi_freq_idx; }; static const struct v4l2_dv_timings_cap lt6911uxc_timings_cap = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 400000000, + V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 600000000, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, V4L2_DV_BT_CAP_PROGRESSIVE | @@ -117,6 +129,16 @@ static const struct v4l2_dv_timings_cap lt6911uxc_timings_cap = { static const struct lt6911uxc_mode supported_modes[] = { { + .width = 3840, + .height = 2160, + .max_fps = { + .numerator = 10000, + .denominator = 600000, + }, + .hts_def = 4400, + .vts_def = 2250, + .mipi_freq_idx = 0, + }, { .width = 3840, .height = 2160, .max_fps = { @@ -125,6 +147,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 4400, .vts_def = 2250, + .mipi_freq_idx = 0, }, { .width = 1920, .height = 1080, @@ -134,6 +157,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 2200, .vts_def = 1125, + .mipi_freq_idx = 2, }, { .width = 1920, .height = 540, @@ -141,6 +165,7 @@ static const struct lt6911uxc_mode supported_modes[] = { .numerator = 10000, .denominator = 600000, }, + .mipi_freq_idx = 3, }, { .width = 1440, .height = 240, @@ -148,6 +173,7 @@ static const struct lt6911uxc_mode supported_modes[] = { .numerator = 10000, .denominator = 600000, }, + .mipi_freq_idx = 4, }, { .width = 1440, .height = 288, @@ -155,6 +181,7 @@ static const struct lt6911uxc_mode supported_modes[] = { .numerator = 10000, .denominator = 500000, }, + .mipi_freq_idx = 4, }, { .width = 1280, .height = 720, @@ -164,6 +191,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 1650, .vts_def = 750, + .mipi_freq_idx = 3, }, { .width = 720, .height = 576, @@ -173,6 +201,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 864, .vts_def = 625, + .mipi_freq_idx = 5, }, { .width = 720, .height = 480, @@ -182,6 +211,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 858, .vts_def = 525, + .mipi_freq_idx = 5, }, }; @@ -390,6 +420,7 @@ static int lt6911uxc_get_detected_timings(struct v4l2_subdev *sd, u8 value, val_h, val_l; u32 fw_ver, mipi_byte_clk, mipi_bitrate; u8 fw_a, fw_b, fw_c, fw_d, lanes; + u8 video_fmt; int ret; memset(timings, 0, sizeof(struct v4l2_dv_timings)); @@ -421,13 +452,15 @@ static int lt6911uxc_get_detected_timings(struct v4l2_subdev *sd, i2c_rd8(sd, MIPI_LANES, &lanes); lt6911uxc->csi_lanes_in_use = lanes; + if (lt6911uxc->csi_lanes_in_use == 8) + v4l2_info(sd, "get 8 lane in use, set dual mipi mode\n"); i2c_wr8(sd, FM1_DET_CLK_SRC_SEL, AD_LMTX_WRITE_CLK); i2c_rd8(sd, FREQ_METER_H, &clk_h); i2c_rd8(sd, FREQ_METER_M, &clk_m); i2c_rd8(sd, FREQ_METER_L, &clk_l); mipi_byte_clk = (((clk_h & 0xf) << 16) | (clk_m << 8) | clk_l); mipi_bitrate = mipi_byte_clk * 8 / 1000; - v4l2_info(sd, "MIPI Byte clk: %dKHz, MIPI bitrate: %dMbps, lanes:%d\n", + v4l2_info(sd, "MIPI Byte clk: %uKHz, MIPI bitrate: %uMbps, lanes:%d\n", mipi_byte_clk, mipi_bitrate, lanes); i2c_rd8(sd, HTOTAL_H, &val_h); @@ -457,8 +490,16 @@ static int lt6911uxc_get_detected_timings(struct v4l2_subdev *sd, hbp = ((val_h << 8) | val_l) * 2; i2c_rd8(sd, VBP, &value); vbp = value; + i2c_rd8(sd, COLOR_FMT_STATUS, &video_fmt); + video_fmt = (video_fmt & GENMASK(6, 5)) >> 5; lt6911uxc_i2c_disable(sd); + if (video_fmt == 0x3) { + lt6911uxc->nosignal = true; + v4l2_err(sd, "%s ERROR: HDMI input YUV420, don't support YUV420!\n", __func__); + return -EINVAL; + } + if (!lt6911uxc_rcv_supported_res(sd, hact, vact)) { lt6911uxc->nosignal = true; v4l2_err(sd, "%s: rcv err res, return no signal!\n", __func__); @@ -694,7 +735,6 @@ static int lt6911uxc_s_dv_timings(struct v4l2_subdev *sd, } lt6911uxc->timings = *timings; - enable_stream(sd, false); return 0; @@ -771,7 +811,9 @@ static int lt6911uxc_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad, case 4: cfg->flags |= V4L2_MBUS_CSI2_4_LANE; break; - + case 8: + cfg->flags |= V4L2_MBUS_CSI2_4_LANE; + break; default: return -EINVAL; } @@ -836,11 +878,50 @@ static int lt6911uxc_enum_frame_interval(struct v4l2_subdev *sd, return 0; } +static int lt6911uxc_get_reso_dist(const struct lt6911uxc_mode *mode, + struct v4l2_dv_timings *timings) +{ + struct v4l2_bt_timings *bt = &timings->bt; + u32 cur_fps, dist_fps; + + cur_fps = fps_calc(bt); + dist_fps = DIV_ROUND_CLOSEST(mode->max_fps.denominator, mode->max_fps.numerator); + + return abs(mode->width - bt->width) + + abs(mode->height - bt->height) + abs(dist_fps - cur_fps); +} + +static const struct lt6911uxc_mode * +lt6911uxc_find_best_fit(struct lt6911uxc *lt6911uxc) +{ + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = lt6911uxc_get_reso_dist(&supported_modes[i], <6911uxc->timings); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + dev_info(<6911uxc->i2c_client->dev, + "find current mode: support_mode[%d], %dx%d@%dfps\n", + cur_best_fit, supported_modes[cur_best_fit].width, + supported_modes[cur_best_fit].height, + DIV_ROUND_CLOSEST(supported_modes[cur_best_fit].max_fps.denominator, + supported_modes[cur_best_fit].max_fps.numerator)); + + return &supported_modes[cur_best_fit]; +} + static int lt6911uxc_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct lt6911uxc *lt6911uxc = to_state(sd); + const struct lt6911uxc_mode *mode; mutex_lock(<6911uxc->confctl_mutex); format->format.code = lt6911uxc->mbus_fmt_code; @@ -850,6 +931,14 @@ static int lt6911uxc_get_fmt(struct v4l2_subdev *sd, lt6911uxc->timings.bt.interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; format->format.colorspace = V4L2_COLORSPACE_SRGB; + + mode = lt6911uxc_find_best_fit(lt6911uxc); + lt6911uxc->cur_mode = mode; + __v4l2_ctrl_s_ctrl_int64(lt6911uxc->pixel_rate, + LT6911UXC_PIXEL_RATE); + __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, + mode->mipi_freq_idx); + mutex_unlock(<6911uxc->confctl_mutex); v4l2_dbg(1, debug, sd, "%s: fmt code:%d, w:%d, h:%d, field mode:%s\n", @@ -859,40 +948,12 @@ static int lt6911uxc_get_fmt(struct v4l2_subdev *sd, return 0; } -static int lt6911uxc_get_reso_dist(const struct lt6911uxc_mode *mode, - struct v4l2_mbus_framefmt *framefmt) -{ - return abs(mode->width - framefmt->width) + - abs(mode->height - framefmt->height); -} - -static const struct lt6911uxc_mode * -lt6911uxc_find_best_fit(struct v4l2_subdev_format *fmt) -{ - struct v4l2_mbus_framefmt *framefmt = &fmt->format; - int dist; - int cur_best_fit = 0; - int cur_best_fit_dist = -1; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { - dist = lt6911uxc_get_reso_dist(&supported_modes[i], framefmt); - if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { - cur_best_fit_dist = dist; - cur_best_fit = i; - } - } - - return &supported_modes[cur_best_fit]; -} - static int lt6911uxc_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct lt6911uxc *lt6911uxc = to_state(sd); const struct lt6911uxc_mode *mode; - int index; /* is overwritten by get_fmt */ u32 code = format->format.code; @@ -915,20 +976,10 @@ static int lt6911uxc_set_fmt(struct v4l2_subdev *sd, return 0; lt6911uxc->mbus_fmt_code = format->format.code; - mode = lt6911uxc_find_best_fit(format); + mode = lt6911uxc_find_best_fit(lt6911uxc); lt6911uxc->cur_mode = mode; enable_stream(sd, false); - if (((mode->width == 720) && (mode->height == 576)) || - ((mode->width == 720) && (mode->height == 480))) - index = 1; - else - index = 0; - - __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, index); - v4l2_dbg(1, debug, sd, "%s res wxh:%dx%d, link freq:%llu", __func__, - mode->width, mode->height, link_freq_menu_items[index]); - return 0; } @@ -957,7 +1008,9 @@ static void lt6911uxc_get_module_inf(struct lt6911uxc *lt6911uxc, static long lt6911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct lt6911uxc *lt6911uxc = to_state(sd); + struct device *dev = <6911uxc->i2c_client->dev; long ret = 0; + struct rkmodule_capture_info *capture_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -966,6 +1019,16 @@ static long lt6911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) case RKMODULE_GET_HDMI_MODE: *(int *)arg = RKMODULE_HDMIIN_MODE; break; + case RKMODULE_GET_CAPTURE_MODE: + capture_info = (struct rkmodule_capture_info *)arg; + if (lt6911uxc->csi_lanes_in_use == 8) { + dev_info(dev, "8 lanes in use, set dual mipi mode\n"); + capture_info->mode = RKMODULE_MULTI_DEV_COMBINE_ONE; + capture_info->multi_dev = lt6911uxc->multi_dev_info; + } else { + capture_info->mode = 0; + } + break; default: ret = -ENOIOCTLCMD; break; @@ -982,6 +1045,7 @@ static long lt6911uxc_compat_ioctl32(struct v4l2_subdev *sd, struct rkmodule_inf *inf; long ret; int *seq; + struct rkmodule_capture_info *capture_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -1014,6 +1078,21 @@ static long lt6911uxc_compat_ioctl32(struct v4l2_subdev *sd, } kfree(seq); break; + case RKMODULE_GET_CAPTURE_MODE: + capture_info = kzalloc(sizeof(*capture_info), GFP_KERNEL); + if (!capture_info) { + ret = -ENOMEM; + return ret; + } + + ret = lt6911uxc_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; @@ -1093,9 +1172,11 @@ static void lt6911uxc_reset(struct lt6911uxc *lt6911uxc) static int lt6911uxc_init_v4l2_ctrls(struct lt6911uxc *lt6911uxc) { + const struct lt6911uxc_mode *mode; struct v4l2_subdev *sd; int ret; + mode = lt6911uxc->cur_mode; sd = <6911uxc->sd; ret = v4l2_ctrl_handler_init(<6911uxc->hdl, 5); if (ret) @@ -1105,8 +1186,9 @@ static int lt6911uxc_init_v4l2_ctrls(struct lt6911uxc *lt6911uxc) V4L2_CID_LINK_FREQ, ARRAY_SIZE(link_freq_menu_items) - 1, 0, link_freq_menu_items); - v4l2_ctrl_new_std(<6911uxc->hdl, NULL, V4L2_CID_PIXEL_RATE, - 0, LT6911UXC_PIXEL_RATE, 1, LT6911UXC_PIXEL_RATE); + lt6911uxc->pixel_rate = v4l2_ctrl_new_std(<6911uxc->hdl, NULL, + V4L2_CID_PIXEL_RATE, + 0, LT6911UXC_PIXEL_RATE, 1, LT6911UXC_PIXEL_RATE); lt6911uxc->detect_tx_5v_ctrl = v4l2_ctrl_new_std(<6911uxc->hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT, @@ -1125,6 +1207,9 @@ static int lt6911uxc_init_v4l2_ctrls(struct lt6911uxc *lt6911uxc) return ret; } + __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, mode->mipi_freq_idx); + __v4l2_ctrl_s_ctrl_int64(lt6911uxc->pixel_rate, LT6911UXC_PIXEL_RATE); + if (lt6911uxc_update_controls(sd)) { ret = -ENODEV; v4l2_err(sd, "update v4l2 ctrls failed! ret:%d\n", ret); @@ -1309,6 +1394,36 @@ static struct attribute *lt6911_attrs[] = { }; ATTRIBUTE_GROUPS(lt6911); +static int lt6911uxc_get_multi_dev_info(struct lt6911uxc *lt6911uxc) +{ + struct device *dev = <6911uxc->i2c_client->dev; + struct device_node *node = dev->of_node; + struct device_node *multi_info_np; + + 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", + <6911uxc->multi_dev_info.dev_idx[0]); + of_property_read_u32(multi_info_np, "dev-idx-r", + <6911uxc->multi_dev_info.dev_idx[1]); + of_property_read_u32(multi_info_np, "combine-idx", + <6911uxc->multi_dev_info.combine_idx[0]); + of_property_read_u32(multi_info_np, "pixel-offset", + <6911uxc->multi_dev_info.pixel_offset); + of_property_read_u32(multi_info_np, "dev-num", + <6911uxc->multi_dev_info.dev_num); + dev_info(dev, + "multi dev left: mipi%d, multi dev right: mipi%d, combile mipi%d, dev num: %d\n", + lt6911uxc->multi_dev_info.dev_idx[0], lt6911uxc->multi_dev_info.dev_idx[1], + lt6911uxc->multi_dev_info.combine_idx[0], lt6911uxc->multi_dev_info.dev_num); + + return 0; +} + static int lt6911uxc_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1341,6 +1456,10 @@ static int lt6911uxc_probe(struct i2c_client *client, return err; } + err = lt6911uxc_get_multi_dev_info(lt6911uxc); + if (err) + v4l2_info(sd, "get multi dev info failed, not use dual mipi mode\n"); + err = lt6911uxc_check_chip_id(lt6911uxc); if (err < 0) return err; @@ -1497,9 +1616,10 @@ static void __exit lt6911uxc_driver_exit(void) i2c_del_driver(<6911uxc_driver); } -late_initcall(lt6911uxc_driver_init); +device_initcall_sync(lt6911uxc_driver_init); module_exit(lt6911uxc_driver_exit); MODULE_DESCRIPTION("Lontium LT6911UXC HDMI to MIPI CSI-2 bridge driver"); MODULE_AUTHOR("Dingxian Wen "); +MODULE_AUTHOR("Jianwei Fan "); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/lt6911uxc.h b/drivers/media/i2c/lt6911uxc.h index 45cb29764879..29c9217172ba 100644 --- a/drivers/media/i2c/lt6911uxc.h +++ b/drivers/media/i2c/lt6911uxc.h @@ -65,4 +65,9 @@ #define AUDIO_SAMPLE_RATAE_H 0xb0aa #define AUDIO_SAMPLE_RATAE_L 0xb0ab +#define COLOR_FMT_STATUS 0xb092 +#define STREAM_CTRL 0x811d +#define STREAM_ENABLE 0xFB +#define STREAM_DISABLE 0xBB + #endif From 824a8b7fd5a6df4745b42545f3fe4343bed6431c Mon Sep 17 00:00:00 2001 From: Mingwei Yan Date: Thu, 28 Sep 2023 15:00:17 +0800 Subject: [PATCH 02/55] media: rockchip: isp: fix rv1106g3 4k cmsk right Signed-off-by: Mingwei Yan Change-Id: I0754f3a4e8b0ac5227b933eeb0cc41d9782fb790 --- drivers/media/platform/rockchip/isp/common.c | 4 +++- drivers/media/platform/rockchip/isp/rkisp.c | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/rockchip/isp/common.c b/drivers/media/platform/rockchip/isp/common.c index e7421abcd93a..1f3e0a14494b 100644 --- a/drivers/media/platform/rockchip/isp/common.c +++ b/drivers/media/platform/rockchip/isp/common.c @@ -169,8 +169,10 @@ void rkisp_update_regs(struct rkisp_device *dev, u32 start, u32 end) continue; } - if (hw->unite == ISP_UNITE_ONE && dev->unite_index == ISP_UNITE_RIGHT) + if (hw->unite == ISP_UNITE_ONE && dev->unite_index == ISP_UNITE_RIGHT) { val = dev->sw_base_addr + i + RKISP_ISP_SW_MAX_SIZE; + flag = dev->sw_base_addr + i + RKISP_ISP_SW_MAX_SIZE + RKISP_ISP_SW_REG_SIZE; + } if (*flag == SW_REG_CACHE) { if ((i == ISP3X_MAIN_RESIZE_CTRL || diff --git a/drivers/media/platform/rockchip/isp/rkisp.c b/drivers/media/platform/rockchip/isp/rkisp.c index 6bab4e5c3a07..12ed753d8d8b 100644 --- a/drivers/media/platform/rockchip/isp/rkisp.c +++ b/drivers/media/platform/rockchip/isp/rkisp.c @@ -1570,6 +1570,7 @@ static void rkisp_config_cmsk_dual(struct rkisp_device *dev, left.win[0].win_en &= ~BIT(i); left.win[1].win_en &= ~BIT(i); left.win[2].win_en &= ~BIT(i); + right.win[i].h_offs = right.win[i].h_offs - w; } else { /* cmsk window at dual isp */ left.win[i].h_size = ALIGN(w - h_offs, 8); From e7893cb3912f69fbc3d73adaa7b44d1d3ce19ea4 Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Fri, 28 Apr 2023 19:30:56 +0530 Subject: [PATCH 03/55] BACKPORT: xhci: Improve the XHCI system resume time Avoid extra 120ms delay during system resume. The xHC controller may signal wake up to 120ms before showing which usb device caused the wake on the xHC port registers. The xhci driver therefore checks for port activity up to 120ms during resume, making sure that the hub driver can see the port change, and won't immediately runtime suspend back due to no port activity. This is however only needed for runtime resume as system resume will resume all child hubs and other child usb devices anyway. Fixes: 253f588c70f6 ("xhci: Improve detection of device initiated wake signal.") Signed-off-by: Basavaraj Natikar Acked-by: Mathias Nyman Link: https://lore.kernel.org/r/20230428140056.1318981-3-Basavaraj.Natikar@amd.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: William Wu (cherry picked from commit 1c024241d018cf9fc17aa8d95c3fe77d671d7142) Change-Id: Icf4e8c97b3d5f6e9550fcad0e748c7badf1b22df --- drivers/usb/host/xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index cd344c52c682..eb50776257ec 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1258,7 +1258,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) * the first wake signalling failed, give it that chance. */ pending_portevent = xhci_pending_portevent(xhci); - if (!pending_portevent) { + if (!pending_portevent && !IS_ENABLED(CONFIG_ARCH_ROCKCHIP)) { msleep(120); pending_portevent = xhci_pending_portevent(xhci); } From db86421b1a0d9c5e7f1ce90282fd06a956efb1d7 Mon Sep 17 00:00:00 2001 From: Algea Cao Date: Thu, 28 Sep 2023 16:28:09 +0800 Subject: [PATCH 04/55] drm: bridge: dw-hdmi: Fix 3d fp mode clock miscalculation 3d fp mode pixel clock needs to be doubled. mode->crtc_clock is already a doubled clock, don't have to multiply it by 2. At the same time, this error will cause the audio cts calculation error so that hdmi has no sound output. This commit can also fix the problem. Signed-off-by: Algea Cao Change-Id: I7d080f9779a640649c149f4b21072597cf0c0cf7 --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index f462aacda62f..35db236c9536 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -2425,9 +2425,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, vmode->previous_pixelclock = vmode->mpixelclock; vmode->mpixelclock = mode->crtc_clock * 1000; - if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == - DRM_MODE_FLAG_3D_FRAME_PACKING) - vmode->mpixelclock *= 2; dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock); vmode->previous_tmdsclock = vmode->mtmdsclock; From 6013ab707048d8eefdf91da18319d29609e91004 Mon Sep 17 00:00:00 2001 From: Cody Xie Date: Sat, 16 Sep 2023 10:13:26 +0800 Subject: [PATCH 05/55] arm64: dts: rockchip: rk3588-vehicle-evb-v22: Add delay for all camera related regulators It will be more safer for regulator consumer devices to power on after their power suppliers are stable. And regulator consumer devices need to completely shut down, then power up for a reset cycle. Therefor, regulator providers need to be make sure shutdown completely. - startup-delay-us can be used to wait regulator providers be powered on good, according their power-up time. - off-on-delay-us can be used to wait regulator providers be shut down completely. 1. cameraN_vcc12v_buck have a approximate ~1ms power on rise time and ~13-~14ms power off fall time 2. vcc5v0_buck uses MPQ4317 which have a approximate ~1.5ms EN(on)->PG(high) delay and ~0.8ms EN(off)->PG(low) Change-Id: I8f23a6a4b53c8760f62c64758a481ad86be4da16 Signed-off-by: Cody Xie --- .../boot/dts/rockchip/rk3588-vehicle-evb-v22.dts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts index c2986168a6c3..a214764f4d6c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts @@ -27,6 +27,8 @@ vin-supply = <&vcc12v_dcin>; pinctrl-names = "default"; pinctrl-0 = <&vcc5v0_buck_en>; + startup-delay-us = <2500>; + off-on-delay-us = <1500>; regulator-state-mem { regulator-on-in-suspend; regulator-suspend-microvolt = <5000000>; @@ -156,6 +158,8 @@ regulator-max-microvolt = <12000000>; enable-active-high; gpio = <&i2c5_nca9539_gpio 6 GPIO_ACTIVE_HIGH>; + startup-delay-us = <2000>; + off-on-delay-us = <16000>; vin-supply = <&vcc12v_dcin>; regulator-state-mem { regulator-off-in-suspend; @@ -172,6 +176,8 @@ regulator-max-microvolt = <12000000>; enable-active-high; gpio = <&i2c5_nca9539_gpio 7 GPIO_ACTIVE_HIGH>; + startup-delay-us = <2000>; + off-on-delay-us = <16000>; vin-supply = <&vcc12v_dcin>; regulator-state-mem { regulator-off-in-suspend; @@ -188,6 +194,8 @@ regulator-max-microvolt = <12000000>; enable-active-high; gpio = <&i2c5_nca9539_gpio 8 GPIO_ACTIVE_HIGH>; + startup-delay-us = <2000>; + off-on-delay-us = <16000>; vin-supply = <&vcc12v_dcin>; regulator-state-mem { regulator-off-in-suspend; @@ -204,6 +212,8 @@ regulator-max-microvolt = <12000000>; enable-active-high; gpio = <&i2c5_nca9539_gpio 9 GPIO_ACTIVE_HIGH>; + startup-delay-us = <2000>; + off-on-delay-us = <16000>; vin-supply = <&vcc12v_dcin>; regulator-state-mem { regulator-off-in-suspend; @@ -220,6 +230,8 @@ regulator-max-microvolt = <5000000>; enable-active-high; gpio = <&i2c5_nca9539_gpio 10 GPIO_ACTIVE_HIGH>; + startup-delay-us = <2000>; + off-on-delay-us = <16000>; vin-supply = <&vcc5v0_usb>; }; @@ -232,6 +244,8 @@ regulator-max-microvolt = <5000000>; enable-active-high; gpio = <&i2c5_nca9539_gpio 11 GPIO_ACTIVE_HIGH>; + startup-delay-us = <2000>; + off-on-delay-us = <16000>; vin-supply = <&vcc5v0_usb>; }; From 726d78e739b46797c34946d78794f8f32ff1e01b Mon Sep 17 00:00:00 2001 From: Tony Xie Date: Tue, 12 Sep 2023 10:25:53 +0800 Subject: [PATCH 06/55] irqchip/gic: allow to config pending function between different os for rpmsg, it needs to set pending reg between different os, so allow to config pending function between different os. Fixes: 2e7658ef5774 ("irqchip/gic: support config amp os irqs") Signed-off-by: Tony Xie Change-Id: I4b06f8a692e34c339d4facb40b8120fdbe1d7bcd --- drivers/irqchip/irq-gic.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index cd3f72b4e2d6..d5add4c238e5 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -271,7 +271,8 @@ static int gic_irq_set_irqchip_state(struct irq_data *d, u32 reg; #ifdef CONFIG_ROCKCHIP_AMP - if (rockchip_amp_check_amp_irq(gic_irq(d))) + if (which != IRQCHIP_STATE_PENDING && + rockchip_amp_check_amp_irq(gic_irq(d))) return -EINVAL; #endif switch (which) { From 5c09e33a23568419f0e9a250011a072eee9bd2f1 Mon Sep 17 00:00:00 2001 From: Tony Xie Date: Tue, 12 Sep 2023 11:08:30 +0800 Subject: [PATCH 07/55] soc: rockchip: amp: using 64-bits variable to manage the cpu's affinity for compatiblility between arm32 and arm64, giv2 and giv3,using 64-bits variable to manage the cpu's affinity. Signed-off-by: Tony Xie Change-Id: Ic5ce758d4711c039be9dc52df8f0ebbbdfcc1244 --- drivers/soc/rockchip/rockchip_amp.c | 108 ++++++++++++++++++---------- include/soc/rockchip/rockchip_amp.h | 17 +++-- 2 files changed, 82 insertions(+), 43 deletions(-) diff --git a/drivers/soc/rockchip/rockchip_amp.c b/drivers/soc/rockchip/rockchip_amp.c index db52d0956ee3..6f5bdc678756 100644 --- a/drivers/soc/rockchip/rockchip_amp.c +++ b/drivers/soc/rockchip/rockchip_amp.c @@ -15,6 +15,7 @@ #include #include #include +#include #define RK_CPU_STATUS_OFF 0 #define RK_CPU_STATUS_ON 1 @@ -24,6 +25,7 @@ #define GPIO_BANK_NUM 16 #define GPIO_GROUP_PRIO_MAX 3 +#define MAX_GIC_SPI_NUM (1020) #define AMP_GIC_DBG(fmt, arg...) do { if (0) { pr_warn(fmt, ##arg); } } while (0) enum amp_cpu_ctrl_status { @@ -55,26 +57,27 @@ static struct { struct amp_gpio_group_s { u32 bank_id; u32 prio; - u32 irq_aff[AMP_AFF_MAX_CPU]; + u64 irq_aff[AMP_AFF_MAX_CPU]; u32 irq_id[AMP_AFF_MAX_CPU]; u32 en[AMP_AFF_MAX_CPU]; }; struct amp_irq_cfg_s { + u64 aff; u32 prio; u32 cpumask; - u32 aff; int amp_flag; -} irqs_cfg[1024]; +} irqs_cfg[MAX_GIC_SPI_NUM]; static struct amp_gic_ctrl_s { + enum gic_type gic_version; + u32 spis_num; struct { u32 aff; u32 cpumask; u32 flag; } aff_to_cpumask[AMP_AFF_MAX_CLUSTER][AMP_AFF_MAX_CPU]; - struct amp_irq_cfg_s irqs_cfg[1024]; - u32 validmask[1020 / 32 + 1]; + struct amp_irq_cfg_s irqs_cfg[MAX_GIC_SPI_NUM]; struct amp_gpio_group_s gpio_grp[GPIO_BANK_NUM][GPIO_GROUP_PRIO_MAX]; u32 gpio_banks; } amp_ctrl; @@ -275,7 +278,12 @@ u32 rockchip_amp_get_irq_cpumask(u32 irq) return amp_ctrl.irqs_cfg[irq].cpumask; } -static u32 amp_get_cpumask_bit(u32 aff) +int rockchip_amp_need_init_amp_irq(u32 irq) +{ + return amp_ctrl.irqs_cfg[irq].amp_flag; +} + +static u32 amp_get_cpumask_bit(u64 aff) { u32 aff_cluster, aff_cpu; @@ -285,18 +293,24 @@ static u32 amp_get_cpumask_bit(u32 aff) if (aff_cpu >= AMP_AFF_MAX_CPU || aff_cluster >= AMP_AFF_MAX_CLUSTER) return 0; - AMP_GIC_DBG("%s: aff:%d-%d: %x\n", __func__, aff_cluster, aff_cpu, + AMP_GIC_DBG(" %s: aff:%d-%d: %x\n", __func__, aff_cluster, aff_cpu, amp_ctrl.aff_to_cpumask[aff_cluster][aff_cpu].cpumask); return amp_ctrl.aff_to_cpumask[aff_cluster][aff_cpu].cpumask; } +u64 rockchip_amp_get_irq_aff(u32 irq) +{ + return amp_ctrl.irqs_cfg[irq].aff; +} + static int gic_amp_get_gpio_prio_group_info(struct device_node *np, struct amp_gic_ctrl_s *amp_ctrl, int prio_id) { - u32 gpio_bank, count0, count1, prio, irq_id, irq_aff; - int i; + u32 gpio_bank, prio, irq_id; + u64 irq_aff; + int i, count0, count1; struct amp_gpio_group_s *gpio_grp; struct amp_irq_cfg_s *irqs_cfg; @@ -320,7 +334,7 @@ static int gic_amp_get_gpio_prio_group_info(struct device_node *np, __func__, gpio_bank, prio_id, prio); count0 = of_property_count_u32_elems(np, "girq-id"); - count1 = of_property_count_u32_elems(np, "girq-aff"); + count1 = of_property_count_u64_elems(np, "girq-aff"); if (count0 != count1) return -EINVAL; @@ -330,7 +344,7 @@ static int gic_amp_get_gpio_prio_group_info(struct device_node *np, for (i = 0; i < count0; i++) { of_property_read_u32_index(np, "girq-id", i, &irq_id); gpio_grp->irq_id[i] = irq_id; - of_property_read_u32_index(np, "girq-aff", i, &irq_aff); + of_property_read_u64_index(np, "girq-aff", i, &irq_aff); gpio_grp->irq_aff[i] = irq_aff; @@ -338,18 +352,24 @@ static int gic_amp_get_gpio_prio_group_info(struct device_node *np, irqs_cfg = &_ctrl->irqs_cfg[irq_id]; - AMP_GIC_DBG(" %s: group cpu-%d, irq-%d: prio-%x, aff-%x en-%d\n", + AMP_GIC_DBG(" %s: group cpu-%d, irq-%d: prio-%x, aff-%llx en-%d\n", __func__, i, gpio_grp->irq_id[i], gpio_grp->prio, gpio_grp->irq_aff[i], gpio_grp->en[i]); if (gpio_grp->en[i]) { irqs_cfg->prio = gpio_grp->prio; irqs_cfg->aff = irq_aff; - irqs_cfg->cpumask = amp_get_cpumask_bit(irq_aff); + if (amp_ctrl->gic_version == GIC_V2) { + irqs_cfg->cpumask = amp_get_cpumask_bit(irq_aff); + if (!irqs_cfg->cpumask) { + pr_err(" %s: get cpumask error\n", __func__); + return -EINVAL; + } + } irqs_cfg->amp_flag = 1; } - AMP_GIC_DBG(" %s: irqs_cfg prio-%x aff-%x cpumaks-%x en-%d\n", + AMP_GIC_DBG(" %s: prio-%x aff-%llx cpumaks-%x flag-%d\n", __func__, irqs_cfg->prio, irqs_cfg->aff, irqs_cfg->cpumask, irqs_cfg->amp_flag); } @@ -404,33 +424,37 @@ static int amp_gic_get_cpumask(struct device_node *np, struct amp_gic_ctrl_s *am { const struct property *prop; int count, i; - u32 cluster, aff_cpu, aff, cpumask; + u32 cluster, aff_cpu; + u64 aff, cpumask; + if (amp_ctrl->gic_version != GIC_V2) + return 0; prop = of_find_property(np, "amp-cpu-aff-maskbits", NULL); + if (!prop) return -1; if (!prop->value) return -1; - count = of_property_count_u32_elems(np, "amp-cpu-aff-maskbits"); + count = of_property_count_u64_elems(np, "amp-cpu-aff-maskbits"); if (count % 2) return -1; for (i = 0; i < count / 2; i++) { - of_property_read_u32_index(np, "amp-cpu-aff-maskbits", + of_property_read_u64_index(np, "amp-cpu-aff-maskbits", 2 * i, &aff); cluster = MPIDR_AFFINITY_LEVEL(aff, 1); aff_cpu = MPIDR_AFFINITY_LEVEL(aff, 0); amp_ctrl->aff_to_cpumask[cluster][aff_cpu].aff = aff; - of_property_read_u32_index(np, "amp-cpu-aff-maskbits", + of_property_read_u64_index(np, "amp-cpu-aff-maskbits", 2 * i + 1, &cpumask); - amp_ctrl->aff_to_cpumask[cluster][aff_cpu].cpumask = cpumask; + amp_ctrl->aff_to_cpumask[cluster][aff_cpu].cpumask = (u32)cpumask; - AMP_GIC_DBG("cpumask: %d-%d: aff-%d cpumask-%d\n", - cluster, aff_cpu, aff, cpumask); + AMP_GIC_DBG("cpumask: %d-%d: aff-%llx cpumask-%d\n", + cluster, aff_cpu, aff, (u32)cpumask); if (!cpumask) return -1; @@ -443,8 +467,9 @@ static void amp_gic_get_irqs_config(struct device_node *np, struct amp_gic_ctrl_s *amp_ctrl) { const struct property *prop; - int count, i; - u32 irq, prio, aff; + u32 irq, i; + int count; + u64 aff, val, prio; prop = of_find_property(np, "amp-irqs", NULL); if (!prop) @@ -453,30 +478,31 @@ static void amp_gic_get_irqs_config(struct device_node *np, if (!prop->value) return; - count = of_property_count_u32_elems(np, "amp-irqs"); + count = of_property_count_u64_elems(np, "amp-irqs"); - if (count < 0 || count % 3) + if (count % 3) return; for (i = 0; i < count / 3; i++) { - of_property_read_u32_index(np, "amp-irqs", 3 * i, &irq); - - if (irq > 1020) + of_property_read_u64_index(np, "amp-irqs", 3 * i, &val); + irq = (u32)val; + if (irq > amp_ctrl->spis_num) break; - of_property_read_u32_index(np, "amp-irqs", 3 * i + 1, &prio); - of_property_read_u32_index(np, "amp-irqs", 3 * i + 2, &aff); + of_property_read_u64_index(np, "amp-irqs", 3 * i + 1, &prio); + of_property_read_u64_index(np, "amp-irqs", 3 * i + 2, &aff); - AMP_GIC_DBG("%s: irq-%d aff-%d prio-%x\n", + AMP_GIC_DBG("%s: irq-%d aff-%llx prio-%llx\n", __func__, irq, aff, prio); - amp_ctrl->irqs_cfg[irq].prio = prio; + amp_ctrl->irqs_cfg[irq].prio = (u32)prio; amp_ctrl->irqs_cfg[irq].aff = aff; - amp_ctrl->irqs_cfg[irq].cpumask = amp_get_cpumask_bit(aff); - - if (!amp_ctrl->irqs_cfg[irq].cpumask) { - AMP_GIC_DBG("%s: get cpumask error\n", __func__); - break; + if (amp_ctrl->gic_version == GIC_V2) { + amp_ctrl->irqs_cfg[irq].cpumask = amp_get_cpumask_bit(aff); + if (!amp_ctrl->irqs_cfg[irq].cpumask) { + pr_err("%s: get cpumask error\n", __func__); + break; + } } if (!amp_ctrl->irqs_cfg[irq].aff && @@ -485,17 +511,20 @@ static void amp_gic_get_irqs_config(struct device_node *np, amp_ctrl->irqs_cfg[irq].amp_flag = 1; - AMP_GIC_DBG("%s: irq-%d aff-%d cpumask-%d pri-%x\n", + AMP_GIC_DBG(" %s: irq-%d aff-%llx cpumask-%x pri-%x\n", __func__, irq, amp_ctrl->irqs_cfg[irq].aff, amp_ctrl->irqs_cfg[irq].cpumask, amp_ctrl->irqs_cfg[irq].prio); } } -void rockchip_amp_get_gic_info(void) +void rockchip_amp_get_gic_info(u32 spis_num, enum gic_type gic_version) { struct device_node *np; + amp_ctrl.spis_num = spis_num; + amp_ctrl.gic_version = gic_version; + np = of_find_node_by_name(NULL, "rockchip-amp"); if (!np) return; @@ -504,6 +533,7 @@ void rockchip_amp_get_gic_info(void) pr_err("%s: get amp gic cpu mask error\n", __func__); goto exit; } + gic_of_get_gpio_group(np, &_ctrl); amp_gic_get_irqs_config(np, &_ctrl); diff --git a/include/soc/rockchip/rockchip_amp.h b/include/soc/rockchip/rockchip_amp.h index 1c37e8b46e0b..851c0f47d54a 100644 --- a/include/soc/rockchip/rockchip_amp.h +++ b/include/soc/rockchip/rockchip_amp.h @@ -9,15 +9,18 @@ #ifndef _ROCKCHIP_AMP #define _ROCKCHIP_AMP +#include + #if IS_REACHABLE(CONFIG_ROCKCHIP_AMP) -void rockchip_amp_get_gic_info(void); +void rockchip_amp_get_gic_info(u32 spis_num, enum gic_type gic_version); int rockchip_amp_check_amp_irq(u32 irq); u32 rockchip_amp_get_irq_prio(u32 irq); u32 rockchip_amp_get_irq_cpumask(u32 irq); +u64 rockchip_amp_get_irq_aff(u32 irq); +int rockchip_amp_need_init_amp_irq(u32 irq); #else -#include - -static inline void rockchip_amp_get_gic_info(void) +static inline void rockchip_amp_get_gic_info(u32 spis_num, + enum gic_type gic_version) { } @@ -35,5 +38,11 @@ static inline u32 rockchip_amp_get_irq_cpumask(u32 irq) { return 0; } + +static inline int rockchip_amp_need_init_amp_irq(u32 irq) +{ + return 0; +} + #endif /* CONFIG_ROCKCHIP_AMP */ #endif /* _ROCKCHIP_AMP */ From cb753cddaa9bc58a570f506b93e5bfcfa22eb774 Mon Sep 17 00:00:00 2001 From: Tony Xie Date: Tue, 12 Sep 2023 14:56:48 +0800 Subject: [PATCH 08/55] irqchip/gic: add the parameter of gic version for rockchip amp Signed-off-by: Tony Xie Change-Id: Idf679a2e91a62a90de4059f09451c58d6d56ecd4 --- drivers/irqchip/irq-gic-common.c | 2 +- drivers/irqchip/irq-gic.c | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index 7ce79b512e01..bf590c4a4538 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -122,7 +122,7 @@ void gic_dist_config(void __iomem *base, int gic_irqs, amp_pri = 0; for (j = 0; j < 4; j++) { - if (rockchip_amp_check_amp_irq(i + j)) { + if (rockchip_amp_need_init_amp_irq(i + j)) { amp_pri |= rockchip_amp_get_irq_prio(i + j) << (j * 8); } else { diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index d5add4c238e5..7f4edce3f1ff 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -534,7 +534,7 @@ static void gic_dist_init(struct gic_chip_data *gic) maskval = 0; for (j = 0; j < 4; j++) { - if (rockchip_amp_check_amp_irq(i + j)) { + if (rockchip_amp_need_init_amp_irq(i + j)) { maskval |= rockchip_amp_get_irq_cpumask(i + j) << (j * 8); } else { @@ -1349,6 +1349,9 @@ static int gic_init_bases(struct gic_chip_data *gic, goto error; } +#ifdef CONFIG_ROCKCHIP_AMP + rockchip_amp_get_gic_info(gic->gic_irqs, GIC_V2); +#endif gic_dist_init(gic); ret = gic_cpu_init(gic); if (ret) @@ -1567,10 +1570,6 @@ static int gic_of_setup(struct gic_chip_data *gic, struct device_node *node) gic_enable_of_quirks(node, gic_quirks, gic); -#ifdef CONFIG_ROCKCHIP_AMP - rockchip_amp_get_gic_info(); -#endif - return 0; error: From 8c22a02698c05b7c5090f48c3666cb31fdc6535e Mon Sep 17 00:00:00 2001 From: Tony Xie Date: Wed, 13 Sep 2023 11:07:33 +0800 Subject: [PATCH 09/55] arm64: dts: rockchip: rk3562-amp: using 64-bits for cpu's affinity Signed-off-by: Tony Xie Change-Id: Ic515f53185d380c6ce4de84b777ca6578e8c08fa --- arch/arm64/boot/dts/rockchip/rk3562-amp.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3562-amp.dtsi b/arch/arm64/boot/dts/rockchip/rk3562-amp.dtsi index 25f820c68c77..58c9df9d44e6 100644 --- a/arch/arm64/boot/dts/rockchip/rk3562-amp.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3562-amp.dtsi @@ -16,8 +16,8 @@ pinctrl-names = "default"; pinctrl-0 = <&uart7m1_xfer>; - amp-cpu-aff-maskbits = <0x0 0x1 0x1 0x2 0x2 0x4 0x3 0x8>; - amp-irqs = ; + amp-cpu-aff-maskbits = /bits/ 64 <0x0 0x1 0x1 0x2 0x2 0x4 0x3 0x8>; + amp-irqs = /bits/ 64 ; status = "okay"; }; From de0064df8fa35b0d0e2951b2b2799f9e02157439 Mon Sep 17 00:00:00 2001 From: Tony Xie Date: Wed, 13 Sep 2023 11:11:47 +0800 Subject: [PATCH 10/55] arm64: dts: rockchip: rk3308b-amp: using 64-bits for cpu's affinity Signed-off-by: Tony Xie Change-Id: I532adb6c891fcda4733740027cb6bbb878b24e2b --- arch/arm64/boot/dts/rockchip/rk3308b-amp.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3308b-amp.dtsi b/arch/arm64/boot/dts/rockchip/rk3308b-amp.dtsi index 9b7ec9810f99..a2d39c8ea3ee 100644 --- a/arch/arm64/boot/dts/rockchip/rk3308b-amp.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3308b-amp.dtsi @@ -14,8 +14,8 @@ pinctrl-names = "default"; pinctrl-0 = <&uart1_xfer>; status = "okay"; - amp-cpu-aff-maskbits = <0x0 0x1 0x1 0x2 0x2 0x4 0x3 0x8>; - amp-irqs = ; + amp-cpu-aff-maskbits = /bits/ 64 <0x0 0x1 0x1 0x2 0x2 0x4 0x3 0x8>; + amp-irqs = /bits/ 64 ; }; reserved-memory { From 86f2de0e18d03216b48288cc41565a7bd11fc9d4 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 22 Sep 2023 09:57:34 +0800 Subject: [PATCH 11/55] ASoC: rockchip: multi-dais: Use interleaved dma transfer API This patch use the interleaved dma transfer API for multi-dais data, now, let's drop the legacy implementation. Signed-off-by: Sugar Zhang Change-Id: I8f217e25ae68017eff3eaedfe1d3475cbbaccde6 --- sound/soc/rockchip/rockchip_multi_dais_pcm.c | 110 +++++++++++++------ 1 file changed, 74 insertions(+), 36 deletions(-) diff --git a/sound/soc/rockchip/rockchip_multi_dais_pcm.c b/sound/soc/rockchip/rockchip_multi_dais_pcm.c index 88daa5dd9ec6..0c9376351e47 100644 --- a/sound/soc/rockchip/rockchip_multi_dais_pcm.c +++ b/sound/soc/rockchip/rockchip_multi_dais_pcm.c @@ -33,6 +33,7 @@ struct dmaengine_mpcm { struct dmaengine_mpcm_runtime_data { struct dma_chan *chans[MAX_DAIS]; + struct dma_interleaved_template *xt; dma_cookie_t cookies[MAX_DAIS]; unsigned int *channel_maps; int num_chans; @@ -135,33 +136,71 @@ static void dmaengine_mpcm_get_master_chan(struct dmaengine_mpcm_runtime_data *p } } +static int dmaengine_config_interleaved(struct snd_pcm_substream *substream, + struct dma_interleaved_template *xt, + int offset, int sample_bytes, int nump, int numf) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int frame_bytes; + + frame_bytes = frames_to_bytes(runtime, 1); + + xt->frame_size = 1; + xt->sgl[0].size = sample_bytes; + xt->sgl[0].icg = frame_bytes - sample_bytes; + +#ifdef CONFIG_NO_GKI + xt->nump = nump; +#endif + xt->numf = numf; + + xt->dir = snd_pcm_substream_to_dma_direction(substream); + + if (xt->dir == DMA_MEM_TO_DEV) { + xt->src_start = runtime->dma_addr + offset; + xt->src_inc = true; + xt->src_sgl = true; + xt->dst_inc = false; + xt->dst_sgl = false; + } else { + xt->dst_start = runtime->dma_addr + offset; + xt->src_inc = false; + xt->src_sgl = false; + xt->dst_inc = true; + xt->dst_sgl = true; + } + + return 0; +} + static int dmaengine_mpcm_prepare_and_submit(struct snd_pcm_substream *substream) { struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct dma_async_tx_descriptor *desc = NULL; - enum dma_transfer_direction direction; + struct dma_interleaved_template *xt = prtd->xt; unsigned long flags = DMA_CTRL_ACK; unsigned int *maps = prtd->channel_maps; - int offset, buffer_bytes, period_bytes; + int offset; int i; - direction = snd_pcm_substream_to_dma_direction(substream); - if (!substream->runtime->no_period_wakeup) flags |= DMA_PREP_INTERRUPT; prtd->pos = 0; offset = 0; - period_bytes = snd_pcm_lib_period_bytes(substream); - buffer_bytes = snd_pcm_lib_buffer_bytes(substream); + for (i = 0; i < prtd->num_chans; i++) { if (!prtd->chans[i]) continue; - desc = dmaengine_prep_dma_cyclic(prtd->chans[i], - runtime->dma_addr + offset, - buffer_bytes, period_bytes, - direction, flags); + + dmaengine_config_interleaved(substream, xt, offset, + samples_to_bytes(runtime, maps[i]), + runtime->period_size, + runtime->buffer_size); + + desc = dmaengine_prep_interleaved_dma(prtd->chans[i], xt, + flags | DMA_PREP_REPEAT); if (!desc) return -ENOMEM; @@ -249,33 +288,34 @@ static void dmaengine_mpcm_single_dma_complete(void *arg) } static int __mpcm_prepare_single_and_submit(struct snd_pcm_substream *substream, - dma_addr_t buf_start, int size) + int buf_offset, int size) { struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_interleaved_template *xt = prtd->xt; struct snd_pcm_runtime *runtime = substream->runtime; struct dma_async_tx_descriptor *desc; - enum dma_transfer_direction direction; unsigned long flags = DMA_CTRL_ACK; unsigned int *maps = prtd->channel_maps; int offset, i; bool callback = false; - direction = snd_pcm_substream_to_dma_direction(substream); - if (!substream->runtime->no_period_wakeup) flags |= DMA_PREP_INTERRUPT; - offset = 0; + offset = buf_offset; for (i = 0; i < prtd->num_chans; i++) { if (!prtd->chans[i]) continue; - desc = dmaengine_prep_slave_single(prtd->chans[i], - buf_start + offset, - size, - direction, flags); + dmaengine_config_interleaved(substream, xt, offset, + samples_to_bytes(runtime, maps[i]), + 0, + bytes_to_frames(runtime, size)); + + desc = dmaengine_prep_interleaved_dma(prtd->chans[i], xt, flags); if (!desc) return -ENOMEM; + if (!callback) { desc->callback = dmaengine_mpcm_single_dma_complete; desc->callback_param = substream; @@ -316,15 +356,15 @@ static int dmaengine_mpcm_prepare_single_and_submit(struct snd_pcm_substream *su pr_debug("%s: offset: %d, buffer_bytes: %d\n", __func__, offset, buffer_bytes); pr_debug("%s: count: %d, residue_bytes: %d\n", __func__, count, residue_bytes); for (i = 0; i < count; i++) { - ret = __mpcm_prepare_single_and_submit(substream, buf_start, + ret = __mpcm_prepare_single_and_submit(substream, offset, period_bytes); if (ret) return ret; - buf_start += period_bytes; + offset += period_bytes; } if (residue_bytes) { - ret = __mpcm_prepare_single_and_submit(substream, buf_start, + ret = __mpcm_prepare_single_and_submit(substream, offset, residue_bytes); if (ret) return ret; @@ -423,22 +463,12 @@ static int dmaengine_mpcm_hw_params(struct snd_soc_component *component, sz = snd_pcm_format_size(format, maps[i]); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { chan = pcm->tx_chans[i]; -#ifdef CONFIG_NO_GKI - if (sz) { - slave_config.src_interlace_size = frame_bytes - sz; - if (slave_config.src_interlace_size) - slave_config.dst_maxburst = sz / slave_config.dst_addr_width; - } -#endif + if (sz && (frame_bytes - sz) > 0) + slave_config.dst_maxburst = sz / slave_config.dst_addr_width; } else { chan = pcm->rx_chans[i]; -#ifdef CONFIG_NO_GKI - if (sz) { - slave_config.dst_interlace_size = frame_bytes - sz; - if (slave_config.dst_interlace_size) - slave_config.src_maxburst = sz / slave_config.src_addr_width; - } -#endif + if (sz && (frame_bytes - sz) > 0) + slave_config.src_maxburst = sz / slave_config.src_addr_width; } if (!chan) continue; @@ -542,6 +572,13 @@ static int dmaengine_mpcm_open(struct snd_soc_component *component, if (!prtd) return -ENOMEM; + prtd->xt = kzalloc(sizeof(struct dma_interleaved_template) + + sizeof(struct data_chunk), GFP_KERNEL); + if (!prtd->xt) { + kfree(prtd); + return -ENOMEM; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { prtd->channel_maps = pcm->mdais->playback_channel_maps; for (i = 0; i < pcm->mdais->num_dais; i++) @@ -642,6 +679,7 @@ static int dmaengine_mpcm_close(struct snd_soc_component *component, { struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); + kfree(prtd->xt); kfree(prtd); return 0; From 7405801db4aa08e0b8e04f55b6e04ab08b688526 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 22 Sep 2023 09:57:35 +0800 Subject: [PATCH 12/55] dmaengine: Drop the unused config This patch drop the unused config src/dst_interleace_size. Signed-off-by: Sugar Zhang Change-Id: I78dba866c6d7c6c755c77d1558a8449716bcc1aa --- include/linux/dmaengine.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 51510f40916d..875d47d133b9 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -453,10 +453,6 @@ struct dma_slave_config { unsigned int slave_id; void *peripheral_config; size_t peripheral_size; -#ifdef CONFIG_NO_GKI - unsigned int src_interlace_size; - unsigned int dst_interlace_size; -#endif }; /** From 0eba9f8ec037e4c7fccb16e2a433be822dd802b8 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 22 Sep 2023 09:57:32 +0800 Subject: [PATCH 13/55] dmaengine: pl330: Add support for interleaved transfer This patch add support for interleaved transfer which used for interleaved audio or 2d video data transfer. for audio situation, we add 'nump' for number of period frames. Signed-off-by: Sugar Zhang Change-Id: I502ea9c86c8403dc5b1f38abf40be8b6ee13c1dc --- drivers/dma/pl330.c | 177 ++++++++++++++++++++++++-------------- include/linux/dmaengine.h | 4 + 2 files changed, 116 insertions(+), 65 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index b1d6faede65b..0b2b7acf5462 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -541,11 +541,9 @@ struct dma_pl330_desc { /* For cyclic capability */ bool cyclic; size_t num_periods; -#ifdef CONFIG_NO_GKI - /* interlace size */ - unsigned int src_interlace_size; - unsigned int dst_interlace_size; -#endif + + /* interleaved size */ + struct data_chunk sgl; }; struct _xfer_spec { @@ -1204,7 +1202,7 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, const struct _xfer_spec *pxs, int cyc, enum pl330_cond cond) { - int off = 0; + int off = 0, i = 0, burstn = 1; /* * do FLUSHP at beginning to clear any stale dma requests before the @@ -1212,30 +1210,36 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, */ if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); + + if (pxs->desc->sgl.size) { + WARN_ON(BYTE_MOD_BURST_LEN(pxs->desc->sgl.size, pxs->ccr)); + burstn = BYTE_TO_BURST(pxs->desc->sgl.size, pxs->ccr); + } + while (cyc--) { - off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); - off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, - pxs->desc->peri); - off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, - pxs->desc->peri); -#ifdef CONFIG_NO_GKI + for (i = 0; i < burstn; i++) { + off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); + off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, + pxs->desc->peri); + off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, + pxs->desc->peri); + } + switch (pxs->desc->rqtype) { case DMA_DEV_TO_MEM: - - if (pxs->desc->dst_interlace_size) + if (pxs->desc->sgl.dst_icg) off += _emit_ADDH(dry_run, &buf[off], DST, - pxs->desc->dst_interlace_size); + pxs->desc->sgl.dst_icg); break; case DMA_MEM_TO_DEV: - if (pxs->desc->src_interlace_size) + if (pxs->desc->sgl.src_icg) off += _emit_ADDH(dry_run, &buf[off], SRC, - pxs->desc->src_interlace_size); + pxs->desc->sgl.src_icg); break; default: WARN_ON(1); break; } -#endif } return off; @@ -1450,9 +1454,7 @@ static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], off += _emit_LPEND(dry_run, &buf[off], &lpend); } -#ifdef CONFIG_NO_GKI - if (!pxs->desc->src_interlace_size && - !pxs->desc->dst_interlace_size) { + if (!pxs->desc->sgl.src_icg && !pxs->desc->sgl.dst_icg) { num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); if (num_dregs) { @@ -1460,14 +1462,6 @@ static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); } } -#else - num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); - - if (num_dregs) { - off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); - off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); - } -#endif off += _emit_SEV(dry_run, &buf[off], ev); @@ -1535,26 +1529,18 @@ static inline int _setup_loops(struct pl330_dmac *pl330, BRST_SIZE(ccr); int off = 0; -#ifdef CONFIG_NO_GKI - if (pxs->desc->rqtype == DMA_DEV_TO_MEM) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) + - pxs->desc->dst_interlace_size); - else if (pxs->desc->rqtype == DMA_MEM_TO_DEV) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) + - pxs->desc->src_interlace_size); -#endif + if (pxs->desc->sgl.size) + bursts = x->bytes / pxs->desc->sgl.size; + while (bursts) { c = bursts; off += _loop(pl330, dry_run, &buf[off], &c, pxs); bursts -= c; } -#ifdef CONFIG_NO_GKI - if (!pxs->desc->src_interlace_size && - !pxs->desc->dst_interlace_size) + + if (!pxs->desc->sgl.src_icg && !pxs->desc->sgl.dst_icg) off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); -#else - off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); -#endif + return off; } @@ -1585,14 +1571,9 @@ static inline int _setup_xfer_cyclic(struct pl330_dmac *pl330, unsigned long bursts = BYTE_TO_BURST(x->bytes, ccr); int off = 0; -#ifdef CONFIG_NO_GKI - if (pxs->desc->rqtype == DMA_DEV_TO_MEM) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) - + pxs->desc->dst_interlace_size); - else if (pxs->desc->rqtype == DMA_MEM_TO_DEV) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) - + pxs->desc->src_interlace_size); -#endif + if (pxs->desc->sgl.size) + bursts = x->bytes / pxs->desc->sgl.size; + /* Setup Loop(s) */ off += _loop_cyclic(pl330, dry_run, &buf[off], bursts, pxs, ev); @@ -2454,10 +2435,6 @@ static int pl330_config_write(struct dma_chan *chan, pch->fifo_addr = slave_config->dst_addr; if (slave_config->dst_addr_width) pch->burst_sz = __ffs(slave_config->dst_addr_width); -#ifdef CONFIG_NO_GKI - if (slave_config->src_interlace_size) - pch->slave_config.src_interlace_size = slave_config->src_interlace_size; -#endif pch->burst_len = fixup_burst_len(slave_config->dst_maxburst, pch->dmac->quirks); } else if (direction == DMA_DEV_TO_MEM) { @@ -2465,10 +2442,6 @@ static int pl330_config_write(struct dma_chan *chan, pch->fifo_addr = slave_config->src_addr; if (slave_config->src_addr_width) pch->burst_sz = __ffs(slave_config->src_addr_width); -#ifdef CONFIG_NO_GKI - if (slave_config->dst_interlace_size) - pch->slave_config.dst_interlace_size = slave_config->dst_interlace_size; -#endif pch->burst_len = fixup_burst_len(slave_config->src_maxburst, pch->dmac->quirks); } @@ -2821,6 +2794,10 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) desc->cyclic = false; desc->num_periods = 1; + desc->sgl.size = 0; + desc->sgl.src_icg = 0; + desc->sgl.dst_icg = 0; + dma_async_tx_descriptor_init(&desc->txd, &pch->chan); return desc; @@ -2936,10 +2913,80 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( desc->cyclic = true; desc->num_periods = len / period_len; desc->txd.flags = flags; + + return &desc->txd; +} + +static struct dma_async_tx_descriptor *pl330_prep_interleaved_dma( + struct dma_chan *chan, struct dma_interleaved_template *xt, + unsigned long flags) +{ + struct dma_pl330_desc *desc = NULL; + struct dma_pl330_chan *pch = to_pchan(chan); + dma_addr_t dst = 0, src = 0; + size_t size, src_icg, dst_icg, period_bytes, buffer_bytes; + size_t nump = 0, numf = 0; + + if (!xt->numf || !xt->sgl[0].size || xt->frame_size != 1) + return NULL; + #ifdef CONFIG_NO_GKI - desc->src_interlace_size = pch->slave_config.src_interlace_size; - desc->dst_interlace_size = pch->slave_config.dst_interlace_size; + nump = xt->nump; #endif + numf = xt->numf; + size = xt->sgl[0].size; + period_bytes = size * nump; + buffer_bytes = size * numf; + + if (flags & DMA_PREP_REPEAT && (!nump || (numf % nump))) + return NULL; + + src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]); + dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]); + + pl330_config_write(chan, &pch->slave_config, xt->dir); + + if (!pl330_prep_slave_fifo(pch, xt->dir)) + return NULL; + + desc = pl330_get_desc(pch); + if (!desc) { + dev_err(chan->device->dev, "Failed to get desc\n"); + return NULL; + } + + if (xt->dir == DMA_MEM_TO_DEV) { + desc->rqcfg.src_inc = 1; + desc->rqcfg.dst_inc = 0; + src = xt->src_start; + dst = pch->fifo_dma; + } else { + desc->rqcfg.src_inc = 0; + desc->rqcfg.dst_inc = 1; + src = pch->fifo_dma; + dst = xt->dst_start; + } + + desc->rqtype = xt->dir; + desc->rqcfg.brst_size = pch->burst_sz; + desc->rqcfg.brst_len = pch->burst_len; + desc->bytes_requested = buffer_bytes; + desc->sgl.size = size; + desc->sgl.src_icg = src_icg; + desc->sgl.dst_icg = dst_icg; + desc->txd.flags = flags; + + if (flags & DMA_PREP_REPEAT) { + desc->cyclic = true; + desc->num_periods = numf / nump; + fill_px(&desc->px, dst, src, period_bytes); + } else { + fill_px(&desc->px, dst, src, buffer_bytes); + } + + dev_dbg(chan->device->dev, "size: %zu, src_icg: %zu, dst_icg: %zu, nump: %zu, numf: %zu\n", + size, src_icg, dst_icg, nump, numf); + return &desc->txd; } @@ -3072,10 +3119,6 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, desc->rqcfg.brst_len = pch->burst_len; desc->rqtype = direction; desc->bytes_requested = sg_dma_len(sg); -#ifdef CONFIG_NO_GKI - desc->src_interlace_size = pch->slave_config.src_interlace_size; - desc->dst_interlace_size = pch->slave_config.dst_interlace_size; -#endif } /* Return the last desc in the chain */ @@ -3311,12 +3354,16 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) dma_cap_set(DMA_SLAVE, pd->cap_mask); dma_cap_set(DMA_CYCLIC, pd->cap_mask); dma_cap_set(DMA_PRIVATE, pd->cap_mask); + dma_cap_set(DMA_INTERLEAVE, pd->cap_mask); + dma_cap_set(DMA_REPEAT, pd->cap_mask); + dma_cap_set(DMA_LOAD_EOT, pd->cap_mask); } pd->device_alloc_chan_resources = pl330_alloc_chan_resources; pd->device_free_chan_resources = pl330_free_chan_resources; pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy; pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic; + pd->device_prep_interleaved_dma = pl330_prep_interleaved_dma; pd->device_tx_status = pl330_tx_status; pd->device_prep_slave_sg = pl330_prep_slave_sg; pd->device_config = pl330_config; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 08537ef11a89..51510f40916d 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -145,6 +145,7 @@ struct data_chunk { * Otherwise, destination is filled contiguously (icg ignored). * Ignored if dst_inc is false. * @numf: Number of frames in this template. + * @nump: Number of period frames in this template. * @frame_size: Number of chunks in a frame i.e, size of sgl[]. * @sgl: Array of {chunk,icg} pairs that make up a frame. */ @@ -157,6 +158,9 @@ struct dma_interleaved_template { bool src_sgl; bool dst_sgl; size_t numf; +#ifdef CONFIG_NO_GKI + size_t nump; +#endif size_t frame_size; struct data_chunk sgl[]; }; From da317c67760aa3ac7ecc910d41f44c7a89b89fa0 Mon Sep 17 00:00:00 2001 From: Rimon Xu Date: Fri, 9 Dec 2022 16:20:19 +0800 Subject: [PATCH 14/55] video: rockchip: vtunnel: add video tunnel driver Signed-off-by: Rimon Xu Change-Id: Ic621f6fcbc4924bd5567c9161f358e9365830618 --- drivers/video/rockchip/Kconfig | 1 + drivers/video/rockchip/Makefile | 1 + drivers/video/rockchip/vtunnel/Kconfig | 12 + drivers/video/rockchip/vtunnel/Makefile | 3 + drivers/video/rockchip/vtunnel/rkvtunnel.c | 1523 ++++++++++++++++++++ drivers/video/rockchip/vtunnel/rkvtunnel.h | 81 ++ 6 files changed, 1621 insertions(+) create mode 100644 drivers/video/rockchip/vtunnel/Kconfig create mode 100644 drivers/video/rockchip/vtunnel/Makefile create mode 100644 drivers/video/rockchip/vtunnel/rkvtunnel.c create mode 100644 drivers/video/rockchip/vtunnel/rkvtunnel.h diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig index 2b5a072d2cdd..571d34c08603 100644 --- a/drivers/video/rockchip/Kconfig +++ b/drivers/video/rockchip/Kconfig @@ -7,3 +7,4 @@ source "drivers/video/rockchip/iep/Kconfig" source "drivers/video/rockchip/mpp/Kconfig" source "drivers/video/rockchip/dvbm/Kconfig" source "drivers/video/rockchip/vehicle/Kconfig" +source "drivers/video/rockchip/vtunnel/Kconfig" diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index 0b8a7bbb95e6..b13d93a1cd6f 100644 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_IEP) += iep/ obj-$(CONFIG_ROCKCHIP_MPP_SERVICE) += mpp/ obj-$(CONFIG_ROCKCHIP_DVBM) += dvbm/ obj-$(CONFIG_VIDEO_REVERSE_IMAGE) += vehicle/ +obj-$(CONFIG_ROCKCHIP_VIDEO_TUNNEL) += vtunnel/ diff --git a/drivers/video/rockchip/vtunnel/Kconfig b/drivers/video/rockchip/vtunnel/Kconfig new file mode 100644 index 000000000000..49ad2a592bb0 --- /dev/null +++ b/drivers/video/rockchip/vtunnel/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +menu "Rockchip video tunnel support" + +config ROCKCHIP_VIDEO_TUNNEL + tristate "Rockchip video tunnel device support" + depends on ARCH_ROCKCHIP + default n + help + Rockchip videotunnel device support. + +endmenu diff --git a/drivers/video/rockchip/vtunnel/Makefile b/drivers/video/rockchip/vtunnel/Makefile new file mode 100644 index 000000000000..fdfd79a1c0a2 --- /dev/null +++ b/drivers/video/rockchip/vtunnel/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_ROCKCHIP_VIDEO_TUNNEL) += rkvtunnel.o diff --git a/drivers/video/rockchip/vtunnel/rkvtunnel.c b/drivers/video/rockchip/vtunnel/rkvtunnel.c new file mode 100644 index 000000000000..238a6516fc32 --- /dev/null +++ b/drivers/video/rockchip/vtunnel/rkvtunnel.c @@ -0,0 +1,1523 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rkvtunnel.h" + +#define DEVICE_NAME "rkvtunnel" +#define RKVT_MAX_NAME_LENGTH 128 +#define RKVT_POOL_SIZE 32 +#define RKVT_MAX_WAIT_MS 4 +#define RKVT_FENCE_WAIT_MS 3000 + +#define RKVT_DBG_USER (1U << 0) +#define RKVT_DBG_BUFFERS (1U << 1) +#define RKVT_DBG_CMD (1U << 2) +#define RKVT_DBG_FILE (1U << 3) + +#define rkvt_dbg(mask, x...)\ + do { if (unlikely(vt_dev_dbg & mask)) pr_info(x); } while (0) + +enum rkvt_buf_status_e { + RKVT_BUF_QUEUE, + RKVT_BUF_DEQUEUE, + RKVT_BUF_ACQUIRE, + RKVT_BUF_RELEASE, + RKVT_BUF_FREE, + RKVT_BUF_BUTT, +}; + +union rkvt_ioc_arg { + struct rkvt_alloc_id_data alloc_data; + struct rkvt_ctrl_data ctrl_data; + struct rkvt_buf_data buffer_data; +}; + +struct rkvt_dev { + struct device *dev; + struct miscdevice mdev; + struct mutex inst_lock; /* protect inst_list and ints_idr */ + struct idr inst_idr; + struct list_head list_inst; /* manage all instances */ + + struct mutex session_lock; /* protect sessions */ + struct list_head list_session; + + char *dev_name; + int inst_id_generator; + atomic64_t cid_generator; + atomic64_t buf_id_generator; + struct dentry *debug_root; +}; + +struct rkvt_session { + struct list_head dev_link; + struct rkvt_dev *vt_dev; + struct list_head list_inst; /* manage instance in session */ + + enum rkvt_caller_e caller; + pid_t pid; + char name[RKVT_MAX_NAME_LENGTH]; + char disp_name[RKVT_MAX_NAME_LENGTH]; + int disp_serial; + int cid; + struct task_struct *task; + struct dentry *debug_root; +}; + +struct rkvt_buffer { + struct file *file_buf[MAX_BUF_HANDLE_FDS]; + int fds_pro[MAX_BUF_HANDLE_FDS]; + int fds_con[MAX_BUF_HANDLE_FDS]; + + struct file *ready_render_fence; + struct dma_fence *rendered_fence; + struct rkvt_session *session_pro; + int cid_pro; + struct rkvt_buf_base base; +}; + +struct rkvt_instance { + struct kref ref; + int id; + struct rkvt_dev *vt_dev; + + struct mutex lock; + struct list_head dev_link; + struct list_head session_link; + struct rkvt_session *consumer; + struct rkvt_session *producer; + wait_queue_head_t wait_consumer; + wait_queue_head_t wait_producer; + + struct dentry *debug_root; + int fcount; + + DECLARE_KFIFO_PTR(fifo_to_consumer, struct rkvt_buffer*); + DECLARE_KFIFO_PTR(fifo_to_producer, struct rkvt_buffer*); + + struct rkvt_buffer vt_buffers[RKVT_POOL_SIZE]; +}; + +static unsigned int vt_dev_dbg; + +module_param(vt_dev_dbg, uint, 0644); +MODULE_PARM_DESC(vt_dev_dbg, "bit switch for vt debug information"); + +static const char * +rkvt_dbg_buf_status_to_string(int status) +{ + const char *status_str; + + switch (status) { + case RKVT_BUF_QUEUE: + status_str = "queued"; + break; + case RKVT_BUF_DEQUEUE: + status_str = "dequeued"; + break; + case RKVT_BUF_ACQUIRE: + status_str = "acquired"; + break; + case RKVT_BUF_RELEASE: + status_str = "released"; + break; + case RKVT_BUF_FREE: + status_str = "free"; + break; + default: + status_str = "unknown"; + } + + return status_str; +} + +static int rkvt_dbg_instance_show(struct seq_file *s, void *unused) +{ + struct rkvt_instance *inst = s->private; + int i; + int size_to_con; + int size_to_pro; + int ref_count; + + mutex_lock(&inst->lock); + size_to_con = kfifo_len(&inst->fifo_to_consumer); + size_to_pro = kfifo_len(&inst->fifo_to_producer); + ref_count = kref_read(&inst->ref); + + seq_printf(s, "tunnel (%p) id=%d, ref=%d, fcount=%d\n", + inst, inst->id, ref_count, inst->fcount); + seq_puts(s, "-----------------------------------------------\n"); + if (inst->consumer) + seq_printf(s, "consumer session (%s) %p\n", + inst->consumer->disp_name, inst->consumer); + if (inst->producer) + seq_printf(s, "producer session (%s) %p\n", + inst->producer->disp_name, inst->producer); + seq_puts(s, "-----------------------------------------------\n"); + + seq_printf(s, "to consumer fifo size:%d\n", size_to_con); + seq_printf(s, "to producer fifo size:%d\n", size_to_pro); + seq_puts(s, "-----------------------------------------------\n"); + + seq_puts(s, "buffers:\n"); + + for (i = 0; i < RKVT_POOL_SIZE; i++) { + struct rkvt_buffer *buffer = &inst->vt_buffers[i]; + int status = buffer->base.buf_status; + + seq_printf(s, " buffer produce_fd[0](%d) status(%s)\n", + buffer->fds_pro[0], + rkvt_dbg_buf_status_to_string(status)); + } + seq_puts(s, "-----------------------------------------------\n"); + mutex_unlock(&inst->lock); + + return 0; +} + +static int +rkvt_dbg_instance_open(struct inode *inode, struct file *file) +{ + return single_open(file, + rkvt_dbg_instance_show, + inode->i_private); +} + +static const struct file_operations dbg_instance_fops = { + .open = rkvt_dbg_instance_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int rkvt_dbg_session_show(struct seq_file *s, void *unused) +{ + struct rkvt_session *session = s->private; + + seq_printf(s, "session(%s) %p role %s cid %d\n", + session->disp_name, session, + session->caller == RKVT_CALLER_PRODUCER ? + "producer" : (session->caller == RKVT_CALLER_CONSUMER ? + "consumer" : "invalid"), session->cid); + seq_puts(s, "-----------------------------------------------\n"); + + return 0; +} + +static int rkvt_dbg_session_open(struct inode *inode, struct file *file) +{ + return single_open(file, + rkvt_dbg_session_show, + inode->i_private); +} + +static const struct file_operations debug_session_fops = { + .open = rkvt_dbg_session_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __rkvt_close_fd(struct files_struct *files, unsigned int fd) +{ + struct file *file; + struct fdtable *fdt; + + spin_lock(&files->file_lock); + + fdt = files_fdtable(files); + if (fd >= fdt->max_fds) + goto out_unlock; + file = fdt->fd[fd]; + if (!file) + goto out_unlock; + + rcu_assign_pointer(fdt->fd[fd], NULL); + spin_unlock(&files->file_lock); + + put_unused_fd(fd); + return filp_close(file, files); + +out_unlock: + spin_unlock(&files->file_lock); + return -EBADF; +} + +static int rkvt_close_fd(struct rkvt_session *session, unsigned int fd) +{ + int ret; + + if (!session->task) + return -ESRCH; + + ret = __rkvt_close_fd(session->task->files, fd); + if (unlikely(ret == -ERESTARTSYS || + ret == -ERESTARTNOINTR || + ret == -ERESTARTNOHAND || + ret == -ERESTART_RESTARTBLOCK)) + ret = -EINTR; + + return ret; +} + +/* The function is responsible for fifo_to_consumer fifo operation + * requires external use of rkvt_instance.lock protection + */ +static void rkvt_inst_clear_consumer(struct rkvt_instance *inst) +{ + struct rkvt_buffer *buffer = NULL; + int i = 0; + + if (!inst) + return; + + while (kfifo_get(&inst->fifo_to_consumer, &buffer)) { + /* put file */ + for (i = 0; i < buffer->base.num_fds; i++) { + if (buffer->file_buf[i]) { + fput(buffer->file_buf[i]); + buffer->file_buf[i] = NULL; + } + inst->fcount--; + } + if (buffer->ready_render_fence) { + fput(buffer->ready_render_fence); + buffer->ready_render_fence = NULL; + } + rkvt_dbg(RKVT_DBG_FILE, + "vt [%d] instance trim file(%p) buffer(%p) ino(%08lu) fcount=%d\n", + inst->id, buffer->file_buf, buffer, + buffer->file_buf[i] ? + file_inode(buffer->file_buf[i])->i_ino : 0, + inst->fcount); + if (inst->producer != NULL) { + buffer->base.buf_status = RKVT_BUF_RELEASE; + kfifo_put(&inst->fifo_to_producer, buffer); + wake_up_interruptible(&inst->wait_producer); + } else { + buffer->base.buf_status = RKVT_BUF_FREE; + } + } +} + +/* The function is responsible for fifo_to_consumer fifo operation + * requires external use of rkvt_instance.lock protection. + */ +static void rkvt_inst_clear_producer(struct rkvt_instance *inst) +{ + struct rkvt_buffer *buffer = NULL; + + if (!inst) + return; + + while (kfifo_get(&inst->fifo_to_producer, &buffer)) { + if (buffer->rendered_fence) { + dma_fence_put(buffer->rendered_fence); + buffer->rendered_fence = NULL; + } + buffer->base.buf_status = RKVT_BUF_FREE; + } +} + +static void rkvt_inst_destroy(struct kref *kref) +{ + struct rkvt_instance *inst = + container_of(kref, struct rkvt_instance, ref); + struct rkvt_dev *vt_dev = inst->vt_dev; + + list_del_init(&inst->dev_link); + idr_remove(&vt_dev->inst_idr, inst->id); + + rkvt_dbg(RKVT_DBG_USER, "vt [%d] destroy\n", inst->id); + + mutex_lock(&inst->lock); + rkvt_inst_clear_consumer(inst); + rkvt_inst_clear_producer(inst); + kfifo_free(&inst->fifo_to_consumer); + kfifo_free(&inst->fifo_to_producer); + mutex_unlock(&inst->lock); + + debugfs_remove_recursive(inst->debug_root); + + devm_kfree(vt_dev->dev, inst); +} + +static struct rkvt_instance *rkvt_inst_create(struct rkvt_dev *vt_dev) +{ + struct rkvt_instance *inst; + int status; + int i; + + inst = devm_kzalloc(vt_dev->dev, sizeof(*inst), GFP_KERNEL); + if (!inst) + return ERR_PTR(-ENOMEM); + + inst->vt_dev = vt_dev; + mutex_init(&inst->lock); + INIT_LIST_HEAD(&inst->dev_link); + INIT_LIST_HEAD(&inst->session_link); + kref_init(&inst->ref); + + status = kfifo_alloc(&inst->fifo_to_consumer, + RKVT_POOL_SIZE, GFP_KERNEL); + if (status) + goto setup_fail; + + status = kfifo_alloc(&inst->fifo_to_producer, + RKVT_POOL_SIZE, GFP_KERNEL); + if (status) + goto fifo_alloc_fail; + + init_waitqueue_head(&inst->wait_producer); + init_waitqueue_head(&inst->wait_consumer); + + for (i = 0; i < RKVT_POOL_SIZE; i++) + inst->vt_buffers[i].base.buf_status = RKVT_BUF_FREE; + + /* insert it to dev instances list */ + mutex_lock(&vt_dev->inst_lock); + list_add_tail(&inst->dev_link, &vt_dev->list_inst); + mutex_unlock(&vt_dev->inst_lock); + + return inst; +fifo_alloc_fail: + kfifo_free(&inst->fifo_to_consumer); +setup_fail: + devm_kfree(vt_dev->dev, inst); + return ERR_PTR(status); +} + +/* The function protected by rkvt_dev.session_lock by caller */ +static int +rkvt_get_session_serial(const struct list_head *sessions, + const unsigned char *name) +{ + int serial = -1; + struct rkvt_session *session, *n; + + list_for_each_entry_safe(session, n, sessions, dev_link) { + if (strcmp(session->name, name)) + continue; + serial = max(serial, session->disp_serial); + } + + return serial + 1; +} + +/* The function protected by rkvt_instance.lock by caller */ +static void +rkvt_session_trim_locked(struct rkvt_session *session, struct rkvt_instance *inst) +{ + if (!session || !inst) + return; + + if (inst->producer && inst->producer == session) { + rkvt_inst_clear_producer(inst); + inst->producer = NULL; + } + + if (inst->consumer && inst->consumer == session) { + rkvt_inst_clear_consumer(inst); + inst->consumer = NULL; + } +} + +static int rkvt_inst_trim(struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst, *n; + int i; + + mutex_lock(&vt_dev->inst_lock); + list_for_each_entry_safe(inst, n, &vt_dev->list_inst, dev_link) { + mutex_lock(&inst->lock); + rkvt_session_trim_locked(session, inst); + + if (!inst->consumer && !inst->producer) { + rkvt_inst_clear_producer(inst); + rkvt_inst_clear_consumer(inst); + + for (i = 0; i < RKVT_POOL_SIZE; i++) + inst->vt_buffers[i].base.buf_status = RKVT_BUF_FREE; + } + mutex_unlock(&inst->lock); + } + mutex_unlock(&vt_dev->inst_lock); + + return 0; +} + +static struct rkvt_session * +rkvt_session_create(struct rkvt_dev *vt_dev, const char *name) +{ + struct rkvt_session *session; + struct task_struct *task = NULL; + + if (!name) { + dev_err(vt_dev->dev, "%s: Name can not be null\n", __func__); + return ERR_PTR(-EINVAL); + } + + session = devm_kzalloc(vt_dev->dev, sizeof(*session), GFP_KERNEL); + if (!session) + return ERR_PTR(-ENOMEM); + + get_task_struct(current->group_leader); + task_lock(current->group_leader); + session->pid = task_pid_nr(current->group_leader); + + if (current->group_leader->flags & PF_KTHREAD) { + put_task_struct(current->group_leader); + task = NULL; + } else { + task = current->group_leader; + } + + task_unlock(current->group_leader); + + session->vt_dev = vt_dev; + session->task = task; + session->caller = RKVT_CALLER_BUTT; + INIT_LIST_HEAD(&session->dev_link); + INIT_LIST_HEAD(&session->list_inst); + snprintf(session->name, RKVT_MAX_NAME_LENGTH, "%s", name); + + mutex_lock(&vt_dev->session_lock); + session->disp_serial = rkvt_get_session_serial(&vt_dev->list_session, name); + snprintf(session->disp_name, RKVT_MAX_NAME_LENGTH, "%s-%d", + name, session->disp_serial); + + list_add_tail(&session->dev_link, &vt_dev->list_session); + + /* add debug fs */ + session->debug_root = debugfs_create_file(session->disp_name, + 0664, + vt_dev->debug_root, + session, + &debug_session_fops); + + mutex_unlock(&vt_dev->session_lock); + + rkvt_dbg(RKVT_DBG_USER, "vt session %s create\n", session->disp_name); + + return session; +} + +static void rkvt_session_destroy(struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + + rkvt_dbg(RKVT_DBG_USER, "vt session %s destroy\n", session->disp_name); + + mutex_lock(&vt_dev->inst_lock); + while ((inst = list_first_entry_or_null(&session->list_inst, + struct rkvt_instance, session_link))) { + list_del_init(&inst->session_link); + kref_put(&inst->ref, rkvt_inst_destroy); + } + mutex_unlock(&vt_dev->inst_lock); + + mutex_lock(&vt_dev->session_lock); + if (session->task) + put_task_struct(session->task); + list_del_init(&session->dev_link); + debugfs_remove_recursive(session->debug_root); + mutex_unlock(&vt_dev->session_lock); + + rkvt_inst_trim(session); + devm_kfree(vt_dev->dev, session); +} + +static int rkvt_open(struct inode *inode, struct file *filep) +{ + struct miscdevice *miscdev = filep->private_data; + struct rkvt_dev *vt_dev = container_of(miscdev, struct rkvt_dev, mdev); + struct rkvt_session *session; + char debug_name[64]; + + snprintf(debug_name, sizeof(debug_name), "%u", task_pid_nr(current->group_leader)); + session = rkvt_session_create(vt_dev, debug_name); + if (IS_ERR(session)) + return PTR_ERR(session); + + filep->private_data = session; + + return 0; +} + +static int rkvt_release(struct inode *inode, struct file *filep) +{ + struct rkvt_session *session = filep->private_data; + + rkvt_session_destroy(session); + filep->private_data = NULL; + + return 0; +} + +static int rkvt_get_connected_id(struct rkvt_dev *vt_dev) +{ + return atomic64_inc_return(&vt_dev->cid_generator); +} + +static struct rkvt_instance * +rkvt_inst_get_by_tid(struct rkvt_dev *vt_dev, int id) +{ + struct rkvt_instance *inst; + + mutex_lock(&vt_dev->inst_lock); + inst = idr_find(&vt_dev->inst_idr, id); + if (!inst) { + mutex_unlock(&vt_dev->inst_lock); + dev_err(vt_dev->dev, "find rkvt [%d] by device idr err, instance is null\n", id); + return NULL; + } + kref_get(&inst->ref); + mutex_unlock(&vt_dev->inst_lock); + + return inst; +} + +static void rkvt_inst_put(struct rkvt_instance *inst) +{ + struct rkvt_dev *vt_dev; + + if (!inst) + return; + + vt_dev = inst->vt_dev; + + mutex_lock(&vt_dev->inst_lock); + kref_put(&inst->ref, rkvt_inst_destroy); + mutex_unlock(&vt_dev->inst_lock); +} + +static int +rkvt_connect_proc(struct rkvt_ctrl_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst; + int ret = 0; + + // ref get not put in function end, because connect need hold 1 refs. + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + + mutex_lock(&inst->lock); + if (data->caller == RKVT_CALLER_PRODUCER) { + if (inst->producer && inst->producer != session) { + dev_err(vt_dev->dev, "Connect to rkvt [%d] err, already has producer\n", + data->vt_id); + ret = -EINVAL; + goto connect_fail; + } + inst->producer = session; + } else if (data->caller == RKVT_CALLER_CONSUMER) { + if (inst->consumer && inst->consumer != session) { + dev_err(vt_dev->dev, "Connect to rkvt [%d] err, already has consumer\n", + data->vt_id); + ret = -EINVAL; + goto connect_fail; + } + inst->consumer = session; + } + mutex_unlock(&inst->lock); + session->cid = rkvt_get_connected_id(vt_dev); + session->caller = data->caller; + + rkvt_dbg(RKVT_DBG_USER, "rkvt [%d] %s-%d connect, instance ref %d\n", + inst->id, + data->caller == RKVT_CALLER_PRODUCER ? "producer" : "consumer", + session->pid, + kref_read(&inst->ref)); + + return 0; + +connect_fail: + mutex_unlock(&inst->lock); + // ref put for rkvt_instance_get_by_tid + rkvt_inst_put(inst); + + return ret; +} + +static int +rkvt_disconnect_proc(struct rkvt_ctrl_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + if (session->caller != data->caller) + goto session_invail; + + mutex_lock(&inst->lock); + if (data->caller == RKVT_CALLER_PRODUCER) { + if (!inst->producer) + goto disconnect_fail; + if (inst->producer != session) + goto disconnect_fail; + + rkvt_session_trim_locked(session, inst); + inst->producer = NULL; + wake_up_interruptible(&inst->wait_producer); + } else if (data->caller == RKVT_CALLER_CONSUMER) { + if (!inst->consumer) + goto disconnect_fail; + if (inst->consumer != session) + goto disconnect_fail; + + rkvt_session_trim_locked(session, inst); + inst->consumer = NULL; + wake_up_interruptible(&inst->wait_consumer); + } + mutex_unlock(&inst->lock); + + rkvt_dbg(RKVT_DBG_USER, "rkvt [%d] %s-%d disconnect, instance ref %d\n", + inst->id, + data->caller == RKVT_CALLER_PRODUCER ? "producer" : "consumer", + session->pid, + kref_read(&inst->ref)); + // ref put for rkvt_instance_get_by_tid + rkvt_inst_put(inst); + // ref put for connect proc + rkvt_inst_put(inst); + session->cid = -1; + + return 0; + +disconnect_fail: + mutex_unlock(&inst->lock); +session_invail: + // ref put for rkvt_instance_get_by_tid + rkvt_inst_put(inst); + + return -EINVAL; +} + +static int +rkvt_reset_proc(struct rkvt_ctrl_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst; + long read_buf_id; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + + mutex_lock(&inst->lock); + rkvt_inst_clear_consumer(inst); + rkvt_inst_clear_producer(inst); + read_buf_id = atomic64_read(&vt_dev->buf_id_generator); + read_buf_id += 0x100; + read_buf_id &= ~0xff; + atomic64_set(&vt_dev->buf_id_generator, read_buf_id); + mutex_unlock(&inst->lock); + + rkvt_inst_put(inst); + + return 0; +} + +static int +rkvt_has_consumer_proc(struct rkvt_ctrl_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + + mutex_lock(&inst->lock); + data->ctrl_data = inst->consumer != NULL ? 1 : 0; + mutex_unlock(&inst->lock); + + rkvt_inst_put(inst); + + return 0; +} + +static int +rkvt_ctrl_proc(struct rkvt_ctrl_data *data, struct rkvt_session *session) +{ + int id = data->vt_id; + int ret = 0; + + if (id < 0) + return -EINVAL; + if (data->caller == RKVT_CALLER_BUTT) + return -EINVAL; + + switch (data->ctrl_cmd) { + case RKVT_CTRL_CONNECT: { + ret = rkvt_connect_proc(data, session); + break; + } + case RKVT_CTRL_DISCONNECT: { + ret = rkvt_disconnect_proc(data, session); + break; + } + case RKVT_CTRL_RESET: { + ret = rkvt_reset_proc(data, session); + break; + } + case RKVT_CTRL_HAS_CONSUMER: { + ret = rkvt_has_consumer_proc(data, session); + break; + } + default: + pr_err("unknown rkvt cmd:%d\n", data->ctrl_cmd); + return -EINVAL; + } + + return ret; +} + +static struct +rkvt_buffer *rkvt_buf_get(struct rkvt_instance *inst, int key) +{ + struct rkvt_buffer *buffer = NULL; + int i; + + mutex_lock(&inst->lock); + for (i = 0; i < RKVT_POOL_SIZE; i++) { + buffer = &inst->vt_buffers[i]; + + if (buffer->base.buf_status == RKVT_BUF_ACQUIRE && + buffer->fds_con[0] == key) + break; + } + mutex_unlock(&inst->lock); + + return buffer; +} + +static int +rkvt_has_buf(struct rkvt_instance *inst, enum rkvt_caller_e caller) +{ + int ret = 0; + + if (caller == RKVT_CALLER_PRODUCER) + ret = !kfifo_is_empty(&inst->fifo_to_producer); + else + ret = !kfifo_is_empty(&inst->fifo_to_consumer); + + return ret; +} + +static int +rkvt_query_buf_and_wait(struct rkvt_instance *inst, + enum rkvt_caller_e caller, + int timeout_ms) +{ + int ret; + wait_queue_head_t *wait_queue; + + if (caller == RKVT_CALLER_PRODUCER) + wait_queue = &inst->wait_producer; + else + wait_queue = &inst->wait_consumer; + if (caller == RKVT_CALLER_PRODUCER && + !kfifo_is_empty(&inst->fifo_to_producer)) + return 0; + if (caller == RKVT_CALLER_CONSUMER && + !kfifo_is_empty(&inst->fifo_to_consumer)) + return 0; + + if (timeout_ms < 0) + wait_event_interruptible(*wait_queue, + rkvt_has_buf(inst, caller)); + else if (timeout_ms > 0) { + ret = wait_event_interruptible_timeout(*wait_queue, + rkvt_has_buf(inst, caller), + msecs_to_jiffies(timeout_ms)); + /* timeout */ + if (ret == 0) + return -EAGAIN; + } else + return -EAGAIN; + + if (caller == RKVT_CALLER_PRODUCER && + kfifo_is_empty(&inst->fifo_to_producer)) + return -EAGAIN; + if (caller == RKVT_CALLER_CONSUMER && + kfifo_is_empty(&inst->fifo_to_consumer)) + return -EAGAIN; + + return 0; +} + +static struct rkvt_buffer *rkvt_get_free_buf(struct rkvt_instance *inst) +{ + struct rkvt_buffer *buffer = NULL; + int i, status; + + mutex_lock(&inst->lock); + for (i = 0; i < RKVT_POOL_SIZE; i++) { + status = inst->vt_buffers[i].base.buf_status; + if (status == RKVT_BUF_FREE || status == RKVT_BUF_DEQUEUE) { + buffer = &inst->vt_buffers[i]; + memset(buffer->file_buf, 0, sizeof(buffer->file_buf)); + buffer->rendered_fence = NULL; + break; + } + } + mutex_unlock(&inst->lock); + + return buffer; +} + +static int +rkvt_queue_buf(struct rkvt_buf_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + struct rkvt_buf_base *base = NULL; + struct rkvt_buffer *buffer = NULL; + int i; + int ret = 0; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + if (!inst->producer || inst->producer != session) { + ret = -EINVAL; + goto queue_fail; + } + if ((data->base.num_fds > MAX_BUF_HANDLE_FDS) || + (data->base.num_ints > MAX_BUF_HANDLE_INTS)) { + ret = -EINVAL; + goto queue_fail; + } + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTQB [%d] start\n", inst->id); + + base = &data->base; + buffer = rkvt_get_free_buf(inst); + for (i = 0; i < base->num_fds; i++) { + buffer->fds_con[i] = -1; + buffer->fds_pro[i] = base->fds[i]; + buffer->file_buf[i] = fget(base->fds[i]); + + if (!buffer->file_buf[i]) { + ret = -EBADF; + goto buf_fget_fail; + } + + inst->fcount++; + rkvt_dbg(RKVT_DBG_FILE, + "VTQB [%d] fget file(%p) buf(%p) buf session(%p) ino(%08lu) fcount=%d\n", + inst->id, buffer->file_buf[i], buffer, buffer->session_pro, + buffer->file_buf[i] ? file_inode(buffer->file_buf[i])->i_ino : 0, + inst->fcount); + } + + if (base->fence_fd >= 0) + buffer->ready_render_fence = fget(base->fence_fd); + + // buffer id is empty, generate a new id + if (base->buffer_id == 0) + base->buffer_id = atomic64_inc_return(&vt_dev->buf_id_generator); + buffer->base = *base; + buffer->base.buf_status = RKVT_BUF_QUEUE; + buffer->session_pro = session; + buffer->cid_pro = session->cid; + + mutex_lock(&inst->lock); + if (inst->consumer) { + kfifo_put(&inst->fifo_to_consumer, buffer); + } else { + for (i = 0; i < buffer->base.num_fds; i++) { + if (buffer->file_buf[i]) { + fput(buffer->file_buf[i]); + buffer->file_buf[i] = NULL; + } + inst->fcount--; + } + if (buffer->ready_render_fence) { + fput(buffer->ready_render_fence); + buffer->ready_render_fence = NULL; + } + buffer->base.buf_status = RKVT_BUF_RELEASE; + kfifo_put(&inst->fifo_to_producer, buffer); + } + mutex_unlock(&inst->lock); + + if (inst->consumer) + wake_up_interruptible(&inst->wait_consumer); + else if (inst->producer) + wake_up_interruptible(&inst->wait_producer); + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTQB [%d] pfd[0]:%d end\n", inst->id, buffer->fds_pro[0]); + +queue_fail: + rkvt_inst_put(inst); + + return ret; +buf_fget_fail: + for (i = 0; i < base->num_fds; i++) { + if (buffer->file_buf[i]) { + fput(buffer->file_buf[i]); + buffer->file_buf[i] = NULL; + inst->fcount--; + } + } + rkvt_inst_put(inst); + + return ret; +} + +static int +rkvt_deque_buf(struct rkvt_buf_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + struct rkvt_buffer *buffer = NULL; + int ret = 0; + unsigned long long cur_time, wait_time; + int i; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + if (!inst->producer || inst->producer != session) { + ret = -EINVAL; + goto deque_fail; + } + + /* empty need wait */ + ret = rkvt_query_buf_and_wait(inst, + RKVT_CALLER_PRODUCER, + data->timeout_ms); + if (ret) + goto deque_fail; + + mutex_lock(&inst->lock); + ret = kfifo_get(&inst->fifo_to_producer, &buffer); + if (!ret || !buffer) { + dev_err(vt_dev->dev, "VTDB [%d] got null buffer ret(%d)\n", inst->id, ret); + mutex_unlock(&inst->lock); + ret = -EAGAIN; + goto deque_fail; + } + mutex_unlock(&inst->lock); + + /* it's previous connect buffer */ + if (buffer->cid_pro != session->cid) { + if (buffer->rendered_fence) { + dma_fence_put(buffer->rendered_fence); + buffer->rendered_fence = NULL; + } + + ret = -EAGAIN; + goto deque_fail; + } + + if (buffer->rendered_fence) { + cur_time = sched_clock(); + ret = dma_fence_wait_timeout(buffer->rendered_fence, false, + msecs_to_jiffies(RKVT_FENCE_WAIT_MS)); + wait_time = sched_clock() - cur_time; + rkvt_dbg(RKVT_DBG_BUFFERS, + "VTDB [%d] pfd[0]:%d rendered fence:%p fence_wait time %llu\n", + inst->id, buffer->fds_pro[0], buffer->rendered_fence, wait_time); + + if (ret < 0) + dev_err(vt_dev->dev, "VTDB [%d] wait fence timeout\n", inst->id); + + dma_fence_put(buffer->rendered_fence); + buffer->rendered_fence = NULL; + } + for (i = 0; i < buffer->base.num_fds; i++) + rkvt_dbg(RKVT_DBG_FILE, + "VTDB [%d] fget file(%p) buf(%p) buf session(%p) ino(%08lu) fcount=%d\n", + inst->id, buffer->file_buf[i], + buffer, buffer->session_pro, + buffer->file_buf[i] ? file_inode(buffer->file_buf[i])->i_ino : 0, + inst->fcount); + + buffer->base.vt_id = inst->id; + /* return the buffer */ + data->base = buffer->base; + buffer->base.buf_status = RKVT_BUF_DEQUEUE; + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTDB [%d] end pfd[0]:%d\n", inst->id, buffer->fds_pro[0]); + +deque_fail: + rkvt_inst_put(inst); + + return ret; +} + +static int +rkvt_acquire_buf(struct rkvt_buf_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + struct rkvt_buffer *buffer = NULL; + int fd, ret = -1; + int i; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + if (!inst->consumer || inst->consumer != session) { + ret = -EINVAL; + goto acquire_fail; + } + if ((data->base.num_fds > MAX_BUF_HANDLE_FDS) || + (data->base.num_ints > MAX_BUF_HANDLE_INTS)) { + ret = -EINVAL; + goto acquire_fail; + } + + /* empty need wait */ + ret = rkvt_query_buf_and_wait(inst, + RKVT_CALLER_CONSUMER, + data->timeout_ms); + if (ret) + goto acquire_fail; + + mutex_lock(&inst->lock); + ret = kfifo_get(&inst->fifo_to_consumer, &buffer); + mutex_unlock(&inst->lock); + if (!ret || !buffer) { + dev_err(vt_dev->dev, "VTAB [%d] got null buffer\n", inst->id); + ret = -EAGAIN; + goto acquire_fail; + } + + /* get the fd in consumer */ + for (i = 0; i < buffer->base.num_fds; i++) { + if (buffer->fds_con[i] <= 0) { + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + goto no_memory; + + fd_install(fd, buffer->file_buf[i]); + buffer->fds_con[i] = fd; + buffer->base.fds[i] = fd; + } + } + if (buffer->ready_render_fence) { + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + goto no_memory; + fd_install(fd, buffer->ready_render_fence); + buffer->base.fence_fd = fd; + } + buffer->base.vt_id = inst->id; + data->base = buffer->base; + buffer->base.buf_status = RKVT_BUF_ACQUIRE; + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTAB [%d] pfd[0](%d) buf(%p) buf session(%p)\n", + inst->id, buffer->fds_pro[0], buffer, buffer->session_pro); + + rkvt_inst_put(inst); + + return 0; + +no_memory: + pr_info("VTAB [%d] install fd error\n", inst->id); + mutex_lock(&inst->lock); + for (i = 0; i < buffer->base.num_fds; i++) { + rkvt_dbg(RKVT_DBG_FILE, + "VTAB [%d] install fd error file(%p) buf(%p) ino(%08lu) fcount=%d\n", + inst->id, buffer->file_buf[i], buffer, + file_inode(buffer->file_buf[i])->i_ino, inst->fcount); + if (buffer->file_buf[i]) { + fput(buffer->file_buf[i]); + buffer->file_buf[i] = NULL; + inst->fcount--; + } + } + if (buffer->ready_render_fence) { + fput(buffer->ready_render_fence); + buffer->ready_render_fence = NULL; + } + buffer->base.buf_status = RKVT_BUF_RELEASE; + + kfifo_put(&inst->fifo_to_producer, buffer); + mutex_unlock(&inst->lock); + if (inst->producer) + wake_up_interruptible(&inst->wait_producer); + ret = -ENOMEM; + +acquire_fail: + rkvt_inst_put(inst); + + return ret; +} + +static int +rkvt_release_buf(struct rkvt_buf_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + struct rkvt_buf_base *buf_base = NULL; + struct rkvt_buffer *buffer = NULL; + int i; + int ret = 0; + long read_buf_id; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + if (!inst->consumer || inst->consumer != session) { + ret = -EINVAL; + goto release_fail; + } + + buf_base = &data->base; + buffer = rkvt_buf_get(inst, buf_base->fds[0]); + if (!buffer) { + ret = -EINVAL; + goto release_fail; + } + + if (buf_base->fence_fd >= 0) + buffer->rendered_fence = sync_file_get_fence(buf_base->fence_fd); + + if (!buffer->rendered_fence) + rkvt_dbg(RKVT_DBG_BUFFERS, "VTRB [%d] rendered fence file is null\n", inst->id); + + /* close the fds in consumer side */ + for (i = 0; i < buf_base->num_fds; i++) { + rkvt_dbg(RKVT_DBG_FILE, + "VTRB [%d] file(%p) buf(%p) buf session(%p) ino(%08lu) fcount=%d\n", + inst->id, buffer->file_buf[i], buffer, buffer->session_pro, + buffer->file_buf[i] ? file_inode(buffer->file_buf[i])->i_ino : 0, + inst->fcount); + rkvt_close_fd(session, buffer->fds_con[i]); + inst->fcount--; + buffer->base.fds[i] = buffer->fds_pro[i]; + } + if (buffer->ready_render_fence) { + fput(buffer->ready_render_fence); + buffer->ready_render_fence = NULL; + } + + buffer->base.crop = buf_base->crop; + buffer->base.buf_status = RKVT_BUF_RELEASE; + + mutex_lock(&inst->lock); + read_buf_id = atomic64_read(&vt_dev->buf_id_generator); + /* if producer has disconnect */ + if (!inst->producer) { + rkvt_dbg(RKVT_DBG_BUFFERS, "VTRB [%d], buffer no producer\n", inst->id); + buffer->base.buf_status = RKVT_BUF_FREE; + } else if ((buffer->base.buffer_id >> 8) != (read_buf_id >> 8)) { + dev_err(vt_dev->dev, "VTRB [%d] generation is different. cur(%lld) VS exp(%lld)\n", + inst->id, buffer->base.buffer_id >> 8, read_buf_id >> 8); + buffer->base.buf_status = RKVT_BUF_FREE; + } else { + if (buffer->session_pro && + buffer->session_pro != inst->producer) { + rkvt_dbg(RKVT_DBG_BUFFERS, + "VTRB [%d] producer not valid, producer(%p), buf session(%p)\n", + inst->id, inst->producer, buffer->session_pro); + buffer->base.buf_status = RKVT_BUF_FREE; + } + + kfifo_put(&inst->fifo_to_producer, buffer); + } + mutex_unlock(&inst->lock); + + if (inst->producer) + wake_up_interruptible(&inst->wait_producer); + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTRB [%d] pfd[0]:%d end\n", inst->id, buffer->fds_pro[0]); + +release_fail: + rkvt_inst_put(inst); + + return ret; +} + +static int +rkvt_cancel_buf(struct rkvt_buf_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + struct rkvt_buf_base *buf_base = NULL; + struct rkvt_buffer *buffer = NULL; + int i; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + if (!inst->producer || inst->producer != session) { + rkvt_inst_put(inst); + return -EINVAL; + } + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTCB [%d] start\n", inst->id); + + buf_base = &data->base; + buffer = rkvt_get_free_buf(inst); + for (i = 0; i < buf_base->num_fds; i++) { + buffer->fds_con[i] = -1; + buffer->fds_pro[i] = buf_base->fds[i]; + rkvt_dbg(RKVT_DBG_FILE, + "VTCB [%d] fget file(%p) buf(%p) buf session(%p) fcount=%d\n", + inst->id, buffer->file_buf[i], buffer, + buffer->session_pro, inst->fcount); + } + // buffer id is empty, generate a new id + if (buf_base->buffer_id == 0) + buf_base->buffer_id = atomic64_inc_return(&vt_dev->buf_id_generator); + buffer->base = *buf_base; + buffer->base.buf_status = RKVT_BUF_RELEASE; + buffer->session_pro = session; + buffer->cid_pro = session->cid; + + mutex_lock(&inst->lock); + kfifo_put(&inst->fifo_to_producer, buffer); + mutex_unlock(&inst->lock); + + if (inst->producer) + wake_up_interruptible(&inst->wait_producer); + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTCB [%d] pfd[0]:%d end\n", inst->id, buffer->fds_pro[0]); + rkvt_inst_put(inst); + + return 0; +} + +static unsigned int rkvt_ioctl_dir(unsigned int cmd) +{ + switch (cmd) { + case RKVT_IOC_ALLOC_ID: + case RKVT_IOC_DEQUE_BUF: + case RKVT_IOC_ACQUIRE_BUF: + case RKVT_IOC_CTRL: + return _IOC_READ; + case RKVT_IOC_QUEUE_BUF: + case RKVT_IOC_RELEASE_BUF: + case RKVT_IOC_CANCEL_BUF: + case RKVT_IOC_FREE_ID: + return _IOC_WRITE; + default: + return _IOC_DIR(cmd); + } +} + +static long rkvt_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + union rkvt_ioc_arg data; + struct rkvt_session *session = filep->private_data; + unsigned int dir = rkvt_ioctl_dir(cmd); + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + + rkvt_dbg(RKVT_DBG_CMD, "rkvt ioctl cmd 0x%x size %d in\n", cmd, _IOC_SIZE(cmd)); + + if (_IOC_SIZE(cmd) > sizeof(data)) + return -EINVAL; + + if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd))) + return -EFAULT; + + switch (cmd) { + case RKVT_IOC_ALLOC_ID: { + char name[64]; + + inst = rkvt_inst_create(session->vt_dev); + if (IS_ERR(inst)) + return PTR_ERR(inst); + + mutex_lock(&vt_dev->inst_lock); + ++vt_dev->inst_id_generator; + ret = idr_alloc(&vt_dev->inst_idr, inst, + vt_dev->inst_id_generator, 0, GFP_KERNEL); + mutex_unlock(&vt_dev->inst_lock); + if (ret < 0) { + rkvt_inst_put(inst); + return ret; + } + + inst->id = ret; + snprintf(name, sizeof(name), "instance-%d", inst->id); + inst->debug_root = + debugfs_create_file(name, 0664, vt_dev->debug_root, + inst, &dbg_instance_fops); + + mutex_lock(&vt_dev->inst_lock); + list_add_tail(&inst->session_link, &session->list_inst); + mutex_unlock(&vt_dev->inst_lock); + + data.alloc_data.vt_id = inst->id; + rkvt_dbg(RKVT_DBG_USER, "rkvt alloc instance [%d], ref %d\n", + inst->id, kref_read(&inst->ref)); + break; + } + case RKVT_IOC_FREE_ID: { + inst = rkvt_inst_get_by_tid(vt_dev, data.alloc_data.vt_id); + /* to do free id operation check */ + if (!inst) { + dev_err(vt_dev->dev, "destroy unknown videotunnel instance:%d\n", + data.alloc_data.vt_id); + ret = -EINVAL; + } else { + rkvt_dbg(RKVT_DBG_USER, "rkvt free instance [%d], ref %d\n", + inst->id, kref_read(&inst->ref)); + + mutex_lock(&vt_dev->inst_lock); + list_del_init(&inst->session_link); + mutex_unlock(&vt_dev->inst_lock); + // ref put for rkvt_instance_get_by_tid + rkvt_inst_put(inst); + // ref put for kref_init in rkvt_inst_create + rkvt_inst_put(inst); + } + break; + } + case RKVT_IOC_CTRL: + ret = rkvt_ctrl_proc(&data.ctrl_data, session); + break; + case RKVT_IOC_QUEUE_BUF: + ret = rkvt_queue_buf(&data.buffer_data, session); + break; + case RKVT_IOC_DEQUE_BUF: + ret = rkvt_deque_buf(&data.buffer_data, session); + break; + case RKVT_IOC_RELEASE_BUF: + ret = rkvt_release_buf(&data.buffer_data, session); + break; + case RKVT_IOC_ACQUIRE_BUF: + ret = rkvt_acquire_buf(&data.buffer_data, session); + break; + case RKVT_IOC_CANCEL_BUF: + ret = rkvt_cancel_buf(&data.buffer_data, session); + break; + default: + dev_err(vt_dev->dev, "%s: cmd 0x%x not found.\n", __func__, cmd); + return -ENOTTY; + } + + if (dir & _IOC_READ) { + if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) + return -EFAULT; + } + + return ret; +} + +static const struct file_operations vt_fops = { + .owner = THIS_MODULE, + .open = rkvt_open, + .release = rkvt_release, + .unlocked_ioctl = rkvt_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = rkvt_ioctl, +#endif +}; + +static int rkvt_probe(struct platform_device *pdev) +{ + int ret; + struct device *dev = &pdev->dev; + struct rkvt_dev *vdev = NULL; + + dev_info(dev, "probe start\n"); + vdev = devm_kzalloc(dev, sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return -ENOMEM; + + vdev->dev = dev; + vdev->dev_name = DEVICE_NAME; + vdev->mdev.minor = MISC_DYNAMIC_MINOR; + vdev->mdev.name = DEVICE_NAME; + vdev->mdev.fops = &vt_fops; + platform_set_drvdata(pdev, vdev); + + ret = misc_register(&vdev->mdev); + if (ret) { + dev_err(dev, "misc_register fail.\n"); + return ret; + } + + mutex_init(&vdev->inst_lock); + mutex_init(&vdev->session_lock); + idr_init(&vdev->inst_idr); + atomic64_set(&vdev->cid_generator, 0); + INIT_LIST_HEAD(&vdev->list_inst); + INIT_LIST_HEAD(&vdev->list_session); + vdev->debug_root = debugfs_create_dir(DEVICE_NAME, NULL); + if (!vdev->debug_root) + dev_err(dev, "failed to create debugfs root directory.\n"); + + dev_info(dev, "probe success\n"); + + return 0; +} + +static int rkvt_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rkvt_dev *vdev = platform_get_drvdata(pdev); + + dev_info(dev, "remove device\n"); + + idr_destroy(&vdev->inst_idr); + debugfs_remove_recursive(vdev->debug_root); + misc_deregister(&vdev->mdev); + + return 0; +} + +static const struct of_device_id rk_vt_match[] = { + { + .compatible = "rockchip,video-tunnel", + }, + { }, +}; + +static struct platform_driver rk_vt_driver = { + .probe = rkvt_probe, + .remove = rkvt_remove, + .driver = { + .name = "rk_videotunnel_driver", + .owner = THIS_MODULE, + .of_match_table = rk_vt_match, + }, +}; + +module_platform_driver(rk_vt_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ROCKCHIP videotunnel driver"); diff --git a/drivers/video/rockchip/vtunnel/rkvtunnel.h b/drivers/video/rockchip/vtunnel/rkvtunnel.h new file mode 100644 index 000000000000..1781aa785bc0 --- /dev/null +++ b/drivers/video/rockchip/vtunnel/rkvtunnel.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd + */ +#ifndef __ROCKCHIP_VIDEO_TUNNEL_H__ +#define __ROCKCHIP_VIDEO_TUNNEL_H__ + +#include +#include + +#define MAX_BUF_HANDLE_FDS 16 +#define MAX_BUF_HANDLE_INTS 128 + +#define RKVT_IOC_MAGIC 'V' +#define RKVT_IOWR(nr, type) _IOWR(RKVT_IOC_MAGIC, nr, type) + +#define RKVT_IOC_ALLOC_ID RKVT_IOWR(0x0, struct rkvt_alloc_id_data) +#define RKVT_IOC_FREE_ID RKVT_IOWR(0x1, struct rkvt_alloc_id_data) +#define RKVT_IOC_CTRL RKVT_IOWR(0x2, struct rkvt_ctrl_data) +#define RKVT_IOC_QUEUE_BUF RKVT_IOWR(0x3, struct rkvt_buf_data) +#define RKVT_IOC_DEQUE_BUF RKVT_IOWR(0x4, struct rkvt_buf_data) +#define RKVT_IOC_CANCEL_BUF RKVT_IOWR(0x5, struct rkvt_buf_data) +#define RKVT_IOC_ACQUIRE_BUF RKVT_IOWR(0x6, struct rkvt_buf_data) +#define RKVT_IOC_RELEASE_BUF RKVT_IOWR(0x7, struct rkvt_buf_data) + +// caller type +enum rkvt_caller_e { + RKVT_CALLER_PRODUCER, + RKVT_CALLER_CONSUMER, + RKVT_CALLER_BUTT, +}; + +// video tunnel caller control +enum rkvt_ctrl_cmd_e { + RKVT_CTRL_CONNECT, + RKVT_CTRL_DISCONNECT, + RKVT_CTRL_RESET, + RKVT_CTRL_HAS_CONSUMER, + RKVT_CTRL_BUTT, +}; + +struct rkvt_alloc_id_data { + int vt_id; +}; + +struct rkvt_ctrl_data { + int vt_id; + enum rkvt_caller_e caller; + enum rkvt_ctrl_cmd_e ctrl_cmd; + int ctrl_data; +}; + +struct rkvt_rect { + int left; + int top; + int right; + int bottom; +}; + +struct rkvt_buf_base { + int vt_id; + int fence_fd; + int buf_status; + int num_fds; /* number of file-descriptors at &data[0] */ + int num_ints; /* number of ints at &data[numFds] */ + int reserved; + int fds[MAX_BUF_HANDLE_FDS]; + int ints[MAX_BUF_HANDLE_INTS]; + int64_t priv_data; + uint64_t expected_present_time; + uint64_t buffer_id; + struct rkvt_rect crop; +}; + +struct rkvt_buf_data { + int vt_id; + int timeout_ms; /* 0: non block, negative: block, other: timeout ms */ + struct rkvt_buf_base base; +}; + +#endif From f7a8feac254f8231f92acddc852669e78e1e2a83 Mon Sep 17 00:00:00 2001 From: Xiao Ya peng Date: Mon, 18 Sep 2023 15:04:17 +0800 Subject: [PATCH 15/55] misc: pcie-rkep: Fix memory leak in pcie_rkep_remove. Fixes: 006505ca7669 ("misc: add rockchip standard EP function driver") Signed-off-by: Xiao Ya peng Change-Id: I602323f9cedc6edf94f951b9b9c9787b3a271550 --- drivers/misc/rockchip/pcie-rkep.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/misc/rockchip/pcie-rkep.c b/drivers/misc/rockchip/pcie-rkep.c index 12caa412f3d9..b39b9f4156af 100644 --- a/drivers/misc/rockchip/pcie-rkep.c +++ b/drivers/misc/rockchip/pcie-rkep.c @@ -815,7 +815,12 @@ static void pcie_rkep_remove(struct pci_dev *pdev) #if IS_ENABLED(CONFIG_PCIE_FUNC_RKEP_USERPAGES) free_contig_range(page_to_pfn(pcie_rkep->user_pages), RKEP_USER_MEM_SIZE >> PAGE_SHIFT); #endif - pci_iounmap(pdev, pcie_rkep->bar0); + if (pcie_rkep->bar0) + pci_iounmap(pdev, pcie_rkep->bar0); + if (pcie_rkep->bar2) + pci_iounmap(pdev, pcie_rkep->bar2); + if (pcie_rkep->bar4) + pci_iounmap(pdev, pcie_rkep->bar4); pci_release_regions(pdev); pci_disable_device(pdev); misc_deregister(&pcie_rkep->dev); From 7a031fea60c88a762848fdcf999280896cae376f Mon Sep 17 00:00:00 2001 From: LongChang Ma Date: Wed, 13 Sep 2023 09:56:46 +0800 Subject: [PATCH 16/55] media: i2c: sc401ai: add support thunder boot Signed-off-by: LongChang Ma Change-Id: I1608a9341ddc772aaa72002e1b9c7361235908c0 --- drivers/media/i2c/sc401ai.c | 97 ++++++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 18 deletions(-) diff --git a/drivers/media/i2c/sc401ai.c b/drivers/media/i2c/sc401ai.c index e4f965d349de..0eac2d3c15be 100644 --- a/drivers/media/i2c/sc401ai.c +++ b/drivers/media/i2c/sc401ai.c @@ -9,6 +9,7 @@ * V0.0X01.0X03 fix gain range. * V0.0X01.0X04 add enum_frame_interval function. * V0.0X01.0X05 add quick stream on/off + * V0.0X01.0X06 support thunder boot function. */ #include @@ -31,8 +32,9 @@ #include #include #include +#include "../platform/rockchip/isp/rkisp_tb_helper.h" -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x05) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x06) #ifndef V4L2_CID_DIGITAL_GAIN #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN @@ -173,6 +175,8 @@ struct sc401ai { const char *module_name; const char *len_name; u32 cur_vts; + bool is_thunderboot; + bool is_first_streamoff; struct preisp_hdrae_exp_s init_hdrae_exp; }; @@ -447,6 +451,8 @@ static const s64 link_freq_menu_items[] = { SC401AI_LINK_FREQ_630, }; +static int __sc401ai_power_on(struct sc401ai *sc401ai); + /* Write registers up to 4 at a time */ static int sc401ai_write_reg(struct i2c_client *client, u16 reg, u32 len, u32 val) @@ -624,6 +630,11 @@ static int sc401ai_set_gain_reg(struct sc401ai *sc401ai, u32 gain) DIG_Fine_gain_reg = abs(800 * gain / (Dcg_gainx100 * Coarse_gain * DIG_gain) / ANA_Fine_gainx64); + if (sc401ai->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + sc401ai->is_thunderboot = false; + __sc401ai_power_on(sc401ai); + } + ret = sc401ai_write_reg(sc401ai->client, SC401AI_REG_DIG_GAIN, SC401AI_REG_VALUE_08BIT, @@ -989,23 +1000,31 @@ static int __sc401ai_start_stream(struct sc401ai *sc401ai) { int ret; - ret = sc401ai_write_array(sc401ai->client, sc401ai->cur_mode->reg_list); - if (ret) - return ret; + if (!sc401ai->is_thunderboot) { + ret = sc401ai_write_array(sc401ai->client, sc401ai->cur_mode->reg_list); + if (ret) + return ret; - /* In case these controls are set before streaming */ - ret = __v4l2_ctrl_handler_setup(&sc401ai->ctrl_handler); - if (ret) - return ret; + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&sc401ai->ctrl_handler); + if (ret) + return ret; + } return sc401ai_write_reg(sc401ai->client, - SC401AI_REG_CTRL_MODE, - SC401AI_REG_VALUE_08BIT, - SC401AI_MODE_STREAMING); + SC401AI_REG_CTRL_MODE, + SC401AI_REG_VALUE_08BIT, + SC401AI_MODE_STREAMING); + } static int __sc401ai_stop_stream(struct sc401ai *sc401ai) { + if (sc401ai->is_thunderboot) { + sc401ai->is_first_streamoff = true; + pm_runtime_put(&sc401ai->client->dev); + } + return sc401ai_write_reg(sc401ai->client, SC401AI_REG_CTRL_MODE, SC401AI_REG_VALUE_08BIT, @@ -1024,6 +1043,10 @@ static int sc401ai_s_stream(struct v4l2_subdev *sd, int on) goto unlock_and_return; if (on) { + if (sc401ai->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + sc401ai->is_thunderboot = false; + __sc401ai_power_on(sc401ai); + } ret = pm_runtime_get_sync(&client->dev); if (ret < 0) { pm_runtime_put_noidle(&client->dev); @@ -1115,6 +1138,10 @@ static int __sc401ai_power_on(struct sc401ai *sc401ai) dev_err(dev, "Failed to enable xvclk\n"); return ret; } + + if (sc401ai->is_thunderboot) + return 0; + if (!IS_ERR(sc401ai->reset_gpio)) gpiod_set_value_cansleep(sc401ai->reset_gpio, 0); @@ -1153,6 +1180,15 @@ static void __sc401ai_power_off(struct sc401ai *sc401ai) int ret; struct device *dev = &sc401ai->client->dev; + if (sc401ai->is_thunderboot) { + if (sc401ai->is_first_streamoff) { + sc401ai->is_thunderboot = false; + sc401ai->is_first_streamoff = false; + } else { + return; + } + } + if (!IS_ERR(sc401ai->pwdn_gpio)) gpiod_set_value_cansleep(sc401ai->pwdn_gpio, 0); clk_disable_unprepare(sc401ai->xvclk); @@ -1491,6 +1527,11 @@ static int sc401ai_check_sensor_id(struct sc401ai *sc401ai, u32 id = 0; int ret; + if (sc401ai->is_thunderboot) { + dev_info(dev, "Enable thunderboot mode, skip sensor id check\n"); + return 0; + } + ret = sc401ai_read_reg(client, SC401AI_REG_CHIP_ID, SC401AI_REG_VALUE_16BIT, &id); if (id != CHIP_ID) { @@ -1549,6 +1590,8 @@ static int sc401ai_probe(struct i2c_client *client, return -EINVAL; } + sc401ai->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); + sc401ai->client = client; for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { if (hdr_mode == supported_modes[i].hdr_mode) { @@ -1565,13 +1608,23 @@ static int sc401ai_probe(struct i2c_client *client, return -EINVAL; } - sc401ai->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(sc401ai->reset_gpio)) - dev_warn(dev, "Failed to get reset-gpios\n"); + if (sc401ai->is_thunderboot) { + sc401ai->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); + if (IS_ERR(sc401ai->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); - sc401ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); - if (IS_ERR(sc401ai->pwdn_gpio)) - dev_warn(dev, "Failed to get pwdn-gpios\n"); + sc401ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS); + if (IS_ERR(sc401ai->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + } else { + sc401ai->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(sc401ai->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + sc401ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(sc401ai->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + } sc401ai->pinctrl = devm_pinctrl_get(dev); if (!IS_ERR(sc401ai->pinctrl)) { @@ -1646,7 +1699,10 @@ static int sc401ai_probe(struct i2c_client *client, pm_runtime_set_active(dev); pm_runtime_enable(dev); - pm_runtime_idle(dev); + if (sc401ai->is_thunderboot) + pm_runtime_get_sync(dev); + else + pm_runtime_idle(dev); return 0; @@ -1718,7 +1774,12 @@ static void __exit sensor_mod_exit(void) i2c_del_driver(&sc401ai_i2c_driver); } +#if defined(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) && !defined(CONFIG_INITCALL_ASYNC) +subsys_initcall(sensor_mod_init); +#else device_initcall_sync(sensor_mod_init); +#endif + module_exit(sensor_mod_exit); MODULE_DESCRIPTION("smartsens sc401ai sensor driver"); From 11a496172bdc734504c2f00327ed05a11e9af54f Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Tue, 13 Sep 2022 08:45:17 +0000 Subject: [PATCH 17/55] arm64: dts: rockchip: Add dual mipi configuration for rk3588 evb1 extboard Signed-off-by: Jianwei Fan Change-Id: Id3efb7546e3e9fb5206cc12058724e778f1c5340 --- arch/arm64/boot/dts/rockchip/Makefile | 1 + ...k3588-evb1-lp4-v10-lt6911uxc-dual-mipi.dts | 326 ++++++++++++++++++ 2 files changed, 327 insertions(+) create mode 100644 arch/arm64/boot/dts/rockchip/rk3588-evb1-lp4-v10-lt6911uxc-dual-mipi.dts diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile index 8e20df2459a0..5793a32bdfe7 100644 --- a/arch/arm64/boot/dts/rockchip/Makefile +++ b/arch/arm64/boot/dts/rockchip/Makefile @@ -205,6 +205,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-linux.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-linux-amp.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-linux-ipc.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-lt6911uxe.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-lt6911uxc-dual-mipi.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-rk628-hdmi2csi.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb2-lp4-v10.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb2-lp4-v10-edp.dtb diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-lp4-v10-lt6911uxc-dual-mipi.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-lp4-v10-lt6911uxc-dual-mipi.dts new file mode 100644 index 000000000000..4894f1bb31fa --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-lp4-v10-lt6911uxc-dual-mipi.dts @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + * + */ +/dts-v1/; + +#include "rk3588-evb1-lp4.dtsi" +#include "rk3588-android.dtsi" + +/ { + model = "Rockchip RK3588 EVB1 LP4 V10 Board + Rockchip RK3588 EVB V10 Extboard"; + compatible = "rockchip,rk3588-evb1-lp4-lt6911uxc-dual-mipi", "rockchip,rk3588"; + + vcc_mipicsi0: vcc-mipicsi0-regulator { + compatible = "regulator-fixed"; + gpio = <&gpio1 RK_PB1 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&mipicsi0_pwr>; + regulator-name = "vcc_mipicsi0"; + enable-active-high; + regulator-boot-on; + regulator-always-on; + }; + + vcc_mipidcphy0: vcc-mipidcphy0-regulator { + compatible = "regulator-fixed"; + gpio = <&gpio2 RK_PC4 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&mipidcphy0_pwr>; + regulator-name = "vcc_mipidcphy0"; + enable-active-high; + regulator-boot-on; + regulator-always-on; + }; + + ext_cam_clk: external-camera-clock { + compatible = "fixed-clock"; + clock-frequency = <24000000>; + clock-output-names = "CLK_CAMERA_24MHZ"; + #clock-cells = <0>; + }; +}; + +&csi2_dphy0_hw { + status = "okay"; +}; + +&csi2_dphy1_hw { + status = "okay"; +}; + +&csi2_dphy0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + hdmi_mipi_in: endpoint@1 { + reg = <1>; + remote-endpoint = <<6911uxc_out0>; + data-lanes = <1 2 3 4>; + }; + }; + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidphy0_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi2_csi2_input>; + }; + }; + }; +}; + +&csi2_dphy1 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + hdmi_mipi_in1: endpoint@1 { + reg = <1>; + remote-endpoint = <<6911uxc_out1>; + data-lanes = <1 2 3 4>; + }; + }; + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidphy1_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi0_csi2_input>; + }; + }; + }; +}; + +&i2c3 { + status = "okay"; + + lt6911uxc: lt6911uxc@2b { + compatible = "lontium,lt6911uxc"; + status = "okay"; + reg = <0x2b>; + clocks = <&ext_cam_clk>; + clock-names = "xvclk"; + power-domains = <&power RK3588_PD_VI>; + pinctrl-names = "default"; + pinctrl-0 = <<6911uxc_pin_1>; + interrupt-parent = <&gpio1>; + interrupts = ; + // reset-gpios = <&gpio1 RK_PB1 GPIO_ACTIVE_LOW>; + // power-gpios = <&gpio1 RK_PA6 GPIO_ACTIVE_HIGH>; + plugin-det-gpios = <&gpio1 RK_PB4 GPIO_ACTIVE_HIGH>; + rockchip,camera-module-index = <0>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "HDMI-MIPI2"; + rockchip,camera-module-lens-name = "LT6911UXC-2"; + + multi-dev-info { + dev-idx-l = <4>; + dev-idx-r = <2>; + combine-idx = <2>; + pixel-offset = <0>; + dev-num = <2>; + }; + + port { + lt6911uxc_out0: endpoint { + remote-endpoint = <&hdmi_mipi_in>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&i2c5 { + status = "okay"; + + lt6911uxc_1: lt6911uxc_1@2b { + compatible = "lontium,lt6911uxc"; + status = "okay"; + reg = <0x2b>; + clocks = <&ext_cam_clk>; + clock-names = "xvclk"; + power-domains = <&power RK3588_PD_VI>; + pinctrl-names = "default"; + pinctrl-0 = <<6911uxc_pin>; + interrupt-parent = <&gpio1>; + interrupts = ; + // reset-gpios = <&gpio1 RK_PB1 GPIO_ACTIVE_LOW>; + // power-gpios = <&gpio1 RK_PA6 GPIO_ACTIVE_HIGH>; + // plugin-det-gpios = <&gpio1 RK_PB4 GPIO_ACTIVE_LOW>; + plugin-det-gpios = <&gpio1 RK_PA1 GPIO_ACTIVE_HIGH>; + rockchip,camera-module-index = <1>; + rockchip,camera-module-facing = "front"; + rockchip,camera-module-name = "HDMI-MIPI0"; + rockchip,camera-module-lens-name = "LT6911UXC-1"; + + multi-dev-info { + dev-idx-l = <1>; + dev-idx-r = <0>; + combine-idx = <0>; + pixel-offset = <0>; + dev-num = <2>; + }; + + port { + lt6911uxc_out1: endpoint { + remote-endpoint = <&hdmi_mipi_in1>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&mipi_dcphy0 { + status = "okay"; +}; + +&mipi_dcphy1 { + status = "okay"; +}; + +&mipi0_csi2_hw { + status = "okay"; +}; + +&mipi1_csi2_hw { + status = "okay"; +}; + +&mipi2_csi2_hw { + status = "okay"; +}; + +&mipi3_csi2_hw { + status = "okay"; +}; + +&mipi4_csi2_hw { + status = "okay"; +}; + +&mipi5_csi2_hw { + status = "okay"; +}; + +&mipi0_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi0_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidphy1_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi0_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi_in0>; + }; + }; + }; +}; + +&mipi2_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi2_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidphy0_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi2_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi_in2>; + }; + }; + }; +}; + +&rkcif_mipi_lvds { + status = "okay"; + + port { + cif_mipi_in0: endpoint { + remote-endpoint = <&mipi0_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds2 { + status = "okay"; + + port { + cif_mipi_in2: endpoint { + remote-endpoint = <&mipi2_csi2_output>; + }; + }; +}; + +&rkcif { + status = "okay"; +}; + +&rkcif_mmu { + status = "okay"; +}; + +&pinctrl { + hdmiin { + lt6911uxc_pin: lt6911uxc-pin { + rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>, + <1 RK_PA1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + lt6911uxc_pin_1: lt6911uxc-pin-1 { + rockchip,pins = <1 RK_PB3 RK_FUNC_GPIO &pcfg_pull_none>, + <1 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; From 26290eb60d44f2db6a6f50e06562c3df038f34b9 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Thu, 28 Sep 2023 07:13:18 +0000 Subject: [PATCH 18/55] media: i2c: lt6911uxc: add dcphy params Change-Id: Id97a48e84fd84a70f4ba2d092436355a55f78779 Signed-off-by: Jianwei Fan --- drivers/media/i2c/lt6911uxc.c | 59 +++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/lt6911uxc.c b/drivers/media/i2c/lt6911uxc.c index 042c094ca284..dc30a145fa35 100644 --- a/drivers/media/i2c/lt6911uxc.c +++ b/drivers/media/i2c/lt6911uxc.c @@ -39,7 +39,7 @@ #define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x3) #define LT6911UXC_NAME "LT6911UXC" -#define LT6911UXC_LINK_FREQ_600M 600000000 +#define LT6911UXC_LINK_FREQ_650M 650000000 #define LT6911UXC_LINK_FREQ_400M 400000000 #define LT6911UXC_LINK_FREQ_300M 300000000 #define LT6911UXC_LINK_FREQ_200M 200000000 @@ -60,7 +60,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-2)"); static const s64 link_freq_menu_items[] = { - LT6911UXC_LINK_FREQ_600M, + LT6911UXC_LINK_FREQ_650M, LT6911UXC_LINK_FREQ_400M, LT6911UXC_LINK_FREQ_300M, LT6911UXC_LINK_FREQ_200M, @@ -114,6 +114,17 @@ struct lt6911uxc_mode { u32 mipi_freq_idx; }; +static struct rkmodule_csi_dphy_param rk3588_dcphy_param = { + .vendor = PHY_VENDOR_SAMSUNG, + .lp_vol_ref = 3, + .lp_hys_sw = {3, 0, 3, 0}, + .lp_escclk_pol_sel = {1, 1, 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 const struct v4l2_dv_timings_cap lt6911uxc_timings_cap = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ @@ -1010,6 +1021,7 @@ static long lt6911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) struct lt6911uxc *lt6911uxc = to_state(sd); struct device *dev = <6911uxc->i2c_client->dev; long ret = 0; + struct rkmodule_csi_dphy_param *dphy_param; struct rkmodule_capture_info *capture_info; switch (cmd) { @@ -1019,6 +1031,19 @@ static long lt6911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) case RKMODULE_GET_HDMI_MODE: *(int *)arg = RKMODULE_HDMIIN_MODE; break; + case RKMODULE_SET_CSI_DPHY_PARAM: + dphy_param = (struct rkmodule_csi_dphy_param *)arg; + if (dphy_param->vendor == PHY_VENDOR_SAMSUNG) + rk3588_dcphy_param = *dphy_param; + dev_dbg(<6911uxc->i2c_client->dev, + "sensor set dphy param\n"); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + dphy_param = (struct rkmodule_csi_dphy_param *)arg; + *dphy_param = rk3588_dcphy_param; + dev_dbg(<6911uxc->i2c_client->dev, + "sensor get dphy param\n"); + break; case RKMODULE_GET_CAPTURE_MODE: capture_info = (struct rkmodule_capture_info *)arg; if (lt6911uxc->csi_lanes_in_use == 8) { @@ -1045,6 +1070,7 @@ static long lt6911uxc_compat_ioctl32(struct v4l2_subdev *sd, struct rkmodule_inf *inf; long ret; int *seq; + struct rkmodule_csi_dphy_param *dphy_param; struct rkmodule_capture_info *capture_info; switch (cmd) { @@ -1078,6 +1104,35 @@ static long lt6911uxc_compat_ioctl32(struct v4l2_subdev *sd, } kfree(seq); 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 = lt6911uxc_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 = lt6911uxc_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; case RKMODULE_GET_CAPTURE_MODE: capture_info = kzalloc(sizeof(*capture_info), GFP_KERNEL); if (!capture_info) { From 8c83c66953f63e69931b81d789ec3e662a2fd339 Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Mon, 9 Oct 2023 10:48:09 +0800 Subject: [PATCH 19/55] arm64/configs: rockchip_linux_defconfig: enable CONFIG_GPIO_SYSCON The SYSCON driver was designed for using memory areas (registers) that are used in several subsystems. We can use bits in one register for various purposes and thus should be handled by various kernel subsystems. In Rockchip RK3328, the output only GPIO_MUTE pin, originally for codec mute control, can also be used for general purpose. It is manipulated by the GRF_SOC_CON10 register in GRF. Aside from the GPIO_MUTE pin, the HDMI pins can also be set in the same way. Therefore, we need to enable this feature in rockchip_linux_defconfig. Change-Id: If83f232121fb9fe54b6ff362d7911edf229d3878 Signed-off-by: Xing Zheng --- arch/arm64/configs/rockchip_linux_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/rockchip_linux_defconfig b/arch/arm64/configs/rockchip_linux_defconfig index 86af8ea238b8..0f9a0c62fc3c 100644 --- a/arch/arm64/configs/rockchip_linux_defconfig +++ b/arch/arm64/configs/rockchip_linux_defconfig @@ -258,6 +258,7 @@ CONFIG_PINCTRL_RK805=y CONFIG_PINCTRL_RK806=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_SYSCON=y CONFIG_POWER_RESET_GPIO=y CONFIG_POWER_RESET_GPIO_RESTART=y CONFIG_SYSCON_REBOOT_MODE=y From eade68257771b05e331137cae17661125cbea251 Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Mon, 9 Oct 2023 15:11:07 +0800 Subject: [PATCH 20/55] arm64/configs: rockchip_defconfig: enable CONFIG_GPIO_SYSCON The SYSCON driver was designed for using memory areas (registers) that are used in several subsystems. We can use bits in one register for various purposes and thus should be handled by various kernel subsystems. In Rockchip RK3328, the output only GPIO_MUTE pin, originally for codec mute control, can also be used for general purpose. It is manipulated by the GRF_SOC_CON10 register in GRF. Aside from the GPIO_MUTE pin, the HDMI pins can also be set in the same way. Therefore, we need to enable this feature in rockchip_defconfig. Change-Id: I0036bb8d3fb007f0ccab5d340d179886926a98ed Signed-off-by: Xing Zheng --- arch/arm64/configs/rockchip_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/rockchip_defconfig b/arch/arm64/configs/rockchip_defconfig index 2e6450c2c3a3..e411b933e58c 100644 --- a/arch/arm64/configs/rockchip_defconfig +++ b/arch/arm64/configs/rockchip_defconfig @@ -520,6 +520,7 @@ CONFIG_SPI_SPIDEV=y CONFIG_PINCTRL_RK805=y CONFIG_PINCTRL_RK806=y CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_SYSCON=y CONFIG_POWER_RESET_GPIO=y CONFIG_SYSCON_REBOOT_MODE=y CONFIG_TEST_POWER=y From 1341bf639b28ddc6d1dc9bf5ffb4a064e4e2ddd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 4 Oct 2022 15:49:00 +0200 Subject: [PATCH 21/55] BACKPORT: iio: adc: rockchip_saradc: do not use internal iio_dev lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The iio_device lock is only meant for internal use. Hence define a device local lock to protect against concurrent accesses. While at it, properly include "mutex.h" for mutex related APIs. Signed-off-by: Nuno Sá Acked-by: Heiko Stuebner Link: https://lore.kernel.org/r/20221004134909.1692021-8-nuno.sa@analog.com Signed-off-by: Jonathan Cameron Signed-off-by: Simon Xue (cherry picked from commit bb690935df8dd8f97612b422c669bd1a5fe87096) Change-Id: If04914774fec56645b1eb9bbb82d504fdd44779f --- drivers/iio/adc/rockchip_saradc.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index 4fb68c1143e6..4aa7afcd94ee 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -68,6 +69,8 @@ struct rockchip_saradc { struct clk *clk; struct completion completion; struct regulator *vref; + /* lock to protect against multiple access to the device */ + struct mutex lock; int uv_vref; struct reset_control *reset; const struct rockchip_saradc_data *data; @@ -189,22 +192,22 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev, #endif switch (mask) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); + mutex_lock(&info->lock); if (info->suspended) { - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&info->lock); return -EBUSY; } ret = rockchip_saradc_conversion(info, chan); if (ret) { rockchip_saradc_power_down(info); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&info->lock); return ret; } *val = info->last_val; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&info->lock); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: /* It is a dummy regulator */ @@ -476,7 +479,7 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p) int ret; int i, j = 0; - mutex_lock(&i_dev->mlock); + mutex_lock(&info->lock); for_each_set_bit(i, i_dev->active_scan_mask, i_dev->masklength) { const struct iio_chan_spec *chan = &i_dev->channels[i]; @@ -493,7 +496,7 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p) iio_push_to_buffers_with_timestamp(i_dev, &data, iio_get_time_ns(i_dev)); out: - mutex_unlock(&i_dev->mlock); + mutex_unlock(&info->lock); iio_trigger_notify_done(i_dev->trig); @@ -784,6 +787,8 @@ static int rockchip_saradc_probe(struct platform_device *pdev) return ret; } #endif + mutex_init(&info->lock); + return devm_iio_device_register(&pdev->dev, indio_dev); } @@ -794,14 +799,14 @@ static int rockchip_saradc_suspend(struct device *dev) struct rockchip_saradc *info = iio_priv(indio_dev); /* Avoid reading saradc when suspending */ - mutex_lock(&indio_dev->mlock); + mutex_lock(&info->lock); clk_disable_unprepare(info->clk); clk_disable_unprepare(info->pclk); regulator_disable(info->vref); info->suspended = true; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&info->lock); return 0; } From ea15f616ecce0c50bb3ae985d58ecbc75d056bc7 Mon Sep 17 00:00:00 2001 From: Cody Xie Date: Thu, 28 Sep 2023 15:21:15 +0800 Subject: [PATCH 22/55] media: i2c: maxim4c: Add regulator suppliers Change-Id: I637e20533515844f185167cda97a619cc475debd Signed-off-by: Cody Xie --- drivers/media/i2c/maxim4c/maxim4c_drv.c | 78 +++++++++++++++++++------ drivers/media/i2c/maxim4c/maxim4c_drv.h | 6 +- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/drivers/media/i2c/maxim4c/maxim4c_drv.c b/drivers/media/i2c/maxim4c/maxim4c_drv.c index 60d8955daac4..3ae0603384e0 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_drv.c +++ b/drivers/media/i2c/maxim4c/maxim4c_drv.c @@ -29,6 +29,9 @@ * V2.03.00 * 1. remote device add the maxim4c prefix to driver name. * + * V2.04.00 + * 1. Add regulator supplier dependencies. + * */ #include #include @@ -58,10 +61,15 @@ #include "maxim4c_api.h" -#define DRIVER_VERSION KERNEL_VERSION(2, 0x03, 0x00) +#define DRIVER_VERSION KERNEL_VERSION(2, 0x04, 0x00) #define MAXIM4C_XVCLK_FREQ 25000000 +static const char *const maxim4c_supply_names[MAXIM4C_NUM_SUPPLIES] = { + "vcc1v2", + "vcc1v8", +}; + static int maxim4c_check_local_chipid(maxim4c_t *maxim4c) { struct i2c_client *client = maxim4c->client; @@ -302,6 +310,13 @@ static inline u32 maxim4c_cal_delay(u32 cycles) static int maxim4c_local_device_power_on(maxim4c_t *maxim4c) { struct device *dev = &maxim4c->client->dev; + int ret; + + ret = regulator_bulk_enable(MAXIM4C_NUM_SUPPLIES, maxim4c->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + return -EINVAL; + } if (!IS_ERR(maxim4c->pwdn_gpio)) { dev_info(dev, "local device pwdn gpio on\n"); @@ -317,24 +332,30 @@ static int maxim4c_local_device_power_on(maxim4c_t *maxim4c) static void maxim4c_local_device_power_off(maxim4c_t *maxim4c) { struct device *dev = &maxim4c->client->dev; + int ret; if (!IS_ERR(maxim4c->pwdn_gpio)) { dev_info(dev, "local device pwdn gpio off\n"); gpiod_set_value_cansleep(maxim4c->pwdn_gpio, 0); } + + ret = regulator_bulk_disable(MAXIM4C_NUM_SUPPLIES, maxim4c->supplies); + if (ret < 0) { + dev_warn(dev, "Failed to disable regulators\n"); + } } static int maxim4c_remote_device_power_on(maxim4c_t *maxim4c) { struct device *dev = &maxim4c->client->dev; + int ret; - // remote PoC enable - if (!IS_ERR(maxim4c->pocen_gpio)) { - dev_info(dev, "remote device pocen gpio on\n"); - - gpiod_set_value_cansleep(maxim4c->pocen_gpio, 1); - usleep_range(5000, 10000); + dev_dbg(dev, "Turn PoC on\n"); + ret = regulator_enable(maxim4c->poc_regulator); + if (ret < 0) { + dev_err(dev, "Unable to turn PoC on\n"); + return ret; } return 0; @@ -343,13 +364,12 @@ static int maxim4c_remote_device_power_on(maxim4c_t *maxim4c) static int maxim4c_remote_device_power_off(maxim4c_t *maxim4c) { struct device *dev = &maxim4c->client->dev; + int ret; - // remote PoC enable - if (!IS_ERR(maxim4c->pocen_gpio)) { - dev_info(dev, "remote device pocen gpio off\n"); - - gpiod_set_value_cansleep(maxim4c->pocen_gpio, 0); - } + dev_dbg(dev, "Turn PoC off\n"); + ret = regulator_disable(maxim4c->poc_regulator); + if (ret < 0) + dev_warn(dev, "Unable to turn PoC off\n"); return 0; } @@ -581,6 +601,7 @@ static int maxim4c_probe(struct i2c_client *client, maxim4c_t *maxim4c = NULL; u32 chip_id; int ret = 0; + unsigned int i; dev_info(dev, "driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16, (DRIVER_VERSION & 0xff00) >> 8, DRIVER_VERSION & 0x00ff); @@ -621,14 +642,37 @@ static int maxim4c_probe(struct i2c_client *client, if (IS_ERR(maxim4c->pwdn_gpio)) dev_warn(dev, "Failed to get pwdn-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"); + for (i = 0; i < MAXIM4C_NUM_SUPPLIES; i++) + maxim4c->supplies[i].supply = maxim4c_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, MAXIM4C_NUM_SUPPLIES, + maxim4c->supplies); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Unable to get supply regulators\n"); + else + dev_warn(dev, "Get PoC regulator deferred\n"); + return ret; + } + + maxim4c->poc_regulator = devm_regulator_get(dev, "poc"); + if (IS_ERR(maxim4c->poc_regulator)) { + if (PTR_ERR(maxim4c->poc_regulator) != -EPROBE_DEFER) + dev_err(dev, "Unable to get PoC regulator (%ld)\n", + PTR_ERR(maxim4c->poc_regulator)); + else + dev_err(dev, "Get PoC regulator deferred\n"); + + ret = PTR_ERR(maxim4c->poc_regulator); +#if !MAXIM4C_TEST_PATTERN + return ret; +#endif + } + mutex_init(&maxim4c->mutex); ret = maxim4c_local_device_power_on(maxim4c); diff --git a/drivers/media/i2c/maxim4c/maxim4c_drv.h b/drivers/media/i2c/maxim4c/maxim4c_drv.h index 444f20f5d3e8..7ba49d42af4d 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_drv.h +++ b/drivers/media/i2c/maxim4c/maxim4c_drv.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,8 @@ #define MAX96712_CHIP_ID 0xA0 #define MAX96722_CHIP_ID 0xA1 +#define MAXIM4C_NUM_SUPPLIES 2 + enum { MAXIM4C_HOT_PLUG_OUT = 0, MAXIM4C_HOT_PLUG_IN, @@ -57,8 +60,9 @@ typedef struct maxim4c { struct i2c_client *client; struct clk *xvclk; struct gpio_desc *pwdn_gpio; - struct gpio_desc *pocen_gpio; struct gpio_desc *lock_gpio; + struct regulator_bulk_data supplies[MAXIM4C_NUM_SUPPLIES]; + struct regulator *poc_regulator; struct mutex mutex; From 423b02db66e1192ea9f5838846dade3dbc0602ae Mon Sep 17 00:00:00 2001 From: Cody Xie Date: Thu, 28 Sep 2023 15:21:59 +0800 Subject: [PATCH 23/55] arm64: dts: rockchip: rk3588-vehicle-evb-maxim-max9671(/2)2-d(c)phy0(/3): Add regulators Change-Id: Idb176416c330ecf11d646d7582377d82ae401f72 Signed-off-by: Cody Xie --- ...588-vehicle-evb-maxim-max96712-dcphy0.dtsi | 40 ++++++++++++++++++- ...588-vehicle-evb-maxim-max96712-dcphy1.dtsi | 40 ++++++++++++++++++- ...3588-vehicle-evb-maxim-max96712-dphy0.dtsi | 40 ++++++++++++++++++- ...3588-vehicle-evb-maxim-max96712-dphy3.dtsi | 39 +++++++++++++++++- ...3588-vehicle-evb-maxim-max96722-dphy0.dtsi | 40 ++++++++++++++++++- ...3588-vehicle-evb-maxim-max96722-dphy3.dtsi | 40 ++++++++++++++++++- 6 files changed, 233 insertions(+), 6 deletions(-) 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 6821fa84829d..85007840d55a 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 @@ -12,6 +12,42 @@ clock-frequency = <25000000>; clock-output-names = "max96712-dcphy0-osc"; }; + + max96712_dcphy0_vcc1v2: max96712-dcphy0-vcc1v2 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dcphy0_vcc1v2"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + startup-delay-us = <850>; + vin-supply = <&vcc5v0_sys>; + }; + + max96712_dcphy0_vcc1v8: max96712-dcphy0-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dcphy0_vcc1v8"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <200>; + vin-supply = <&vcc_3v3_s3>; + }; + + max96712_dcphy0_poc: max96712-dcphy0-poc { + compatible = "regulator-fixed"; + regulator-name = "max96712_dcphy0_poc"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + enable-active-high; + gpio = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>; + startup-delay-us = <1050>; + off-on-delay-us = <515000>; + vin-supply = <&vcc12v_dcin>; + }; }; &mipi_dcphy0 { @@ -64,8 +100,10 @@ power-domains = <&power RK3588_PD_VI>; rockchip,grf = <&sys_grf>; pwdn-gpios = <&gpio4 RK_PA4 GPIO_ACTIVE_HIGH>; - pocen-gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>; lock-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>; + vcc1v2-supply = <&max96712_dcphy0_vcc1v2>; + vcc1v8-supply = <&max96712_dcphy0_vcc1v8>; + poc-supply = <&max96712_dcphy0_poc>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; 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 2b0a04eff63f..9c7b3c7a4677 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 @@ -12,6 +12,42 @@ clock-frequency = <25000000>; clock-output-names = "max96712-dcphy1-osc"; }; + + max96712_dcphy1_vcc1v2: max96712-dcphy1-vcc1v2 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dcphy1_vcc1v2"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + startup-delay-us = <850>; + vin-supply = <&vcc5v0_sys>; + }; + + max96712_dcphy1_vcc1v8: max96712-dcphy1-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dcphy1_vcc1v8"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <200>; + vin-supply = <&vcc_3v3_s3>; + }; + + max96712_dcphy1_poc: max96712-dcphy1-poc { + compatible = "regulator-fixed"; + regulator-name = "max96712_dcphy1_poc"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + enable-active-high; + gpio = <&gpio3 RK_PA7 GPIO_ACTIVE_HIGH>; + startup-delay-us = <1050>; + off-on-delay-us = <515000>; + vin-supply = <&vcc12v_dcin>; + }; }; &mipi_dcphy1 { @@ -64,8 +100,10 @@ power-domains = <&power RK3588_PD_VI>; rockchip,grf = <&sys_grf>; pwdn-gpios = <&gpio4 RK_PD1 GPIO_ACTIVE_HIGH>; - pocen-gpios = <&gpio3 RK_PA7 GPIO_ACTIVE_HIGH>; lock-gpios = <&gpio4 RK_PD3 GPIO_ACTIVE_HIGH>; + vcc1v2-supply = <&max96712_dcphy1_vcc1v2>; + vcc1v8-supply = <&max96712_dcphy1_vcc1v8>; + poc-supply = <&max96712_dcphy1_poc>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; 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 b68f8f73607a..18580f723e28 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 @@ -12,6 +12,42 @@ clock-frequency = <25000000>; clock-output-names = "max96712-dphy0-osc0"; }; + + max96712_dphy0_vcc1v2: max96712-dphy0-vcc1v2 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dphy0_vcc1v2"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + startup-delay-us = <850>; + vin-supply = <&vcc5v0_sys>; + }; + + max96712_dphy0_vcc1v8: max96712-dphy0-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dphy0_vcc1v8"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <200>; + vin-supply = <&vcc_3v3_s3>; + }; + + max96712_dphy0_poc: max96712-dphy0-poc { + compatible = "regulator-fixed"; + regulator-name = "max96712_dphy0_poc"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + enable-active-high; + gpio = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>; + startup-delay-us = <1050>; + off-on-delay-us = <515000>; + vin-supply = <&vcc12v_dcin>; + }; }; &csi2_dphy0_hw { @@ -64,8 +100,10 @@ power-domains = <&power RK3588_PD_VI>; rockchip,grf = <&sys_grf>; pwdn-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_HIGH>; - pocen-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>; lock-gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_HIGH>; + vcc1v2-supply = <&max96712_dphy0_vcc1v2>; + vcc1v8-supply = <&max96712_dphy0_vcc1v8>; + poc-supply = <&max96712_dphy0_poc>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; 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 27ab0492cdfc..e146b16365b9 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 @@ -12,6 +12,41 @@ clock-frequency = <25000000>; clock-output-names = "max96712-dphy3-osc0"; }; + + max96712_dphy3_vcc1v2: max96712-dphy3-vcc1v2 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dphy3_vcc1v2"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + startup-delay-us = <850>; + vin-supply = <&vcc5v0_sys>; + }; + + max96712_dphy3_vcc1v8: max96712-dphy3-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dphy3_vcc1v8"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <200>; + vin-supply = <&vcc_3v3_s3>; + }; + + max96712_dphy3_poc: max96712-dphy3-poc { + compatible = "regulator-fixed"; + regulator-name = "max96712_dphy3_poc"; + regulator-boot-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + enable-active-high; + gpio = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; + startup-delay-us = <1050>; + off-on-delay-us = <515000>; + vin-supply = <&vcc12v_dcin>; + }; }; &csi2_dphy1_hw { @@ -64,8 +99,10 @@ power-domains = <&power RK3588_PD_VI>; rockchip,grf = <&sys_grf>; pwdn-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>; - pocen-gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; lock-gpios = <&gpio3 RK_PB4 GPIO_ACTIVE_HIGH>; + vcc1v2-supply = <&max96712_dphy3_vcc1v2>; + vcc1v8-supply = <&max96712_dphy3_vcc1v8>; + poc-supply = <&max96712_dphy3_poc>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; 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 45447510b7c1..becc8999e206 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 @@ -12,6 +12,42 @@ clock-frequency = <25000000>; clock-output-names = "max96722-dphy0-osc0"; }; + + max96722_dphy0_vcc1v2: max96722-dphy0-vcc1v2 { + compatible = "regulator-fixed"; + regulator-name = "max96722_dphy0_vcc1v2"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + startup-delay-us = <850>; + vin-supply = <&vcc5v0_sys>; + }; + + max96722_dphy0_vcc1v8: max96722-dphy0-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "max96722_dphy0_vcc1v8"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <200>; + vin-supply = <&vcc_3v3_s3>; + }; + + max96722_dphy0_poc: max96722-dphy0-poc { + compatible = "regulator-fixed"; + regulator-name = "max96722_dphy0_poc"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + enable-active-high; + gpio = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>; + startup-delay-us = <1050>; + off-on-delay-us = <515000>; + vin-supply = <&vcc12v_dcin>; + }; }; &csi2_dphy0_hw { @@ -64,8 +100,10 @@ power-domains = <&power RK3588_PD_VI>; rockchip,grf = <&sys_grf>; pwdn-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_HIGH>; - pocen-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>; lock-gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_HIGH>; + vcc1v2-supply = <&max96722_dphy0_vcc1v2>; + vcc1v8-supply = <&max96722_dphy0_vcc1v8>; + poc-supply = <&max96722_dphy0_poc>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; 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 ad176da3978d..ad1b25ef7b97 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 @@ -12,6 +12,42 @@ clock-frequency = <25000000>; clock-output-names = "max96722-dphy3-osc0"; }; + + max96722_dphy3_vcc1v2: max96722-dphy3-vcc1v2 { + compatible = "regulator-fixed"; + regulator-name = "max96722_dphy3_vcc1v2"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + startup-delay-us = <850>; + vin-supply = <&vcc5v0_sys>; + }; + + max96722_dphy3_vcc1v8: max96722-dphy3-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "max96722_dphy3_vcc1v8"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <200>; + vin-supply = <&vcc_3v3_s3>; + }; + + max96722_dphy3_poc: max96722-dphy3-poc { + compatible = "regulator-fixed"; + regulator-name = "max96722_dphy3_poc"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + enable-active-high; + gpio = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; + startup-delay-us = <1050>; + off-on-delay-us = <515000>; + vin-supply = <&vcc12v_dcin>; + }; }; &csi2_dphy1_hw { @@ -64,8 +100,10 @@ power-domains = <&power RK3588_PD_VI>; rockchip,grf = <&sys_grf>; pwdn-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>; - pocen-gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; lock-gpios = <&gpio3 RK_PB4 GPIO_ACTIVE_HIGH>; + vcc1v2-supply = <&max96722_dphy3_vcc1v2>; + vcc1v8-supply = <&max96722_dphy3_vcc1v8>; + poc-supply = <&max96722_dphy3_poc>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; From 368f7855705837966c19483857d9c867583f2cbe Mon Sep 17 00:00:00 2001 From: Cody Xie Date: Sat, 7 Oct 2023 19:57:41 +0800 Subject: [PATCH 24/55] arm64: dts: rockchip: rk3588-vehicle-evb-v22: Change regulators supply for cameras Change-Id: I6c004cc78d851500b6e1651419c6aa3b3f6a5a31 Signed-off-by: Cody Xie --- arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts index a214764f4d6c..2fe348b486b8 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts @@ -343,6 +343,14 @@ }; }; +&max96712_dphy3_vcc1v2 { + vin-supply = <&vcc5v0_buck>; +}; + +&max96712_dphy3_poc { + vin-supply = <&camera1_vcc12v_buck>; +}; + &avdd1v8_ddr_pll_s0 { regulator-state-mem { regulator-on-in-suspend; From f237af29a40cedd73bc562b154d3925bc2f9cdb6 Mon Sep 17 00:00:00 2001 From: Cody Xie Date: Sat, 7 Oct 2023 15:35:51 +0800 Subject: [PATCH 25/55] media: i2c: maxim4c: Add config ssc-ratio property Change-Id: I1178c5becbdf2ded1648e31ff609b7ce6a07bdf5 Signed-off-by: Cody Xie --- drivers/media/i2c/maxim4c/maxim4c_drv.c | 5 +++-- drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c | 12 ++++++++++++ drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/maxim4c/maxim4c_drv.c b/drivers/media/i2c/maxim4c/maxim4c_drv.c index 3ae0603384e0..49b612ba16ca 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_drv.c +++ b/drivers/media/i2c/maxim4c/maxim4c_drv.c @@ -29,8 +29,9 @@ * V2.03.00 * 1. remote device add the maxim4c prefix to driver name. * - * V2.04.00 + * V2.04.01 * 1. Add regulator supplier dependencies. + * 2. Add config ssc-ratio property * */ #include @@ -61,7 +62,7 @@ #include "maxim4c_api.h" -#define DRIVER_VERSION KERNEL_VERSION(2, 0x04, 0x00) +#define DRIVER_VERSION KERNEL_VERSION(2, 0x04, 0x01) #define MAXIM4C_XVCLK_FREQ 25000000 diff --git a/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c b/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c index 5734902e59f8..af5071a6b586 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c +++ b/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c @@ -182,6 +182,11 @@ int maxim4c_dphy_dpll_predef_set(maxim4c_t *maxim4c, s64 link_freq_hz) reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS, 0xf4); + reg_addr = 0x1C03 + 0x100 * phy_idx; + ret |= maxim4c_i2c_update_byte(client, reg_addr, + MAXIM4C_I2C_REG_ADDR_16BITS, + 0x07, phy_cfg->ssc_ratio); + // Set dpll data rate reg_addr = 0x0415 + 0x03 * phy_idx; ret |= maxim4c_i2c_update_byte(client, @@ -349,6 +354,12 @@ static int maxim4c_mipi_txphy_config_parse_dt(struct device *dev, phy_cfg->clock_mode = value; } + ret = of_property_read_u32(node, "ssc-ratio", &value); + if (ret == 0) { + dev_info(dev, "ssc-ratio property: %d", value); + phy_cfg->ssc_ratio = value; + } + sub_idx++; } } @@ -541,6 +552,7 @@ void maxim4c_mipi_txphy_data_init(maxim4c_t *maxim4c) phy_cfg->vc_ext_en = 0; phy_cfg->clock_master = 0; phy_cfg->clock_mode = MAXIM4C_TXPHY_DPLL_PREDEF; + phy_cfg->ssc_ratio = 0; } } EXPORT_SYMBOL(maxim4c_mipi_txphy_data_init); diff --git a/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h b/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h index 759488d71625..901d0aa66c86 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h +++ b/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h @@ -53,6 +53,7 @@ struct maxim4c_txphy_cfg { u8 vc_ext_en; u8 clock_master; u8 clock_mode; + u8 ssc_ratio; }; typedef struct maxim4c_mipi_txphy { From e5ab4cbe7f5312a3a5edbfd1bdacbd8390667752 Mon Sep 17 00:00:00 2001 From: Cody Xie Date: Sun, 8 Oct 2023 10:42:05 +0800 Subject: [PATCH 26/55] media: i2c: maxim4c: Add debugfs entry to change MIPI timing Change-Id: I3e215583975e7272ca1c655dc61a59abc6c07d4e Signed-off-by: Cody Xie --- drivers/media/i2c/maxim4c/Makefile | 3 +- drivers/media/i2c/maxim4c/maxim4c_api.h | 3 + drivers/media/i2c/maxim4c/maxim4c_debugfs.c | 60 +++++++++++++ drivers/media/i2c/maxim4c/maxim4c_drv.c | 17 +++- drivers/media/i2c/maxim4c/maxim4c_drv.h | 2 + .../media/i2c/maxim4c/maxim4c_mipi_txphy.c | 84 +++++++++++++++++++ .../media/i2c/maxim4c/maxim4c_mipi_txphy.h | 34 ++++++++ 7 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 drivers/media/i2c/maxim4c/maxim4c_debugfs.c diff --git a/drivers/media/i2c/maxim4c/Makefile b/drivers/media/i2c/maxim4c/Makefile index f1ee630c6c35..665a8669d041 100644 --- a/drivers/media/i2c/maxim4c/Makefile +++ b/drivers/media/i2c/maxim4c/Makefile @@ -7,7 +7,8 @@ maxim4c-objs += maxim4c_i2c.o \ maxim4c_remote.o \ maxim4c_pattern.o \ maxim4c_v4l2.o \ - maxim4c_drv.o + maxim4c_drv.o \ + maxim4c_debugfs.o obj-$(CONFIG_MAXIM4C_SER_MAX9295) += remote_max9295.o obj-$(CONFIG_MAXIM4C_SER_MAX96715) += remote_max96715.o diff --git a/drivers/media/i2c/maxim4c/maxim4c_api.h b/drivers/media/i2c/maxim4c/maxim4c_api.h index 6dd3bbca17d7..25f261c84a44 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_api.h +++ b/drivers/media/i2c/maxim4c/maxim4c_api.h @@ -98,4 +98,7 @@ int maxim4c_pattern_support_mode_init(maxim4c_t *maxim4c); int maxim4c_pattern_data_init(maxim4c_t *maxim4c); int maxim4c_pattern_enable(maxim4c_t *maxim4c, bool enable); +int maxim4c_dbgfs_init(maxim4c_t *maxim4c); +void maxim4c_dbgfs_deinit(maxim4c_t *maxim4c); + #endif /* __MAXIM4C_API_H__ */ diff --git a/drivers/media/i2c/maxim4c/maxim4c_debugfs.c b/drivers/media/i2c/maxim4c/maxim4c_debugfs.c new file mode 100644 index 000000000000..75dddd7114e8 --- /dev/null +++ b/drivers/media/i2c/maxim4c/maxim4c_debugfs.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Quad GMSL Deserializer debugfs helper functions + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + */ +#include +#include + +#include "maxim4c_api.h" + +int maxim4c_dbgfs_init(maxim4c_t *maxim4c) +{ + struct dentry *entry; + + entry = debugfs_create_dir("maxim4c", NULL); + + debugfs_create_u8("timing_override_en", 0600, entry, + &maxim4c->mipi_txphy.timing_override_en); + + debugfs_create_u8("t_hs_przero", 0600, entry, + &maxim4c->mipi_txphy.timing.t_hs_przero); + debugfs_create_u8("t_hs_prep", 0600, entry, + &maxim4c->mipi_txphy.timing.t_hs_prep); + debugfs_create_u8("t_clk_trail", 0600, entry, + &maxim4c->mipi_txphy.timing.t_clk_trail); + debugfs_create_u8("t_clk_przero", 0600, entry, + &maxim4c->mipi_txphy.timing.t_clk_przero); + debugfs_create_u8("t_lpx", 0600, entry, &maxim4c->mipi_txphy.timing.t_lpx); + debugfs_create_u8("t_hs_trail", 0600, entry, + &maxim4c->mipi_txphy.timing.t_hs_trail); + + debugfs_create_u8("t_clk_prep", 0600, entry, + &maxim4c->mipi_txphy.timing.t_clk_prep); + debugfs_create_u8("t_lpxesc", 0600, entry, + &maxim4c->mipi_txphy.timing.t_lpxesc); + + debugfs_create_u8("csi2_t_pre", 0600, entry, + &maxim4c->mipi_txphy.timing.csi2_t_pre); + debugfs_create_u8("csi2_t_post", 0600, entry, + &maxim4c->mipi_txphy.timing.csi2_t_post); + debugfs_create_u8("csi2_tx_gap", 0600, entry, + &maxim4c->mipi_txphy.timing.csi2_tx_gap); + debugfs_create_u32("csi2_twakeup", 0600, entry, + &maxim4c->mipi_txphy.timing.csi2_twakeup); + + maxim4c->dbgfs_root = entry; + + return 0; +} +EXPORT_SYMBOL(maxim4c_dbgfs_init); + +void maxim4c_dbgfs_deinit(maxim4c_t *maxim4c) +{ + debugfs_remove_recursive(maxim4c->dbgfs_root); +} +EXPORT_SYMBOL(maxim4c_dbgfs_deinit); diff --git a/drivers/media/i2c/maxim4c/maxim4c_drv.c b/drivers/media/i2c/maxim4c/maxim4c_drv.c index 49b612ba16ca..16d11afb4828 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_drv.c +++ b/drivers/media/i2c/maxim4c/maxim4c_drv.c @@ -29,9 +29,10 @@ * V2.03.00 * 1. remote device add the maxim4c prefix to driver name. * - * V2.04.01 + * V2.04.02 * 1. Add regulator supplier dependencies. * 2. Add config ssc-ratio property + * 3. Add debugfs entry to change MIPI timing * */ #include @@ -62,7 +63,7 @@ #include "maxim4c_api.h" -#define DRIVER_VERSION KERNEL_VERSION(2, 0x04, 0x01) +#define DRIVER_VERSION KERNEL_VERSION(2, 0x04, 0x02) #define MAXIM4C_XVCLK_FREQ 25000000 @@ -713,15 +714,19 @@ static int maxim4c_probe(struct i2c_client *client, maxim4c_module_data_init(maxim4c); maxim4c_module_parse_dt(maxim4c); + ret = maxim4c_dbgfs_init(maxim4c); + if (ret) + goto err_subdev_deinit; + #if (MAXIM4C_LOCAL_DES_ON_OFF_EN == 0) ret = maxim4c_module_hw_init(maxim4c); if (ret) - goto err_subdev_deinit; + goto err_dbgfs_deinit; #endif /* MAXIM4C_LOCAL_DES_ON_OFF_EN */ ret = maxim4c_remote_mfd_add_devices(maxim4c); if (ret) - goto err_subdev_deinit; + goto err_dbgfs_deinit; maxim4c_lock_irq_init(maxim4c); maxim4c_lock_state_work_init(maxim4c); @@ -732,6 +737,8 @@ static int maxim4c_probe(struct i2c_client *client, return 0; +err_dbgfs_deinit: + maxim4c_dbgfs_deinit(maxim4c); err_subdev_deinit: maxim4c_v4l2_subdev_deinit(maxim4c); err_power_off: @@ -748,6 +755,8 @@ static int maxim4c_remove(struct i2c_client *client) maxim4c_lock_state_work_deinit(maxim4c); + maxim4c_dbgfs_deinit(maxim4c); + maxim4c_v4l2_subdev_deinit(maxim4c); mutex_destroy(&maxim4c->mutex); diff --git a/drivers/media/i2c/maxim4c/maxim4c_drv.h b/drivers/media/i2c/maxim4c/maxim4c_drv.h index 7ba49d42af4d..d4542e15eeb2 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_drv.h +++ b/drivers/media/i2c/maxim4c/maxim4c_drv.h @@ -108,6 +108,8 @@ typedef struct maxim4c { struct mfd_cell remote_mfd_devs[MAXIM4C_LINK_ID_MAX]; maxim4c_remote_t *remote_device[MAXIM4C_LINK_ID_MAX]; + + struct dentry *dbgfs_root; } maxim4c_t; #endif /* __MAXIM4C_DRV_H__ */ diff --git a/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c b/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c index af5071a6b586..8776401149fb 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c +++ b/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c @@ -11,6 +11,82 @@ #include "maxim4c_api.h" +static int maxim4c_txphy_init_timing(maxim4c_t *maxim4c) +{ + struct i2c_client *client = maxim4c->client; + int ret = 0; + u16 reg_addr = 0; + u8 reg_mask; + u8 timing; + u8 phy_idx = 0; + + if (!maxim4c->mipi_txphy.timing_override_en) + return 0; + + timing = ((maxim4c->mipi_txphy.timing.t_hs_przero & 0x3) << 6 | + (maxim4c->mipi_txphy.timing.t_hs_prep & 0x3) << 4 | + (maxim4c->mipi_txphy.timing.t_clk_trail & 0x3) << 2 | + (maxim4c->mipi_txphy.timing.t_clk_przero & 0x3) << 0); + + ret |= maxim4c_i2c_write_byte(client, 0x08A1, + MAXIM4C_I2C_REG_ADDR_16BITS, timing); + + reg_mask = 0x0F; + timing = ((maxim4c->mipi_txphy.timing.t_lpx & 0x3) << 2 | + (maxim4c->mipi_txphy.timing.t_hs_trail & 0x3) << 0); + + ret |= maxim4c_i2c_update_byte( + client, 0x08A2, MAXIM4C_I2C_REG_ADDR_16BITS, reg_mask, timing); + + reg_mask = (0x3 << 6); + timing = (maxim4c->mipi_txphy.timing.t_lpxesc & 0x3) << 6; + ret |= maxim4c_i2c_update_byte( + client, 0x08A5, MAXIM4C_I2C_REG_ADDR_16BITS, reg_mask, timing); + + reg_mask = (0x7 << 5); + timing = (maxim4c->mipi_txphy.timing.t_lpxesc & 0x7) << 5; + ret |= maxim4c_i2c_update_byte( + client, 0x08A8, MAXIM4C_I2C_REG_ADDR_16BITS, reg_mask, timing); + + for (phy_idx = 0; phy_idx < MAXIM4C_TXPHY_ID_MAX; phy_idx++) { + reg_mask = 0xFF; + reg_addr = 0x0905 + 0x40 * phy_idx; + timing = maxim4c->mipi_txphy.timing.csi2_t_pre; + ret |= maxim4c_i2c_update_byte(client, reg_addr, + MAXIM4C_I2C_REG_ADDR_16BITS, + reg_mask, timing); + + reg_addr = 0x0906 + 0x40 * phy_idx; + timing = maxim4c->mipi_txphy.timing.csi2_t_post; + ret |= maxim4c_i2c_update_byte(client, reg_addr, + MAXIM4C_I2C_REG_ADDR_16BITS, + reg_mask, timing); + + reg_addr = 0x0907 + 0x40 * phy_idx; + timing = maxim4c->mipi_txphy.timing.csi2_tx_gap; + ret |= maxim4c_i2c_update_byte(client, reg_addr, + MAXIM4C_I2C_REG_ADDR_16BITS, + reg_mask, timing); + + reg_addr = 0x0908 + 0x40 * phy_idx; + timing = maxim4c->mipi_txphy.timing.csi2_twakeup & 0xFF; + ret |= maxim4c_i2c_update_byte(client, reg_addr, + MAXIM4C_I2C_REG_ADDR_16BITS, + reg_mask, timing); + timing = (maxim4c->mipi_txphy.timing.csi2_twakeup >> 8) & 0xFF; + ret |= maxim4c_i2c_update_byte(client, reg_addr + 1, + MAXIM4C_I2C_REG_ADDR_16BITS, + reg_mask, timing); + reg_mask = 0x7; + timing = (maxim4c->mipi_txphy.timing.csi2_twakeup >> 16) & 0x7; + ret |= maxim4c_i2c_update_byte(client, reg_addr + 2, + MAXIM4C_I2C_REG_ADDR_16BITS, + reg_mask, timing); + } + + return ret; +} + static int maxim4c_txphy_auto_init_deskew(maxim4c_t *maxim4c) { struct i2c_client *client = maxim4c->client; @@ -521,6 +597,9 @@ int maxim4c_mipi_txphy_hw_init(maxim4c_t *maxim4c) // mipi txphy auto init deskew ret |= maxim4c_txphy_auto_init_deskew(maxim4c); + // mipi txphy timing init + ret |= maxim4c_txphy_init_timing(maxim4c); + if (ret) { dev_err(dev, "%s: txphy hw init error\n", __func__); return ret; @@ -540,6 +619,11 @@ void maxim4c_mipi_txphy_data_init(maxim4c_t *maxim4c) mipi_txphy->force_clock_out_en = 1; mipi_txphy->force_clk0_en = 0; mipi_txphy->force_clk3_en = 0; + mipi_txphy->timing.t_lpx = 1; + mipi_txphy->timing.csi2_t_pre = 0x71; + mipi_txphy->timing.csi2_t_post = 0x19; + mipi_txphy->timing.csi2_tx_gap = 0x1C; + mipi_txphy->timing.csi2_twakeup = 0x100; for (i = 0; i < MAXIM4C_TXPHY_ID_MAX; i++) { phy_cfg = &mipi_txphy->phy_cfg[i]; diff --git a/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h b/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h index 901d0aa66c86..29416be78a82 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h +++ b/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h @@ -56,12 +56,46 @@ struct maxim4c_txphy_cfg { u8 ssc_ratio; }; +struct maxim4c_txphy_timing { + /* 0x8A1 */ + u8 t_hs_przero; + u8 t_hs_prep; + u8 t_clk_trail; + u8 t_clk_przero; + + /* 0x8A2 */ + u8 t_lpx; + u8 t_hs_trail; + + /* 0x8A5 */ + u8 t_clk_prep; + + /* 0x8A8 */ + u8 t_lpxesc; + + /* 0x8AE */ + u8 t_t3_post; + u8 t_t3_prep; + + /* 0x905 */ + u8 csi2_t_pre; + /* 0x906 */ + u8 csi2_t_post; + /* 0x907 */ + u8 csi2_tx_gap; + /* 0x908,0x909,0x90A */ + u32 csi2_twakeup; +}; + 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 */ + u8 timing_override_en; + struct maxim4c_txphy_timing timing; + struct maxim4c_txphy_cfg phy_cfg[MAXIM4C_TXPHY_ID_MAX]; } maxim4c_mipi_txphy_t; From f67504a486e7a87a405be7149cbf8a1f0dfb6728 Mon Sep 17 00:00:00 2001 From: David Wu Date: Sun, 8 Oct 2023 19:22:41 +0800 Subject: [PATCH 27/55] i2c: busses: i2c-rk3x: Fix msg length 32 for autostop tx only mode If autostop mode is enabled, such as RK3588, autostop mode requires statistics on the transmission length; for tx only mode, the device address is also counted as one byte, so the judgment conditions become different. Signed-off-by: David Wu Change-Id: I91c0b68fabb60952f630a98133423aa4cb1b9b48 --- drivers/i2c/busses/i2c-rk3x.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 71592edcf792..0d6dcece3a45 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -307,6 +307,13 @@ static bool rk3x_i2c_auto_stop(struct rk3x_i2c *i2c) if (len > 32) goto out; + /* For tx mode, one byte of the device address also needs to be counted, + * if the data length is equal to 32, which is actually 33 bytes, it would + * need to be divided into two parts, and needs to jump out of autostop. + */ + if (i2c->msg->len == 32 && i2c->mode == REG_CON_MOD_TX && !i2c->processed) + goto out; + i2c->state = STATE_STOP; con1 |= REG_CON1_TRANSFER_AUTO_STOP | REG_CON1_AUTO_STOP; From 006c0b1e7710d471119a69d6bd56917a15a85a0b Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 9 Oct 2023 11:36:48 +0800 Subject: [PATCH 28/55] i2c: busses: i2c-rk3x: Add i2c bus reset for scl hold timeout by slave If the scl has been pulled low for a long time and is not released, at this time, because the exception needs to end the I2C, a reset needs to be done to ensure that the state machine is restored. Set the timeout time to 200ms, and the debounce of the scl is pulled low to 100ms. Signed-off-by: David Wu Change-Id: Iee0c2afe2fbd59a30d1b7ef486d0856b00b88cc8 --- drivers/i2c/busses/i2c-rk3x.c | 48 +++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 0d6dcece3a45..4b424cf1b5eb 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #define REG_IEN 0x18 /* interrupt enable */ #define REG_IPD 0x1c /* interrupt pending */ #define REG_FCNT 0x20 /* finished count */ +#define REG_SCL_OE_DB 0x24 /* Slave hold scl debounce */ #define REG_CON1 0x228 /* control register1 */ /* Data buffer offsets */ @@ -87,6 +89,7 @@ enum { #define REG_INT_START BIT(4) /* START condition generated */ #define REG_INT_STOP BIT(5) /* STOP condition generated */ #define REG_INT_NAKRCV BIT(6) /* NACK received */ +#define REG_INT_SLV_HDSCL BIT(7) /* slave hold scl */ #define REG_INT_ALL 0xff /* Disable i2c all irqs */ @@ -97,7 +100,7 @@ enum { #define REG_CON1_NACK_AUTO_STOP BIT(2) /* Constants */ -#define WAIT_TIMEOUT 1000 /* ms */ +#define WAIT_TIMEOUT 200 /* ms */ #define DEFAULT_SCL_RATE (100 * 1000) /* Hz */ /** @@ -225,6 +228,9 @@ struct rk3x_i2c { struct notifier_block clk_rate_nb; bool autostop_supported; + struct reset_control *reset; + struct reset_control *reset_apb; + /* Settings */ struct i2c_timings t; @@ -990,9 +996,10 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate) { struct i2c_timings *t = &i2c->t; struct rk3x_i2c_calced_timings calc; + unsigned long period, time_hold = (WAIT_TIMEOUT / 2) * 1000000; u64 t_low_ns, t_high_ns; unsigned long flags; - u32 val; + u32 val, cnt; int ret; ret = i2c->soc_data->calc_timings(clk_rate, t, &calc); @@ -1007,6 +1014,10 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate) i2c_writel(i2c, val, REG_CON); i2c_writel(i2c, (calc.div_high << 16) | (calc.div_low & 0xffff), REG_CLKDIV); + + period = DIV_ROUND_UP(1000000000, clk_rate); + cnt = DIV_ROUND_UP(time_hold, period); + i2c_writel(i2c, cnt, REG_SCL_OE_DB); spin_unlock_irqrestore(&i2c->lock, flags); clk_disable(i2c->pclk); @@ -1172,12 +1183,30 @@ static int rk3x_i2c_wait_xfer_poll(struct rk3x_i2c *i2c, unsigned long xfer_time return !i2c->busy; } +/* + * Reset i2c controller, reset all i2c registers. + */ +static void rk3x_i2c_reset_controller(struct rk3x_i2c *i2c) +{ + if (!IS_ERR_OR_NULL(i2c->reset)) { + reset_control_assert(i2c->reset); + udelay(10); + reset_control_deassert(i2c->reset); + } + + if (!IS_ERR_OR_NULL(i2c->reset_apb)) { + reset_control_assert(i2c->reset_apb); + udelay(10); + reset_control_deassert(i2c->reset_apb); + } +} + static int rk3x_i2c_xfer_common(struct i2c_adapter *adap, struct i2c_msg *msgs, int num, bool polling) { struct rk3x_i2c *i2c = (struct rk3x_i2c *)adap->algo_data; unsigned long timeout, flags; - u32 val; + u32 val, ipd = 0; int ret = 0; int i; @@ -1196,7 +1225,7 @@ static int rk3x_i2c_xfer_common(struct i2c_adapter *adap, * rk3x_i2c_setup()). */ for (i = 0; i < num; i += ret) { - unsigned long xfer_time = 100; + unsigned long xfer_time = WAIT_TIMEOUT; int len; ret = rk3x_i2c_setup(i2c, msgs + i, num - i); @@ -1234,8 +1263,9 @@ static int rk3x_i2c_xfer_common(struct i2c_adapter *adap, spin_lock_irqsave(&i2c->lock, flags); if (timeout == 0) { + ipd = i2c_readl(i2c, REG_IPD); dev_err(i2c->dev, "timeout, ipd: 0x%02x, state: %d\n", - i2c_readl(i2c, REG_IPD), i2c->state); + ipd, i2c->state); /* Force a STOP condition without interrupt */ rk3x_i2c_disable_irq(i2c); @@ -1263,6 +1293,12 @@ static int rk3x_i2c_xfer_common(struct i2c_adapter *adap, spin_unlock_irqrestore(&i2c->lock, flags); + if ((ret == -ETIMEDOUT) && (ipd & REG_INT_SLV_HDSCL)) { + rk3x_i2c_reset_controller(i2c); + dev_err(i2c->dev, "SCL hold by slave, check your device.\n"); + rk3x_i2c_adapt_div(i2c, clk_get_rate(i2c->clk)); + } + return ret < 0 ? ret : num; } @@ -1609,6 +1645,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, i2c); + i2c->reset = devm_reset_control_get(&pdev->dev, "i2c"); if (!has_acpi_companion(&pdev->dev)) { if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) { /* Only one clock to use for bus clock and peripheral clock */ @@ -1617,6 +1654,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev) } else { i2c->clk = devm_clk_get(&pdev->dev, "i2c"); i2c->pclk = devm_clk_get(&pdev->dev, "pclk"); + i2c->reset_apb = devm_reset_control_get(&pdev->dev, "apb"); } if (IS_ERR(i2c->clk)) From 70d61088440d251cfe4aa86407aec2b8775003a7 Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 9 Oct 2023 11:56:27 +0800 Subject: [PATCH 29/55] arm64: dts: rockchip: rk3588s: Add reset for i2c Signed-off-by: David Wu Change-Id: Ib7ab98d3059bbefba1fbf3a26c82d8d35f155944 --- arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi index 6b46d1b10cf7..a9d49518b024 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi @@ -2965,6 +2965,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c0m0_xfer>; + resets = <&cru SRST_I2C0>, <&cru SRST_P_I2C0>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -5950,6 +5952,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c1m0_xfer>; + resets = <&cru SRST_I2C1>, <&cru SRST_P_I2C1>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -5963,6 +5967,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c2m0_xfer>; + resets = <&cru SRST_I2C2>, <&cru SRST_P_I2C2>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -5976,6 +5982,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c3m0_xfer>; + resets = <&cru SRST_I2C3>, <&cru SRST_P_I2C3>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -5989,6 +5997,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c4m0_xfer>; + resets = <&cru SRST_I2C4>, <&cru SRST_P_I2C4>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -6002,6 +6012,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c5m0_xfer>; + resets = <&cru SRST_I2C5>, <&cru SRST_P_I2C5>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -6429,6 +6441,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c6m0_xfer>; + resets = <&cru SRST_I2C6>, <&cru SRST_P_I2C6>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -6442,6 +6456,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c7m0_xfer>; + resets = <&cru SRST_I2C7>, <&cru SRST_P_I2C7>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -6455,6 +6471,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c8m0_xfer>; + resets = <&cru SRST_I2C8>, <&cru SRST_P_I2C8>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; From 1a2bbe66b1b30eef7f546141113cdefe8a1b41d7 Mon Sep 17 00:00:00 2001 From: Shunhua Lan Date: Sat, 7 Oct 2023 11:02:45 +0800 Subject: [PATCH 30/55] media: rockchip: hdmirx: add private v4l2 event add private v4l2 event: RK_HDMIRX_V4L2_EVENT_AUDIOINFO when audio info changed, driver will queue event and apps should chang audio record parameters Signed-off-by: Shunhua Lan Change-Id: I6fa6c951d648f546655475eede21d1769444741c --- include/uapi/linux/rk_hdmirx_config.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/uapi/linux/rk_hdmirx_config.h b/include/uapi/linux/rk_hdmirx_config.h index a0b7e436aed9..26dcea790013 100644 --- a/include/uapi/linux/rk_hdmirx_config.h +++ b/include/uapi/linux/rk_hdmirx_config.h @@ -82,4 +82,7 @@ enum hdmirx_color_space { #define RK_HDMIRX_V4L2_EVENT_SIGNAL_LOST \ (V4L2_EVENT_PRIVATE_START + 1) +#define RK_HDMIRX_V4L2_EVENT_AUDIOINFO \ + (V4L2_EVENT_PRIVATE_START + 2) + #endif /* _UAPI_RK_HDMIRX_CONFIG_H */ From 95aa09af433a6a92cc334f66c799687d16e4123c Mon Sep 17 00:00:00 2001 From: Shunhua Lan Date: Sat, 7 Oct 2023 11:06:35 +0800 Subject: [PATCH 31/55] media: rockchip: hdmirx: implement RK_HDMIRX_V4L2_EVENT_AUDIOINFO event Signed-off-by: Shunhua Lan Change-Id: I3f0594807d86990121e45460df359b33b6594c21 --- drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c index dcd49b65ea94..12a2ca89e52a 100644 --- a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c +++ b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c @@ -471,6 +471,7 @@ static int hdmirx_subscribe_event(struct v4l2_fh *fh, case V4L2_EVENT_CTRL: return v4l2_ctrl_subscribe_event(fh, sub); case RK_HDMIRX_V4L2_EVENT_SIGNAL_LOST: + case RK_HDMIRX_V4L2_EVENT_AUDIOINFO: return v4l2_event_subscribe(fh, sub, 0, NULL); default: @@ -2353,6 +2354,15 @@ static int hdmirx_register_stream_vdev(struct hdmirx_stream *stream) return 0; } +static void process_audio_change(struct rk_hdmirx_dev *hdmirx_dev) +{ + struct hdmirx_stream *stream = &hdmirx_dev->stream; + const struct v4l2_event evt_audio_info = { + .type = RK_HDMIRX_V4L2_EVENT_AUDIOINFO, + }; + v4l2_event_queue(&stream->vdev, &evt_audio_info); +} + static void process_signal_change(struct rk_hdmirx_dev *hdmirx_dev) { struct hdmirx_stream *stream = &hdmirx_dev->stream; @@ -3248,6 +3258,7 @@ static void hdmirx_delayed_work_audio(struct work_struct *work) if (!hdmirx_dev->audio_present) { dev_info(hdmirx_dev->dev, "audio on"); hdmirx_audio_handle_plugged_change(hdmirx_dev, 1); + process_audio_change(hdmirx_dev); hdmirx_dev->audio_present = true; } if (cur_state - init_state > 16 && cur_state - pre_state > 0) @@ -3258,6 +3269,7 @@ static void hdmirx_delayed_work_audio(struct work_struct *work) if (hdmirx_dev->audio_present) { dev_info(hdmirx_dev->dev, "audio off"); hdmirx_audio_handle_plugged_change(hdmirx_dev, 0); + process_audio_change(hdmirx_dev); hdmirx_dev->audio_present = false; } } From cd5a25f0f1cdfaea64571eb428fc94f4701f4d7d Mon Sep 17 00:00:00 2001 From: Shunhua Lan Date: Sat, 7 Oct 2023 16:01:33 +0800 Subject: [PATCH 32/55] media: rockchip: hdmirx: add new controls for audio status and audio rate Signed-off-by: Shunhua Lan Change-Id: I9fd34740ee0417c5dd0f5bf61b8070b162205768 --- .../platform/rockchip/hdmirx/rk_hdmirx.c | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c index 12a2ca89e52a..9775b20ba518 100644 --- a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c +++ b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -188,6 +189,8 @@ struct rk_hdmirx_dev { struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler hdl; struct v4l2_ctrl *detect_tx_5v_ctrl; + struct v4l2_ctrl *audio_sampling_rate_ctrl; + struct v4l2_ctrl *audio_present_ctrl; struct v4l2_dv_timings timings; struct gpio_desc *hdmirx_det_gpio; struct work_struct work_wdt_config; @@ -4187,6 +4190,50 @@ static void hdmirx_cancel_cpu_limit_freq(struct rk_hdmirx_dev *hdmirx_dev) dev_err(hdmirx_dev->dev, "%s freq qos nod add\n", __func__); } +static int hdmirx_get_custom_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rk_hdmirx_dev *hdmirx_dev = container_of(ctrl->handler, struct rk_hdmirx_dev, hdl); + int ret = 0; + + if (ctrl->id == RK_V4L2_CID_AUDIO_SAMPLING_RATE) { + *ctrl->p_new.p_s32 = hdmirx_dev->audio_state.fs_audio; + } else if (ctrl->id == RK_V4L2_CID_AUDIO_PRESENT) { + *ctrl->p_new.p_s32 = tx_5v_power_present(hdmirx_dev) ? + hdmirx_dev->audio_present : 0; + } else { + ret = -EINVAL; + } + return ret; +} + +static const struct v4l2_ctrl_ops hdmirx_custom_ctrl_ops = { + .g_volatile_ctrl = hdmirx_get_custom_ctrl, +}; + +static const struct v4l2_ctrl_config hdmirx_ctrl_audio_sampling_rate = { + .ops = &hdmirx_custom_ctrl_ops, + .id = RK_V4L2_CID_AUDIO_SAMPLING_RATE, + .name = "Audio sampling rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 768000, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, +}; + +static const struct v4l2_ctrl_config hdmirx_ctrl_audio_present = { + .ops = &hdmirx_custom_ctrl_ops, + .id = RK_V4L2_CID_AUDIO_PRESENT, + .name = "Audio present", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, +}; + static int hdmirx_probe(struct platform_device *pdev) { const struct v4l2_dv_timings timings_def = HDMIRX_DEFAULT_TIMING; @@ -4300,10 +4347,19 @@ static int hdmirx_probe(struct platform_device *pdev) strscpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name)); hdl = &hdmirx_dev->hdl; - v4l2_ctrl_handler_init(hdl, 1); + v4l2_ctrl_handler_init(hdl, 3); hdmirx_dev->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); + /* custom controls */ + hdmirx_dev->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom(hdl, + &hdmirx_ctrl_audio_sampling_rate, NULL); + if (hdmirx_dev->audio_sampling_rate_ctrl) + hdmirx_dev->audio_sampling_rate_ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + hdmirx_dev->audio_present_ctrl = v4l2_ctrl_new_custom(hdl, + &hdmirx_ctrl_audio_present, NULL); + if (hdmirx_dev->audio_present_ctrl) + hdmirx_dev->audio_present_ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; if (hdl->error) { dev_err(dev, "v4l2 ctrl handler init failed!\n"); ret = hdl->error; From c51724a2c1b72c836255d3eace0b424326302323 Mon Sep 17 00:00:00 2001 From: Yandong Lin Date: Mon, 9 Oct 2023 15:33:33 +0800 Subject: [PATCH 33/55] video: rockchip: mpp: vepu2: add iommu fault handle Signed-off-by: Yandong Lin Change-Id: If6dac8d783d5b6d3b344ab2f511503af9c322073 --- drivers/video/rockchip/mpp/mpp_vepu2.c | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/video/rockchip/mpp/mpp_vepu2.c b/drivers/video/rockchip/mpp/mpp_vepu2.c index 395a5b69333f..fb5f5e5c28e3 100644 --- a/drivers/video/rockchip/mpp/mpp_vepu2.c +++ b/drivers/video/rockchip/mpp/mpp_vepu2.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "mpp_debug.h" #include "mpp_common.h" @@ -882,6 +883,48 @@ static int vepu_reset(struct mpp_dev *mpp) return 0; } +static int vepu2_iommu_fault_handle(struct iommu_domain *iommu, struct device *iommu_dev, + unsigned long iova, int status, void *arg) +{ + struct mpp_dev *mpp = (struct mpp_dev *)arg; + struct mpp_task *mpp_task; + struct vepu_dev *enc = to_vepu_dev(mpp); + struct vepu_ccu *ccu = enc->ccu; + + dev_err(iommu_dev, "fault addr 0x%08lx status %x arg %p\n", + iova, status, arg); + + if (ccu) { + int i; + struct mpp_dev *core; + + for (i = 0; i < ccu->core_num; i++) { + core = ccu->cores[i]; + if (core->iommu_info && (&core->iommu_info->pdev->dev == iommu_dev)) { + mpp = core; + break; + } + } + } + + if (!mpp) { + dev_err(iommu_dev, "pagefault without device to handle\n"); + return 0; + } + mpp_task = mpp->cur_task; + if (mpp_task) + mpp_task_dump_mem_region(mpp, mpp_task); + + mpp_task_dump_hw_reg(mpp); + /* + * Mask iommu irq, in order for iommu not repeatedly trigger pagefault. + * Until the pagefault task finish by hw timeout. + */ + rockchip_iommu_mask_irq(mpp->dev); + + return 0; +} + static struct mpp_hw_ops vepu_v2_hw_ops = { .init = vepu_init, .clk_on = vepu_clk_on, @@ -1100,6 +1143,7 @@ static int vepu_core_probe(struct platform_device *pdev) return -EINVAL; } + mpp->fault_handler = vepu2_iommu_fault_handle; mpp->session_max_buffers = VEPU2_SESSION_MAX_BUFFERS; vepu_procfs_init(mpp); vepu_procfs_ccu_init(mpp); @@ -1149,6 +1193,7 @@ static int vepu_probe_default(struct platform_device *pdev) return -EINVAL; } + mpp->fault_handler = vepu2_iommu_fault_handle; mpp->session_max_buffers = VEPU2_SESSION_MAX_BUFFERS; vepu_procfs_init(mpp); /* register current device to mpp service */ From ca7fd6569330a5bbde402d518e7ea33f4ae362ac Mon Sep 17 00:00:00 2001 From: Yandong Lin Date: Mon, 9 Oct 2023 17:06:01 +0800 Subject: [PATCH 34/55] video: rockchip: mpp: rkvenc2: optimize iommu fault handle 1. There are two rkvenc cores and share a iommu domain. When they work at the same time, the arg with iommu fault handle will be mismatch with real device. So find the real device according to iommu_dev. 2. config appropriate timeout threshold and it can trigger hw timeout when pagefault. Signed-off-by: Yandong Lin Change-Id: If816e43c9732d6cefc60d80efbaf297001d500ff --- drivers/video/rockchip/mpp/mpp_rkvenc2.c | 34 ++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/drivers/video/rockchip/mpp/mpp_rkvenc2.c b/drivers/video/rockchip/mpp/mpp_rkvenc2.c index 5bd70546150c..5ea47d99163e 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvenc2.c +++ b/drivers/video/rockchip/mpp/mpp_rkvenc2.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "mpp_debug.h" #include "mpp_iommu.h" @@ -44,6 +45,8 @@ #define RKVENC_MAX_DCHS_ID 4 #define RKVENC_MAX_SLICE_FIFO_LEN 256 #define RKVENC_SCLR_DONE_STA BIT(2) +#define RKVENC_WDG 0x38 +#define TIMEOUT_MS 100 #define to_rkvenc_info(info) \ container_of(info, struct rkvenc_hw_info, hw) @@ -1194,6 +1197,7 @@ static int rkvenc_run(struct mpp_dev *mpp, struct mpp_task *mpp_task) struct rkvenc_task *task = to_rkvenc_task(mpp_task); struct rkvenc_hw_info *hw = enc->hw_info; u32 timing_en = mpp->srv->timing_en; + u32 timeout_thd; mpp_debug_enter(); @@ -1247,6 +1251,13 @@ static int rkvenc_run(struct mpp_dev *mpp, struct mpp_task *mpp_task) /* Flush the register before the start the device */ wmb(); + /* + * reconfig timeout threshold. + * bit0-bit23,x1024 core clk cycles + */ + timeout_thd = mpp_read(mpp, RKVENC_WDG) & 0xff000000; + timeout_thd |= TIMEOUT_MS * clk_get_rate(enc->core_clk_info.clk) / 1024000; + mpp_write(mpp, RKVENC_WDG, timeout_thd); mpp_write(mpp, enc->hw_info->enc_start_base, start_val); mpp_task_run_end(mpp_task, timing_en); @@ -1880,7 +1891,7 @@ static int rkvenc_soft_reset(struct mpp_dev *mpp) /* safe reset */ mpp_write(mpp, hw->int_mask_base, 0x3FF); - mpp_write(mpp, hw->enc_clr_base, 0x1); + mpp_write(mpp, hw->enc_clr_base, 0x3); ret = readl_relaxed_poll_timeout(mpp->reg_base + hw->int_sta_base, rst_status, rst_status & RKVENC_SCLR_DONE_STA, @@ -2404,14 +2415,33 @@ static int rkvenc2_iommu_fault_handle(struct iommu_domain *iommu, { struct mpp_dev *mpp = (struct mpp_dev *)arg; struct rkvenc_dev *enc = to_rkvenc_dev(mpp); - struct mpp_task *mpp_task = mpp->cur_task; + struct mpp_task *mpp_task; + struct rkvenc_ccu *ccu = enc->ccu; + if (ccu) { + struct rkvenc_dev *core = NULL, *n; + + list_for_each_entry_safe(core, n, &ccu->core_list, core_link) { + if (core->mpp.iommu_info && + (&core->mpp.iommu_info->pdev->dev == iommu_dev)) { + mpp = &core->mpp; + break; + } + } + } + mpp_task = mpp->cur_task; dev_info(mpp->dev, "core %d page fault found dchs %08x\n", mpp->core_id, mpp_read_relaxed(&enc->mpp, DCHS_REG_OFFSET)); if (mpp_task) mpp_task_dump_mem_region(mpp, mpp_task); + /* + * Mask iommu irq, in order for iommu not repeatedly trigger pagefault. + * Until the pagefault task finish by hw timeout. + */ + rockchip_iommu_mask_irq(mpp->dev); + return 0; } From de1cbb0ebd2d3b3e66bf6682af5798d76f423c68 Mon Sep 17 00:00:00 2001 From: Yandong Lin Date: Tue, 10 Oct 2023 10:07:45 +0800 Subject: [PATCH 35/55] video: rockchip: mpp: fix iommu fault irq triggered repeatedly Add mask iommu irq in iommu fault handle func to prevent iommu trigger pagefault repeatedly. Signed-off-by: Yandong Lin Change-Id: I11c9b918e3a9c3af576b7f82cbdc7dbeb960dbd8 --- drivers/video/rockchip/mpp/mpp_iommu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/video/rockchip/mpp/mpp_iommu.c b/drivers/video/rockchip/mpp/mpp_iommu.c index 116ec664ec00..1abbfb74f4e2 100644 --- a/drivers/video/rockchip/mpp/mpp_iommu.c +++ b/drivers/video/rockchip/mpp/mpp_iommu.c @@ -463,6 +463,12 @@ static int mpp_iommu_handle(struct iommu_domain *iommu, else mpp_task_dump_hw_reg(mpp); + /* + * Mask iommu irq, in order for iommu not repeatedly trigger pagefault. + * Until the pagefault task finish by hw timeout. + */ + rockchip_iommu_mask_irq(mpp->dev); + return 0; } From bbe13f289fc573df0b69c8405aa15e246f6420a2 Mon Sep 17 00:00:00 2001 From: Wangqiang Guo Date: Tue, 10 Oct 2023 07:09:20 +0000 Subject: [PATCH 36/55] input: sensors: add icm4260x driver. Change-Id: Ic1951c136bb81c0a93229e75e39605303cef3105 Signed-off-by: Wangqiang Guo --- drivers/input/sensors/accel/Kconfig | 6 + drivers/input/sensors/accel/Makefile | 1 + drivers/input/sensors/accel/icm4260x_acc.c | 470 +++++++++++++++++++++ drivers/input/sensors/gyro/Kconfig | 6 + drivers/input/sensors/gyro/Makefile | 1 + drivers/input/sensors/gyro/icm4260x_gyro.c | 211 +++++++++ include/linux/icm4260x.h | 432 +++++++++++++++++++ include/linux/sensor-dev.h | 2 + 8 files changed, 1129 insertions(+) create mode 100644 drivers/input/sensors/accel/icm4260x_acc.c create mode 100644 drivers/input/sensors/gyro/icm4260x_gyro.c create mode 100644 include/linux/icm4260x.h diff --git a/drivers/input/sensors/accel/Kconfig b/drivers/input/sensors/accel/Kconfig index 4576b8ccdb69..fb10b4c50c83 100644 --- a/drivers/input/sensors/accel/Kconfig +++ b/drivers/input/sensors/accel/Kconfig @@ -158,6 +158,12 @@ config ICM2060X_ACC To have support for your specific gsesnor you will have to select the proper drivers which depend on this option. +config ICM4260X_ACC + tristate "gsensor icm4260x" + help + To have support for your specific gsesnor you will have to + select the proper drivers which depend on this option. + config IAM20680_ACC tristate "gsensor iam20680" default n diff --git a/drivers/input/sensors/accel/Makefile b/drivers/input/sensors/accel/Makefile index 96f6cc6ee8e9..67e0bcdde3da 100644 --- a/drivers/input/sensors/accel/Makefile +++ b/drivers/input/sensors/accel/Makefile @@ -21,5 +21,6 @@ obj-$(CONFIG_GS_DA215S) += da215s/ obj-$(CONFIG_GS_DA223) += da223.o obj-$(CONFIG_GS_DA228E) += da228e/ obj-$(CONFIG_ICM2060X_ACC) += icm2060x_acc.o +obj-$(CONFIG_ICM4260X_ACC) += icm4260x_acc.o da223-y := da223_cust.o da223_core.o obj-$(CONFIG_IAM20680_ACC) += iam20680_acc.o diff --git a/drivers/input/sensors/accel/icm4260x_acc.c b/drivers/input/sensors/accel/icm4260x_acc.c new file mode 100644 index 000000000000..c2a3b2c9ee4f --- /dev/null +++ b/drivers/input/sensors/accel/icm4260x_acc.c @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Rockchip Co.,Ltd. + * Author: Wangqiang Guo + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include + +/** + * icm4260x_set_idle() - Set Idle bit in PWR_MGMT_0 register + * @client: struct i2c_client.. + * + * Set ACCEL_LP_CLK_SEL as well when necessary with a proper wait + * + * Return: 0 when successful. + */ +static int icm4260x_set_idle(struct i2c_client *client) +{ + u8 reg_pwr_mgmt_0; + u8 d; + int ret = 0; + + reg_pwr_mgmt_0 = sensor_read_reg(client, ICM4260X_PWR_MGMT_0); + /* set Idle bit. + * when accel LPM is already enabled, set ACCEL_LP_CLK_SEL bit as well. + */ + d = reg_pwr_mgmt_0; + d |= BIT_IDLE; + if ((d & BIT_ACCEL_MODE_MASK) == BIT_ACCEL_MODE_LPM) + d |= BIT_ACCEL_LP_CLK_SEL; + + ret = sensor_write_reg(client, ICM4260X_PWR_MGMT_0, d); + usleep_range(20, 21); + + return ret; +} + +/** + * icm4260x_mreg_read() - Multiple byte read from MREG area. + * @client: struct i2c_client. + * @addr: MREG register start address including bank in upper byte. + * @len: length to read in byte. + * @data: pointer to store read data. + * + * Return: 0 when successful. + */ +static int icm4260x_mreg_read(struct i2c_client *client, int addr, int len, u8 *data) +{ + int ret; + u8 reg_pwr_mgmt_0; + + reg_pwr_mgmt_0 = sensor_read_reg(client, ICM4260X_PWR_MGMT_0); + + ret = icm4260x_set_idle(client); + if (ret) + return ret; + + ret = sensor_write_reg(client, ICM4260X_BLK_SEL_R, (addr >> 8) & 0xff); + usleep_range(INV_ICM42607_BLK_SEL_WAIT_US, + INV_ICM42607_BLK_SEL_WAIT_US + 1); + if (ret) + goto restore_bank; + + ret = sensor_write_reg(client, ICM4260X_MADDR_R, addr & 0xff); + usleep_range(INV_ICM42607_MADDR_WAIT_US, + INV_ICM42607_MADDR_WAIT_US + 1); + if (ret) + goto restore_bank; + + *data = ICM4260X_M_R; + ret = sensor_rx_data(client, data, len); + usleep_range(INV_ICM42607_M_RW_WAIT_US, + INV_ICM42607_M_RW_WAIT_US + 1); + if (ret) + goto restore_bank; + +restore_bank: + ret |= sensor_write_reg(client, ICM4260X_BLK_SEL_R, 0); + usleep_range(INV_ICM42607_BLK_SEL_WAIT_US, + INV_ICM42607_BLK_SEL_WAIT_US + 1); + + ret |= sensor_write_reg(client, ICM4260X_PWR_MGMT_0, reg_pwr_mgmt_0); + + return ret; +} + +/** + * icm4260x_mreg_single_write() - Single byte write to MREG area. + * @client: struct i2c_client. + * @addr: MREG register address including bank in upper byte. + * @data: data to write. + * + * Return: 0 when successful. + */ +static int icm4260x_mreg_single_write(struct i2c_client *client, int addr, u8 data) +{ + int ret; + u8 reg_pwr_mgmt_0; + + reg_pwr_mgmt_0 = sensor_read_reg(client, ICM4260X_PWR_MGMT_0); + + ret = icm4260x_set_idle(client); + if (ret) + return ret; + + ret = sensor_write_reg(client, ICM4260X_BLK_SEL_W, (addr >> 8) & 0xff); + usleep_range(INV_ICM42607_BLK_SEL_WAIT_US, + INV_ICM42607_BLK_SEL_WAIT_US + 1); + if (ret) + goto restore_bank; + + ret = sensor_write_reg(client, ICM4260X_MADDR_W, addr & 0xff); + usleep_range(INV_ICM42607_MADDR_WAIT_US, + INV_ICM42607_MADDR_WAIT_US + 1); + if (ret) + goto restore_bank; + + ret = sensor_write_reg(client, ICM4260X_M_W, data); + usleep_range(INV_ICM42607_M_RW_WAIT_US, + INV_ICM42607_M_RW_WAIT_US + 1); + if (ret) + goto restore_bank; + +restore_bank: + ret |= sensor_write_reg(client, ICM4260X_BLK_SEL_W, 0); + usleep_range(INV_ICM42607_BLK_SEL_WAIT_US, + INV_ICM42607_BLK_SEL_WAIT_US + 1); + + ret |= sensor_write_reg(client, ICM4260X_PWR_MGMT_0, reg_pwr_mgmt_0); + + return ret; +} + +/* + * OTP reload procedure. + */ +static int icm4260x_otp_reload(struct i2c_client *client) +{ + int ret; + u8 rb = 0; + + /* set idle bit */ + ret = icm4260x_set_idle(client); + if (ret) + return ret; + + /* Set OTP_COPY_MODE to 2'b01 */ + ret = icm4260x_mreg_read(client, ICM4260X_OTP_CONFIG_MREG_TOP1, 1, &rb); + if (ret) + return ret; + rb &= ~OTP_COPY_MODE_MASK; + rb |= BIT_OTP_COPY_NORMAL; + ret = icm4260x_mreg_single_write(client, ICM4260X_OTP_CONFIG_MREG_TOP1, rb); + if (ret) + return ret; + + /* set OTP_PWR_DOWN to 1'b0 and wait for 300us */ + ret = icm4260x_mreg_read(client, ICM4260X_OTP_CTRL7_MREG_OTP, 1, &rb); + if (ret) + return ret; + rb &= ~BIT_OTP_PWR_DOWN; + ret = icm4260x_mreg_single_write(client, ICM4260X_OTP_CTRL7_MREG_OTP, rb); + if (ret) + return ret; + usleep_range(300, 400); + + /* set OTP_RELOAD to 1'b1 and wait for 280us */ + ret = icm4260x_mreg_read(client, ICM4260X_OTP_CTRL7_MREG_OTP, 1, &rb); + if (ret) + return ret; + rb |= BIT_OTP_RELOAD; + ret = icm4260x_mreg_single_write(client, ICM4260X_OTP_CTRL7_MREG_OTP, rb); + if (ret) + return ret; + usleep_range(280, 380); + + return 0; +} + +static int sensor_active(struct i2c_client *client, int enable, int rate) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + u8 status = 0; + + sensor->ops->ctrl_data = sensor_read_reg(client, sensor->ops->ctrl_reg); + + if (!enable) { + status = (0xff & ~BIT_ACCEL_MODE_MASK); + sensor->ops->ctrl_data &= status; + } else { + status = BIT_ACCEL_MODE_LNM; + sensor->ops->ctrl_data |= status; + sensor->ops->ctrl_data &= ~BIT_IDLE; + } + + result = sensor_write_reg(client, sensor->ops->ctrl_reg, + sensor->ops->ctrl_data); + if (result) { + dev_err(&client->dev, + "%s: fail to set pwr_mgmt0(%d)\n", __func__, result); + return result; + } + usleep_range(250, 260); + + return result; +} + +/* + * write POR value + */ + +static int icm4260x_set_default_register(struct i2c_client *client) +{ + int status = 0; + + status |= sensor_write_reg(client, ICM4260X_GYRO_CONFIG0, 0x06); + status |= sensor_write_reg(client, ICM4260X_ACCEL_CONFIG0, 0x06); + status |= sensor_write_reg(client, ICM4260X_APEX_CONFIG0, 0x08); + status |= sensor_write_reg(client, ICM4260X_APEX_CONFIG1, 0x02); + status |= sensor_write_reg(client, ICM4260X_WOM_CONFIG, 0); + status |= sensor_write_reg(client, ICM4260X_FIFO_CONFIG1, 0x01); + status |= sensor_write_reg(client, ICM4260X_FIFO_CONFIG2, 0); + status |= sensor_write_reg(client, ICM4260X_FIFO_CONFIG3, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_FIFO_CONFIG5_MREG_TOP1, 0x20); + status |= icm4260x_mreg_single_write(client, ICM4260X_ST_CONFIG_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_INT_SOURCE7_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_INT_SOURCE8_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_INT_SOURCE9_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_INT_SOURCE10_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG2_MREG_TOP1, 0xA2); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG3_MREG_TOP1, 0x85); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG4_MREG_TOP1, 0x51); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG5_MREG_TOP1, 0x80); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG9_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG10_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG11_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_ACCEL_WOM_X_THR_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_ACCEL_WOM_Y_THR_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_ACCEL_WOM_Z_THR_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER0_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER1_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER2_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER3_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER4_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER5_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER6_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER7_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER8_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG12_MREG_TOP1, 0); + + if (status) + return -EIO; + + return 0; +} + +static int sensor_init(struct i2c_client *client) +{ + int ret = 0; + u8 device_id = 0, value = 0; + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + device_id = sensor_read_reg(client, ICM4260X_WHO_AM_I); + if (device_id != ICM42607_DEVICE_ID) { + dev_err(&client->dev, "%s: check id err, read_id: %d\n", + __func__, device_id); + return -1; + } + + ret = icm4260x_otp_reload(client); + if (ret) { + dev_err(&client->dev, + "ICM4260X OTP reload error,ret: %d!\n", ret); + return ret; + } + + + ret = icm4260x_set_default_register(client); + if (ret) { + dev_err(&client->dev, + "set ICM4260X default_register error,ret: %d!\n", ret); + return ret; + } + + /* SPI or I2C only + * FIFO count : byte mode, big endian + * sensor data : big endian + */ + value |= BIT_FIFO_COUNT_ENDIAN; + value |= BIT_SENSOR_DATA_ENDIAN; + ret = sensor_write_reg(client, ICM4260X_INTF_CONFIG0, value); + if (ret) + return ret; + + /* configure clock */ + value = BIT_CLK_SEL_PLL | BIT_I3C_SDR_EN | BIT_I3C_DDR_EN; + ret = sensor_write_reg(client, ICM4260X_INTF_CONFIG1, value); + if (ret) + return ret; + + /* INT pin configuration */ + /* + * value = (INT_POLARITY << SHIFT_INT1_POLARITY) | + * (INT_DRIVE_CIRCUIT << SHIFT_INT1_DRIVE_CIRCUIT) | + * (INT_MODE << SHIFT_INT1_MODE); + * ret = sensor_write_reg(client, ICM4260X_INT_CONFIG_REG, value); + * if (ret) + * return ret; + */ + + /* disable sensors */ + ret = sensor_write_reg(client, ICM4260X_PWR_MGMT_0, 0); + if (ret) + return ret; + + /* set Full scale select for accelerometer UI interface output*/ + value = sensor_read_reg(client, ICM4260X_ACCEL_CONFIG0); + value &= ~BIT_ACCEL_FSR; + value |= ACCEL_FS_SEL << SHIFT_ACCEL_FS_SEL; + ret = sensor_write_reg(client, ICM4260X_ACCEL_CONFIG0, value); + if (ret) + return ret; + + /* turn on accelerometer*/ + ret = sensor->ops->active(client, 0, sensor->pdata->poll_delay_ms); + if (ret) { + dev_err(&client->dev, + "%s: fail to active sensor(%d)\n", __func__, ret); + return ret; + } + + return ret; +} + +static int gsensor_report_value(struct i2c_client *client, + struct sensor_axis *axis) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + if (sensor->status_cur == SENSOR_ON) { + /* Report acceleration sensor information */ + input_report_abs(sensor->input_dev, ABS_X, axis->x); + input_report_abs(sensor->input_dev, ABS_Y, axis->y); + input_report_abs(sensor->input_dev, ABS_Z, axis->z); + input_sync(sensor->input_dev); + } + + return 0; +} + +static int sensor_report_value(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + struct sensor_platform_data *pdata = sensor->pdata; + int ret = 0; + short x, y, z; + struct sensor_axis axis; + u8 buffer[6] = {0}; + + if (sensor->ops->read_len < 6) { + dev_err(&client->dev, "%s: length is error, len = %d\n", + __func__, sensor->ops->read_len); + return -EINVAL; + } + + /* Data bytes from hardware xH, xL, yH, yL, zH, zL */ + *buffer = sensor->ops->read_reg; + ret = sensor_rx_data(client, buffer, sensor->ops->read_len); + if (ret < 0) { + dev_err(&client->dev, + "%s: read data failed, ret = %d\n", __func__, ret); + return ret; + } + x = ((buffer[0] << 8) & 0xff00) + (buffer[1] & 0xFF); + y = ((buffer[2] << 8) & 0xff00) + (buffer[3] & 0xFF); + z = ((buffer[4] << 8) & 0xff00) + (buffer[5] & 0xFF); + + //printk("%s,x:%d, y:%d, z:%d\n", __func__, x, y, z); + axis.x = (pdata->orientation[0]) * x + (pdata->orientation[1]) * y + + (pdata->orientation[2]) * z; + axis.y = (pdata->orientation[3]) * x + (pdata->orientation[4]) * y + + (pdata->orientation[5]) * z; + axis.z = (pdata->orientation[6]) * x + (pdata->orientation[7]) * y + + (pdata->orientation[8]) * z; + + gsensor_report_value(client, &axis); + + mutex_lock(&(sensor->data_mutex)); + sensor->axis = axis; + mutex_unlock(&(sensor->data_mutex)); + + return ret; +} + +static struct sensor_operate gsensor_icm4260x_ops = { + .name = "icm4260x_acc", + .type = SENSOR_TYPE_ACCEL, + .id_i2c = ACCEL_ID_ICM4260X, + .read_reg = ICM4260X_ACCEL_DATA_X0, + .read_len = 6, + .id_reg = SENSOR_UNKNOW_DATA, + .id_data = SENSOR_UNKNOW_DATA, + .precision = ICM4260X_PRECISION, + .ctrl_reg = ICM4260X_PWR_MGMT_0, + .int_status_reg = ICM4260X_INT_STATUS, + .range = {-32768, 32768}, + .trig = IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + .active = sensor_active, + .init = sensor_init, + .report = sensor_report_value, +}; + +/****************operate according to sensor chip:end************/ +static int gsensor_icm4260x_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + client->addr = ICM42607_ADDR; + return sensor_register_device(client, NULL, devid, &gsensor_icm4260x_ops); +} + +static int gsensor_icm4260x_remove(struct i2c_client *client) +{ + return sensor_unregister_device(client, NULL, &gsensor_icm4260x_ops); +} + +static const struct i2c_device_id gsensor_icm4260x_id[] = { + {"icm42607_acc", ACCEL_ID_ICM4260X}, + {} +}; + +static struct i2c_driver gsensor_icm4260x_driver = { + .probe = gsensor_icm4260x_probe, + .remove = gsensor_icm4260x_remove, + .shutdown = sensor_shutdown, + .id_table = gsensor_icm4260x_id, + .driver = { + .name = "gsensor_icm4260x", +#ifdef CONFIG_PM + .pm = &sensor_pm_ops, +#endif + }, +}; + +module_i2c_driver(gsensor_icm4260x_driver); + +MODULE_AUTHOR("Wangqiang Guo "); +MODULE_DESCRIPTION("icm4260x_acc 3-Axis accelerometer driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/sensors/gyro/Kconfig b/drivers/input/sensors/gyro/Kconfig index 5b27389831ee..58b9b6dc0540 100644 --- a/drivers/input/sensors/gyro/Kconfig +++ b/drivers/input/sensors/gyro/Kconfig @@ -40,6 +40,12 @@ config GYRO_LSM330 config GYRO_ICM2060X tristate "gyroscope icm2060x_gyro" +config GYRO_ICM4260X + tristate "gyroscope icm4260x_gyro" + help + To have support for your specific gyro you will have to + select the proper drivers which depend on this option. + config GYRO_IAM20680 tristate "gyroscope iam20680_gyro" default n diff --git a/drivers/input/sensors/gyro/Makefile b/drivers/input/sensors/gyro/Makefile index 8c0a42734c65..d8c149baabc4 100644 --- a/drivers/input/sensors/gyro/Makefile +++ b/drivers/input/sensors/gyro/Makefile @@ -9,4 +9,5 @@ obj-$(CONFIG_GYRO_MPU6500) += mpu6500_gyro.o obj-$(CONFIG_GYRO_MPU6880) += mpu6880_gyro.o obj-$(CONFIG_GYRO_LSM330) += lsm330_gyro.o obj-$(CONFIG_GYRO_ICM2060X) += icm2060x_gyro.o +obj-$(CONFIG_GYRO_ICM4260X) += icm4260x_gyro.o obj-$(CONFIG_GYRO_IAM20680) += iam20680_gyro.o diff --git a/drivers/input/sensors/gyro/icm4260x_gyro.c b/drivers/input/sensors/gyro/icm4260x_gyro.c new file mode 100644 index 000000000000..965541a107ea --- /dev/null +++ b/drivers/input/sensors/gyro/icm4260x_gyro.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Rockchip Co.,Ltd. + * Author: Wangqiang Guo + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include + +static int sensor_active(struct i2c_client *client, int enable, int rate) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + int status = 0; + + sensor->ops->ctrl_data = sensor_read_reg(client, sensor->ops->ctrl_reg); + + if (!enable) { + status = (0xff & ~BIT_GYRO_MODE_MASK); + sensor->ops->ctrl_data &= status; + } else { + status = BIT_GYRO_MODE_LNM; + sensor->ops->ctrl_data |= status; + sensor->ops->ctrl_data &= ~BIT_IDLE; + } + + result = sensor_write_reg(client, sensor->ops->ctrl_reg, + sensor->ops->ctrl_data); + if (result) { + dev_err(&client->dev, + "%s: fail to set pwr_mgmt0(%d)\n", __func__, result); + return result; + } + /* Gyroscope needs to be kept ON for a minimum of 45ms */ + usleep_range(45*1000, 45*1010); + + return result; +} + +static int sensor_init(struct i2c_client *client) +{ + int ret = 0; + u8 value; + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + /* + * init on icm42607_acc.c + */ + + /* set Full scale select for accelerometer UI interface output*/ + value = sensor_read_reg(client, ICM4260X_GYRO_CONFIG0); + value &= ~BIT_GYRO_FSR; + value |= GYRO_FS_SEL << SHIFT_GYRO_FS_SEL; + ret = sensor_write_reg(client, ICM4260X_GYRO_CONFIG0, value); + if (ret) + return ret; + + /* turn on accelerometer*/ + ret = sensor->ops->active(client, 0, sensor->pdata->poll_delay_ms); + if (ret) { + dev_err(&client->dev, + "%s: fail to active sensor(%d)\n", __func__, ret); + return ret; + } + + return ret; +} + +static int gyro_report_value(struct i2c_client *client, + struct sensor_axis *axis) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + if (sensor->status_cur == SENSOR_ON) { + /* Report acceleration sensor information */ + input_report_abs(sensor->input_dev, ABS_RX, axis->x); + input_report_abs(sensor->input_dev, ABS_RY, axis->y); + input_report_abs(sensor->input_dev, ABS_RZ, axis->z); + input_sync(sensor->input_dev); + } + + return 0; +} + +static int sensor_report_value(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + struct sensor_platform_data *pdata = sensor->pdata; + int ret = 0; + short x, y, z; + struct sensor_axis axis; + u8 buffer[6] = {0}; + + if (sensor->ops->read_len < 6) { + dev_err(&client->dev, "%s: length is error, len = %d\n", + __func__, sensor->ops->read_len); + return -EINVAL; + } + + *buffer = sensor->ops->read_reg; + ret = sensor_rx_data(client, buffer, sensor->ops->read_len); + if (ret < 0) { + dev_err(&client->dev, + "%s: read data failed, ret = %d\n", __func__, ret); + return ret; + } + + x = ((buffer[0] << 8) & 0xff00) + (buffer[1] & 0xFF); + y = ((buffer[2] << 8) & 0xff00) + (buffer[3] & 0xFF); + z = ((buffer[4] << 8) & 0xff00) + (buffer[5] & 0xFF); + + axis.x = (pdata->orientation[0]) * x + (pdata->orientation[1]) * y + + (pdata->orientation[2]) * z; + axis.y = (pdata->orientation[3]) * x + (pdata->orientation[4]) * y + + (pdata->orientation[5]) * z; + axis.z = (pdata->orientation[6]) * x + (pdata->orientation[7]) * y + + (pdata->orientation[8]) * z; + + gyro_report_value(client, &axis); + + mutex_lock(&(sensor->data_mutex)); + sensor->axis = axis; + mutex_unlock(&(sensor->data_mutex)); + + return ret; +} + +static struct sensor_operate gyro_icm4260x_ops = { + .name = "icm4260x_gyro", + .type = SENSOR_TYPE_GYROSCOPE, + .id_i2c = GYRO_ID_ICM4260X, + .read_reg = ICM4260X_GYRO_DATA_X0, + .read_len = 6, + .id_reg = SENSOR_UNKNOW_DATA, + .id_data = SENSOR_UNKNOW_DATA, + .precision = ICM4260X_PRECISION, + .ctrl_reg = ICM4260X_PWR_MGMT_0, + .int_status_reg = ICM4260X_INT_STATUS, + .range = {-32768, 32768}, + .trig = IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + .active = sensor_active, + .init = sensor_init, + .report = sensor_report_value, +}; + +/****************operate according to sensor chip:end************/ +static int gyro_icm4260x_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + client->addr = ICM42607_ADDR; + return sensor_register_device(client, NULL, devid, &gyro_icm4260x_ops); +} + +static int gyro_icm4260x_remove(struct i2c_client *client) +{ + return sensor_unregister_device(client, NULL, &gyro_icm4260x_ops); +} + +static const struct i2c_device_id gyro_icm4260x_id[] = { + {"icm42607_gyro", GYRO_ID_ICM4260X}, + {} +}; + +static struct i2c_driver gyro_icm4260x_driver = { + .probe = gyro_icm4260x_probe, + .remove = gyro_icm4260x_remove, + .shutdown = sensor_shutdown, + .id_table = gyro_icm4260x_id, + .driver = { + .name = "gyro_icm4260x", +#ifdef CONFIG_PM + .pm = &sensor_pm_ops, +#endif + }, +}; + +static int __init gyro_icm4260x_init(void) +{ + return i2c_add_driver(&gyro_icm4260x_driver); +} + +static void __exit gyro_icm4260x_exit(void) +{ + i2c_del_driver(&gyro_icm4260x_driver); +} +/* must register after icm4260x_acc */ +device_initcall_sync(gyro_icm4260x_init); +module_exit(gyro_icm4260x_exit); + +MODULE_AUTHOR("Wangqiang Guo "); +MODULE_DESCRIPTION("icm4260x_gyro 3-Axis accelerometer driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/icm4260x.h b/include/linux/icm4260x.h new file mode 100644 index 000000000000..8917d8bc307e --- /dev/null +++ b/include/linux/icm4260x.h @@ -0,0 +1,432 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Definitions for icm4260x chip. + */ +#ifndef __ICM4260X_ACC_H +#define __ICM4260X_ACC_H + +/* Registers and associated bit definitions */ +/* Bank 0 */ +#define ICM4260X_MISC_1 0x00 +#define ICM4260X_CHIP_CONFIG_REG 0x01 +#define ICM4260X_SIGNAL_PATH_RESET 0x02 +#define ICM4260X_DRIVE_CONFIG_REG1 0x03 +#define ICM4260X_DRIVE_CONFIG_REG2 0x04 +#define ICM4260X_DRIVE_CONFIG_REG3 0x05 +#define ICM4260X_INT_CONFIG_REG 0x06 +#define ICM4260X_ODRGRID0 0x07 +#define ICM4260X_ODRGRID1 0x08 +#define ICM4260X_TEMP_DATA0 0x09 +#define ICM4260X_TEMP_DATA1 0x0a +#define ICM4260X_ACCEL_DATA_X0 0x0b +#define ICM4260X_ACCEL_DATA_X1 0x0c +#define ICM4260X_ACCEL_DATA_Y0 0x0d +#define ICM4260X_ACCEL_DATA_Y1 0x0e +#define ICM4260X_ACCEL_DATA_Z0 0x0f +#define ICM4260X_ACCEL_DATA_Z1 0x10 +#define ICM4260X_GYRO_DATA_X0 0x11 +#define ICM4260X_GYRO_DATA_X1 0x12 +#define ICM4260X_GYRO_DATA_Y0 0x13 +#define ICM4260X_GYRO_DATA_Y1 0x14 +#define ICM4260X_GYRO_DATA_Z0 0x15 +#define ICM4260X_GYRO_DATA_Z1 0x16 +#define ICM4260X_TMST_FSYNC1 0x17 +#define ICM4260X_TMST_FSYNC2 0x18 +#define ICM4260X_ODR_LP_STATUS 0x19 +#define ICM4260X_PWR_MGMT_0 0x1f +#define ICM4260X_GYRO_CONFIG0 0x20 +#define ICM4260X_ACCEL_CONFIG0 0x21 +#define ICM4260X_TEMP_CONFIG0 0x22 +#define ICM4260X_GYRO_CONFIG1 0x23 +#define ICM4260X_ACCEL_CONFIG1 0x24 +#define ICM4260X_APEX_CONFIG0 0x25 +#define ICM4260X_APEX_CONFIG1 0x26 +#define ICM4260X_WOM_CONFIG 0x27 +#define ICM4260X_FIFO_CONFIG1 0x28 +#define ICM4260X_FIFO_CONFIG2 0x29 +#define ICM4260X_FIFO_CONFIG3 0x2a +#define ICM4260X_INT_SOURCE0 0x2b +#define ICM4260X_INT_SOURCE1 0x2c +#define ICM4260X_INT_SOURCE3 0x2d +#define ICM4260X_INT_SOURCE4 0x2e +#define ICM4260X_FIFO_LOST_PKT0 0x2f +#define ICM4260X_FIFO_LOST_PKT1 0x30 +#define ICM4260X_APEX_DATA0 0x31 +#define ICM4260X_APEX_DATA1 0x32 +#define ICM4260X_APEX_DATA2 0x33 +#define ICM4260X_APEX_DATA3 0x34 +#define ICM4260X_INTF_CONFIG0 0x35 +#define ICM4260X_INTF_CONFIG1 0x36 +#define ICM4260X_INT_STATUS_DRDY 0x39 +#define ICM4260X_INT_STATUS 0x3a +#define ICM4260X_INT_STATUS2 0x3b +#define ICM4260X_INT_STATUS3 0x3c +#define ICM4260X_FIFO_BYTE_COUNT1 0x3d +#define ICM4260X_FIFO_BYTE_COUNT2 0x3e +#define ICM4260X_FIFO_DATA_REG 0x3f +#define ICM4260X_S4S_GYRO_TPH1 0x40 +#define ICM4260X_S4S_GYRO_TPH2 0x41 +#define ICM4260X_S4S_ACCEL_TPH1 0x42 +#define ICM4260X_S4S_ACCEL_TPH2 0x43 +#define ICM4260X_S4S_RR 0x44 +#define ICM4260X_GYR_BIAS_CFG1 0x46 +#define ICM4260X_WHO_AM_I 0x75 +#define ICM4260X_S4S_ST 0x76 +#define ICM4260X_S4S_ST_CLONE 0x77 +#define ICM4260X_S4S_DT 0x78 +#define ICM4260X_BLK_SEL_W 0x79 +#define ICM4260X_MADDR_W 0x7a +#define ICM4260X_M_W 0x7b +#define ICM4260X_BLK_SEL_R 0x7c +#define ICM4260X_MADDR_R 0x7d +#define ICM4260X_M_R 0x7e + +/* MREG_TOP1 */ +#define ICM4260X_TMST_CONFIG1_MREG_TOP1 0x00 +#define ICM4260X_FIFO_CONFIG5_MREG_TOP1 0x01 +#define ICM4260X_FIFO_CONFIG6_MREG_TOP1 0x02 +#define ICM4260X_FSYNC_CONFIG_MREG_TOP1 0x03 +#define ICM4260X_INT_CONFIG0_MREG_TOP1 0x04 +#define ICM4260X_INT_CONFIG1_MREG_TOP1 0x05 +#define ICM4260X_AFSR_CONFIG0_MREG_TOP1 0x07 +#define ICM4260X_AFSR_CONFIG1_MREG_TOP1 0x08 +#define ICM4260X_TBC_RCOSC_MREG_TOP1 0x0d +#define ICM4260X_TBC_PLL_MREG_TOP1 0x0e +#define ICM4260X_ST_CONFIG_MREG_TOP1 0x13 +#define ICM4260X_SELFTEST_MREG_TOP1 0x14 +#define ICM4260X_PADS_CONFIG3_MREG_TOP1 0x17 +#define ICM4260X_TEMP_CONFIG1_MREG_TOP1 0x1c +#define ICM4260X_TEMP_CONFIG3_MREG_TOP1 0x1e +#define ICM4260X_S4S_CONFIG1_MREG_TOP1 0x1f +#define ICM4260X_S4S_CONFIG2_MREG_TOP1 0x20 +#define ICM4260X_S4S_FREQ_RATIO1_MREG_TOP1 0x21 +#define ICM4260X_S4S_FREQ_RATIO2_MREG_TOP1 0x22 +#define ICM4260X_INTF_CONFIG6_MREG_TOP1 0x23 +#define ICM4260X_INTF_CONFIG10_MREG_TOP1 0x25 +#define ICM4260X_INTF_CONFIG7_MREG_TOP1 0x28 +#define ICM4260X_OTP_CONFIG_MREG_TOP1 0x2b +#define ICM4260X_INT_SOURCE6_MREG_TOP1 0x2f +#define ICM4260X_INT_SOURCE7_MREG_TOP1 0x30 +#define ICM4260X_INT_SOURCE8_MREG_TOP1 0x31 +#define ICM4260X_INT_SOURCE9_MREG_TOP1 0x32 +#define ICM4260X_INT_SOURCE10_MREG_TOP1 0x33 +#define ICM4260X_GYRO_PWR_CFG0_MREG_TOP1 0x38 +#define ICM4260X_ACCEL_CP_CFG0_MREG_TOP1 0x39 +#define ICM4260X_APEX_CONFIG2_MREG_TOP1 0x44 +#define ICM4260X_APEX_CONFIG3_MREG_TOP1 0x45 +#define ICM4260X_APEX_CONFIG4_MREG_TOP1 0x46 +#define ICM4260X_APEX_CONFIG5_MREG_TOP1 0x47 +#define ICM4260X_APEX_CONFIG9_MREG_TOP1 0x48 +#define ICM4260X_APEX_CONFIG10_MREG_TOP1 0x49 +#define ICM4260X_APEX_CONFIG11_MREG_TOP1 0x4a +#define ICM4260X_APEX_CONFIG12_MREG_TOP1 0x67 +#define ICM4260X_ACCEL_WOM_X_THR_MREG_TOP1 0x4b +#define ICM4260X_ACCEL_WOM_Y_THR_MREG_TOP1 0x4c +#define ICM4260X_ACCEL_WOM_Z_THR_MREG_TOP1 0x4d +#define ICM4260X_GOS_USER0_MREG_TOP1 0x4e +#define ICM4260X_GOS_USER1_MREG_TOP1 0x4f +#define ICM4260X_GOS_USER2_MREG_TOP1 0x50 +#define ICM4260X_GOS_USER3_MREG_TOP1 0x51 +#define ICM4260X_GOS_USER4_MREG_TOP1 0x52 +#define ICM4260X_GOS_USER5_MREG_TOP1 0x53 +#define ICM4260X_GOS_USER6_MREG_TOP1 0x54 +#define ICM4260X_GOS_USER7_MREG_TOP1 0x55 +#define ICM4260X_GOS_USER8_MREG_TOP1 0x56 +#define ICM4260X_ST_STATUS1_MREG_TOP1 0x63 +#define ICM4260X_ST_STATUS2_MREG_TOP1 0x64 + +/* MMEM_TOP */ +#define ICM4260X_XA_ST_DATA_MMEM_TOP 0x5000 +#define ICM4260X_YA_ST_DATA_MMEM_TOP 0x5001 +#define ICM4260X_ZA_ST_DATA_MMEM_TOP 0x5002 +#define ICM4260X_XG_ST_DATA_MMEM_TOP 0x5003 +#define ICM4260X_YG_ST_DATA_MMEM_TOP 0x5004 +#define ICM4260X_ZG_ST_DATA_MMEM_TOP 0x5005 + +/* MREG_OTP */ +#define ICM4260X_OTP_CTRL7_MREG_OTP 0x2806 + +/* Bank0 REG_GYRO_CONFIG0/REG_ACCEL_CONFIG0 */ +#define SHIFT_GYRO_FS_SEL 5 +#define SHIFT_ACCEL_FS_SEL 5 +#define SHIFT_ODR_CONF 0 +#define BIT_GYRO_FSR 0x60 +#define BIT_GYRO_ODR 0x0F +#define BIT_ACCEL_FSR 0x60 +#define ACCEL_FS_SEL 3 //(-2G, +2G) +#define GYRO_FS_SEL 3 //00~11:2000dps\1000dps\500dps\250dps +#define BIT_ACCEL_ODR 0x0F +#define BIT_SENSOR_ODR_800HZ 0x06 +#define BIT_SENSOR_ODR_400HZ 0x07 +#define BIT_SENSOR_ODR_200HZ 0x08 +#define BIT_SENSOR_ODR_100HZ 0x09 +#define BIT_SENSOR_ODR_50HZ 0x0A +#define BIT_SENSOR_ODR_25HZ 0x0B +#define BIT_SENSOR_ODR_12HZ 0x0C +#define BIT_SENSOR_ODR_6HZ 0x0D +#define BIT_SENSOR_ODR_3HZ 0x0E + +/* Bank0 REG_GYRO_CONFIG1 */ +#define BIT_GYR_UI_FLT_BW_BYPASS 0x00 +#define BIT_GYR_UI_FLT_BW_180HZ 0x01 +#define BIT_GYR_UI_FLT_BW_121HZ 0x02 +#define BIT_GYR_UI_FLT_BW_73HZ 0x03 +#define BIT_GYR_UI_FLT_BW_53HZ 0x04 +#define BIT_GYR_UI_FLT_BW_34HZ 0x05 +#define BIT_GYR_UI_FLT_BW_25HZ 0x06 +#define BIT_GYR_UI_FLT_BW_16HZ 0x07 +#define BIT_GYR_UI_AVG_IND_2X 0x00 +#define BIT_GYR_UI_AVG_IND_4X 0x10 +#define BIT_GYR_UI_AVG_IND_8X 0x20 +#define BIT_GYR_UI_AVG_IND_16X 0x30 +#define BIT_GYR_UI_AVG_IND_32X 0x40 +#define BIT_GYR_UI_AVG_IND_64X 0x50 + +/* Bank0 REG_ACCEL_CONFIG1 */ +#define BIT_ACC_FILT_BW_IND_BYPASS 0x00 +#define BIT_ACC_FILT_BW_IND_180HZ 0x01 +#define BIT_ACC_FILT_BW_IND_121HZ 0x02 +#define BIT_ACC_FILT_BW_IND_73HZ 0x03 +#define BIT_ACC_FILT_BW_IND_53HZ 0x04 +#define BIT_ACC_FILT_BW_IND_34HZ 0x05 +#define BIT_ACC_FILT_BW_IND_25HZ 0x06 +#define BIT_ACC_FILT_BW_IND_16HZ 0x07 +#define BIT_ACC_UI_AVG_IND_2X 0x00 +#define BIT_ACC_UI_AVG_IND_4X 0x10 +#define BIT_ACC_UI_AVG_IND_8X 0x20 +#define BIT_ACC_UI_AVG_IND_16X 0x30 +#define BIT_ACC_UI_AVG_IND_32X 0x40 +#define BIT_ACC_UI_AVG_IND_64X 0x50 + +/* Bank0 REG_INT_CONFIG_REG */ +#define SHIFT_INT1_MODE 0x02 +#define SHIFT_INT1_DRIVE_CIRCUIT 0x01 +#define SHIFT_INT1_POLARITY 0x00 + +/* Bank0 REG_PWR_MGMT_0 */ +#define BIT_ACCEL_MODE_OFF 0x00 +#define BIT_ACCEL_MODE_LPM 0x02 +#define BIT_ACCEL_MODE_LNM 0x03 +#define BIT_ACCEL_MODE_MASK 0x03 +#define BIT_GYRO_MODE_OFF 0x00 +#define BIT_GYRO_MODE_STBY 0x04 +#define BIT_GYRO_MODE_LPM 0x08 +#define BIT_GYRO_MODE_LNM 0x0c +#define BIT_GYRO_MODE_MASK 0x0c +#define BIT_IDLE 0x10 +#define BIT_ACCEL_LP_CLK_SEL 0x80 + +/* Bank0 REG_SIGNAL_PATH_RESET */ +#define BIT_FIFO_FLUSH 0x04 +#define BIT_SOFT_RESET_CHIP_CONFIG 0x10 + +/* Bank0 REG_INTF_CONFIG0 */ +#define BIT_SIFS_CFG_I2C_ONLY 0x02 +#define BIT_SIFS_CFG_SPI_ONLY 0x03 +#define BIT_SENSOR_DATA_ENDIAN 0x10 +#define BIT_FIFO_COUNT_ENDIAN 0x20 +#define BIT_FIFO_COUNT_FORMAT 0x40 +#define BIT_FIFO_SREG_INVALID_IND_DIS 0x80 + +/* Bank0 REG_INTF_CONFIG1 */ +#define BIT_CLK_SEL_RC 0x00 +#define BIT_CLK_SEL_PLL 0x01 +#define BIT_CLK_SEL_DIS 0x03 +#define BIT_I3C_DDR_EN 0x04 +#define BIT_I3C_SDR_EN 0x08 +#define BIT_GYRO_AFSR_MODE_LFS 0x00 +#define BIT_GYRO_AFSR_MODE_HFS 0x20 +#define BIT_GYRO_AFSR_MODE_DYN 0x40 + +/* Bank0 REG_FIFO_CONFIG1 */ +#define BIT_FIFO_MODE_NO_BYPASS 0x00 +#define BIT_FIFO_MODE_BYPASS 0x01 +#define BIT_FIFO_MODE_STREAM 0x00 +#define BIT_FIFO_MODE_STOPFULL 0x02 + +/* Bank 0 REG_INT_SOURCE0 */ +#define BIT_INT_AGC_RDY_INT1_EN 0x01 +#define BIT_INT_FIFO_FULL_INT1_EN 0x02 +#define BIT_INT_FIFO_THS_INT1_EN 0x04 +#define BIT_INT_DRDY_INT_EN 0x08 +#define BIT_INT_RESET_DONE_INT1_EN 0x10 +#define BIT_INT_PLL_RDY_INT1_EN 0x20 +#define BIT_INT_FSYNC_INT1_EN 0x40 +#define BIT_INT_ST_DONE_INT1_EN 0x80 + +/* Bank 0 REG_INT_SOURCE1 */ +#define BIT_INT_WOM_X_INT1_EN 0x01 +#define BIT_INT_WOM_Y_INT1_EN 0x02 +#define BIT_INT_WOM_Z_INT1_EN 0x04 +#define BIT_INT_WOM_XYZ_INT1_EN (BIT_INT_WOM_X_INT1_EN | \ + BIT_INT_WOM_Y_INT1_EN | BIT_INT_WOM_Z_INT1_EN) +#define BIT_INT_SMD_INT1_EN 0x08 +#define BIT_INT_I3C_PROTCL_ERR_INT1_EN 0x40 + +/* Bank0 REG_INT_STATUS_DRDY */ +#define BIT_INT_STATUS_DRDY 0x01 + +/* Bank0 REG_INT_STATUS */ +#define BIT_INT_STATUS_AGC_RDY 0x01 +#define BIT_INT_STATUS_FIFO_FULL 0x02 +#define BIT_INT_STATUS_FIFO_THS 0x04 +#define BIT_INT_STATUS_RESET_DONE 0x10 +#define BIT_INT_STATUS_PLL_RDY 0x20 +#define BIT_INT_STATUS_FSYNC 0x40 +#define BIT_INT_STATUS_ST_DONE 0x80 + +/* Bank0 REG_INT_STATUS2 */ +#define BIT_INT_STATUS_WOM_Z 0x01 +#define BIT_INT_STATUS_WOM_Y 0x02 +#define BIT_INT_STATUS_WOM_X 0x04 +#define BIT_INT_STATUS_WOM_XYZ (BIT_INT_STATUS_WOM_X | \ + BIT_INT_STATUS_WOM_Y | BIT_INT_STATUS_WOM_Z) +#define BIT_INT_STATUS_SMD 0x08 + +/* Bank 0 REG_INT_STATUS3 */ +#define BIT_INT_STATUS_LOWG_DET 0x02 +#define BIT_INT_STATUS_FF_DET 0x04 +#define BIT_INT_STATUS_TILT_DET 0x08 +#define BIT_INT_STATUS_STEP_CNT_OVFL 0x10 +#define BIT_INT_STATUS_STEP_DET 0x20 + +/* Bank0 REG_WOM_CONFIG */ +#define BIT_WOM_EN_OFF 0x00 +#define BIT_WOM_EN_ON 0x01 +#define BIT_WOM_MODE_INITIAL 0x00 +#define BIT_WOM_MODE_PREV 0x02 +#define BIT_WOM_INT_MODE_OR 0x00 +#define BIT_WOM_INT_MODE_AND 0x04 +#define BIT_WOM_INT_DUR_LEGACY 0x00 +#define BIT_WOM_INT_DUR_2ND 0x08 +#define BIT_WOM_INT_DUR_3RD 0x10 +#define BIT_WOM_INT_DUR_4TH 0x18 + +/* Bank0 REG_APEX_CONFIG0 */ +#define BIT_DMP_SRAM_RESET_APEX 0x01 +#define BIT_DMP_INIT_EN 0x04 +#define BIT_DMP_POWER_SAVE_EN 0x08 + +/* Bank0 REG_APEX_CONFIG1 */ +#define BIT_DMP_ODR_25HZ 0x00 +#define BIT_DMP_ODR_50HZ 0x02 +#define BIT_DMP_ODR_100HZ 0x03 +#define BIT_DMP_PEDO_EN 0x08 +#define BIT_DMP_TILT_EN 0x10 +#define BIT_DMP_FF_EN 0x20 +#define BIT_DMP_SMD_EN 0x40 + +/* REG_OTP_CONFIG_MREG_TOP1 */ +#define BIT_OTP_COPY_NORMAL 0x04 +#define BIT_OTP_COPY_ST_DATA 0x0C +#define OTP_COPY_MODE_MASK 0x0C + +/* REG_INT_SOURCE6_MREG_TOP1 */ +#define BIT_INT_TLT_DET_INT1_EN 0x08 +#define BIT_INT_STEP_CNT_OVFL_INT1_EN 0x10 +#define BIT_INT_STEP_DET_INT1_EN 0x20 +#define BIT_INT_LOWG_INT1_EN 0x40 +#define BIT_INT_FF_INT1_EN 0x80 + +/* REG_TMST_CONFIG1_MREG_TOP1 */ +#define BIT_TMST_EN 0x01 +#define BIT_TMST_FSYNC_EN 0x02 +#define BIT_TMST_DELTA_EN 0x04 +#define BIT_TMST_RESOL 0x08 +#define BIT_TMST_ON_SREG_EN 0x10 +#define BIT_ODR_EN_WITHOUT_SENSOR 0x40 + +/* REG_FIFO_CONFIG5_MREG_TOP1 */ +#define BIT_FIFO_ACCEL_EN 0x01 +#define BIT_FIFO_GYRO_EN 0x02 +#define BIT_FIFO_TMST_FSYNC_EN 0x04 +#define BIT_FIFO_HIRES_EN 0x08 +#define BIT_RESUME_PARTIAL_RD 0x10 +#define BIT_WM_GT_TH 0x20 + +/* REG_SELFTEST_MREG_TOP1 */ +#define BIT_EN_AX_ST 0x01 +#define BIT_EN_AY_ST 0x02 +#define BIT_EN_AZ_ST 0x04 +#define BIT_EN_GX_ST 0x08 +#define BIT_EN_GY_ST 0x10 +#define BIT_EN_GZ_ST 0x20 +#define BIT_ACCEL_ST_EN 0x40 +#define BIT_GYRO_ST_EN 0x80 + +/* REG_ST_CONFIG_MREG_TOP1 */ +#define BIT_PD_ACCEL_CP45_ST_REG 0x80 +#define SHIFT_GYRO_ST_LIM 0 +#define SHIFT_ACCEL_ST_LIM 3 +#define SHIFT_ST_NUM_SAMPLE 6 + +/* REG_ST_STATUS1_MREG_TOP1 */ +#define BIT_DMP_AX_ST_PASS 0x02 +#define BIT_DMP_AY_ST_PASS 0x04 +#define BIT_DMP_AZ_ST_PASS 0x08 +#define BIT_DMP_ACCEL_ST_DONE 0x10 +#define BIT_DMP_ACCEL_ST_PASS 0x20 + +/* REG_ST_STATUS2_MREG_TOP1 */ +#define BIT_DMP_GX_ST_PASS 0x02 +#define BIT_DMP_GY_ST_PASS 0x04 +#define BIT_DMP_GZ_ST_PASS 0x08 +#define BIT_DMP_GYRO_ST_DONE 0x10 +#define BIT_DMP_GYRO_ST_PASS 0x20 +#define BIT_DMP_ST_INCOMPLETE 0x40 + +/* REG_OTP_CTRL7_MREG_OTP */ +#define BIT_OTP_RELOAD 0x08 +#define BIT_OTP_PWR_DOWN 0x02 + + +/* fifo data packet header */ +#define BIT_FIFO_HEAD_MSG 0x80 +#define BIT_FIFO_HEAD_ACCEL 0x40 +#define BIT_FIFO_HEAD_GYRO 0x20 +#define BIT_FIFO_HEAD_20 0x10 +#define BIT_FIFO_HEAD_TMSP_ODR 0x08 +#define BIT_FIFO_HEAD_TMSP_NO_ODR 0x04 +#define BIT_FIFO_HEAD_TMSP_FSYNC 0x0C +#define BIT_FIFO_HEAD_ODR_ACCEL 0x02 +#define BIT_FIFO_HEAD_ODR_GYRO 0x01 + +/* data definitions */ +#define FIFO_PACKET_BYTE_SINGLE 8 +#define FIFO_PACKET_BYTE_6X 16 +#define FIFO_PACKET_BYTE_HIRES 20 +#define FIFO_COUNT_BYTE 2 + +/* sensor startup time */ +#define INV_ICM43600_GYRO_START_TIME 100 +#define INV_ICM43600_ACCEL_START_TIME 100 + +/* sensor stop time */ +#define INV_ICM43600_GYRO_STOP_TIME 20 + +/* M-reg access wait tile */ +#define INV_ICM42607_MCLK_WAIT_US 20 +#define INV_ICM42607_BLK_SEL_WAIT_US 10 +#define INV_ICM42607_MADDR_WAIT_US 10 +#define INV_ICM42607_M_RW_WAIT_US 10 + +/* temperature sensor */ +#define TEMP_SCALE 100 /* scale by 100 */ +#define TEMP_LSB_PER_DEG 2 /* 2LSB=1degC */ +#define TEMP_OFFSET 25 /* 25 degC */ + +/* + * INT configurations + * Polarity: 0 -> Active Low, 1 -> Active High + * Drive circuit: 0 -> Open Drain, 1 -> Push-Pull + * Mode: 0 -> Pulse, 1 -> Latch + */ +#define INT_POLARITY 1 +#define INT_DRIVE_CIRCUIT 1 +#define INT_MODE 0 + +#define ICM42607_DEVICE_ID 0x61 +#define ICM4260X_PRECISION 16 +#define ICM42607_ADDR 0x68 + +#endif diff --git a/include/linux/sensor-dev.h b/include/linux/sensor-dev.h index 81d3aa1ea38b..a29d512ec0d1 100644 --- a/include/linux/sensor-dev.h +++ b/include/linux/sensor-dev.h @@ -70,6 +70,7 @@ enum sensor_id { ACCEL_ID_DA215S, ACCEL_ID_DA228E, ACCEL_ID_IAM20680, + ACCEL_ID_ICM4260X, COMPASS_ID_ALL, COMPASS_ID_AK8975, COMPASS_ID_AK8963, @@ -97,6 +98,7 @@ enum sensor_id { GYRO_ID_LSM330, GYRO_ID_ICM2060X, GYRO_ID_IAM20680, + GYRO_ID_ICM4260X, LIGHT_ID_ALL, LIGHT_ID_CM3217, LIGHT_ID_CM3218, From 2604a38ab45fef071b54515f4edd0c5e7c245a70 Mon Sep 17 00:00:00 2001 From: Wang Xiaobin Date: Mon, 9 Oct 2023 10:05:54 +0800 Subject: [PATCH 37/55] media: i2c: add cam-tb-setup Thunder boot related camera parameters. Signed-off-by: Wang Xiaobin Change-Id: I221e22041677325842a3d192c232fbff9d1bc992 --- drivers/media/i2c/Makefile | 1 + drivers/media/i2c/cam-tb-setup.c | 97 ++++++++++++++++++++++++++++++++ drivers/media/i2c/cam-tb-setup.h | 33 +++++++++++ 3 files changed, 131 insertions(+) create mode 100644 drivers/media/i2c/cam-tb-setup.c create mode 100644 drivers/media/i2c/cam-tb-setup.h diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 11c344f93afe..5067b36b3390 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -236,3 +236,4 @@ obj-$(CONFIG_SDR_MAX2175) += max2175.o obj-$(CONFIG_VIDEO_OTP_EEPROM) += otp_eeprom.o obj-$(CONFIG_VIDEO_PREISP_DUMMY_SENSOR) += preisp-dummy.o +obj-$(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) += cam-tb-setup.o diff --git a/drivers/media/i2c/cam-tb-setup.c b/drivers/media/i2c/cam-tb-setup.c new file mode 100644 index 000000000000..e300dc62731c --- /dev/null +++ b/drivers/media/i2c/cam-tb-setup.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2023 Rockchip Electronics Co., Ltd + +#include +#include +#include +#include "cam-tb-setup.h" + +static u32 rk_cam_w; +static u32 rk_cam_h; +static u32 rk_cam_hdr; +static u32 rk_cam_fps; + +static int __init rk_cam_w_setup(char *str) +{ + int ret = 0; + unsigned long val = 0; + + ret = kstrtoul(str, 0, &val); + if (!ret) + rk_cam_w = (u32)val; + else + pr_err("get rk_cam_w fail\n"); + + return 0; +} + +u32 get_rk_cam_w(void) +{ + return rk_cam_w; +} +EXPORT_SYMBOL(get_rk_cam_w); + +static int __init rk_cam_h_setup(char *str) +{ + int ret = 0; + unsigned long val = 0; + + ret = kstrtoul(str, 0, &val); + if (!ret) + rk_cam_h = (u32)val; + else + pr_err("get rk_cam_h fail\n"); + + return 0; +} + +u32 get_rk_cam_h(void) +{ + return rk_cam_h; +} +EXPORT_SYMBOL(get_rk_cam_h); + +static int __init rk_cam_hdr_setup(char *str) +{ + int ret = 0; + unsigned long val = 0; + + ret = kstrtoul(str, 0, &val); + if (!ret) + rk_cam_hdr = (u32)val; + else + pr_err("get rk_cam_hdr fail\n"); + + return 0; +} + +u32 get_rk_cam_hdr(void) +{ + return rk_cam_hdr; +} +EXPORT_SYMBOL(get_rk_cam_hdr); + +static int __init __maybe_unused rk_cam_fps_setup(char *str) +{ + int ret = 0; + unsigned long val = 0; + + ret = kstrtoul(str, 0, &val); + if (!ret) + rk_cam_fps = (u32)val; + else + pr_err("get rk_cam_fps fail\n"); + + return 0; +} + +u32 get_rk_cam_fps(void) +{ + return rk_cam_fps; +} +EXPORT_SYMBOL(get_rk_cam_fps); + +__setup("rk_cam_w=", rk_cam_w_setup); +__setup("rk_cam_h=", rk_cam_h_setup); +__setup("rk_cam_hdr=", rk_cam_hdr_setup); +__setup("rk_cam_fps=", rk_cam_fps_setup); diff --git a/drivers/media/i2c/cam-tb-setup.h b/drivers/media/i2c/cam-tb-setup.h new file mode 100644 index 000000000000..61afc32c20b7 --- /dev/null +++ b/drivers/media/i2c/cam-tb-setup.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023 Rockchip Electronics Co., Ltd. */ + +#ifndef CAM_TB_SETUP_H +#define CAM_TB_SETUP_H + +#include + +#ifdef CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP +u32 get_rk_cam_w(void); +u32 get_rk_cam_h(void); +u32 get_rk_cam_hdr(void); +u32 get_rk_cam_fps(void); +#else +static inline u32 get_rk_cam_w(void) +{ + return 0; +} +static inline u32 get_rk_cam_h(void) +{ + return 0; +} +static inline u32 get_rk_cam_hdr(void) +{ + return 0; +} +static inline u32 get_rk_cam_fps(void) +{ + return 0; +} +#endif + +#endif From 180ac760c92c04a177108c7ba77fd3092b65f055 Mon Sep 17 00:00:00 2001 From: Wang Xiaobin Date: Thu, 7 Sep 2023 12:50:15 +0800 Subject: [PATCH 38/55] media: i2c: gc2093: get camera param from cam-tb-setup Signed-off-by: Wang Xiaobin Change-Id: I733f612f413a788beee7c1acbe02462dcefb2dc1 --- drivers/media/i2c/gc2093.c | 67 +++----------------------------------- 1 file changed, 5 insertions(+), 62 deletions(-) diff --git a/drivers/media/i2c/gc2093.c b/drivers/media/i2c/gc2093.c index aa87a731dfe4..f612814c355e 100644 --- a/drivers/media/i2c/gc2093.c +++ b/drivers/media/i2c/gc2093.c @@ -33,6 +33,7 @@ #include #include #include "../platform/rockchip/isp/rkisp_tb_helper.h" +#include "cam-tb-setup.h" #define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) #define GC2093_NAME "gc2093" @@ -1612,68 +1613,6 @@ static const struct dev_pm_ops gc2093_pm_ops = { #ifdef CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP -static u32 rk_cam_hdr; -static u32 rk_cam_w; -static u32 rk_cam_h; -static u32 rk_cam_fps; - -static int __init __maybe_unused rk_cam_hdr_setup(char *str) -{ - int ret = 0; - unsigned long val = 0; - - ret = kstrtoul(str, 0, &val); - if (!ret) - rk_cam_hdr = (u32)val; - else - pr_err("get rk_cam_hdr fail\n"); - return 1; -} - -static int __init __maybe_unused rk_cam_w_setup(char *str) -{ - int ret = 0; - unsigned long val = 0; - - ret = kstrtoul(str, 0, &val); - if (!ret) - rk_cam_w = (u32)val; - else - pr_err("get rk_cam_w fail\n"); - return 1; -} - -static int __init __maybe_unused rk_cam_h_setup(char *str) -{ - int ret = 0; - unsigned long val = 0; - - ret = kstrtoul(str, 0, &val); - if (!ret) - rk_cam_h = (u32)val; - else - pr_err("get rk_cam_h fail\n"); - return 1; -} - -static int __init __maybe_unused rk_cam_fps_setup(char *str) -{ - int ret = 0; - unsigned long val = 0; - - ret = kstrtoul(str, 0, &val); - if (!ret) - rk_cam_fps = (u32)val; - else - pr_err("get rk_cam_fps fail\n"); - return 1; -} - -__setup("rk_cam_hdr=", rk_cam_hdr_setup); -__setup("rk_cam_w=", rk_cam_w_setup); -__setup("rk_cam_h=", rk_cam_h_setup); -__setup("rk_cam_fps=", rk_cam_fps_setup); - static void find_terminal_resolution(struct gc2093 *gc2093) { int i = 0; @@ -1682,6 +1621,10 @@ static void find_terminal_resolution(struct gc2093 *gc2093) u32 cur_fps = 0; u32 dst_fps = 0; u32 tmp_fps = 0; + u32 rk_cam_hdr = get_rk_cam_hdr(); + u32 rk_cam_w = get_rk_cam_w(); + u32 rk_cam_h = get_rk_cam_h(); + u32 rk_cam_fps = get_rk_cam_fps(); if (rk_cam_w == 0 || rk_cam_h == 0 || rk_cam_fps == 0) From 4bbe1970adadcdb2efeffa4bde3030ab9a488bf7 Mon Sep 17 00:00:00 2001 From: Yu Qiaowei Date: Tue, 10 Oct 2023 16:41:35 +0800 Subject: [PATCH 39/55] video: rockchip: rga2: add hardware time-consuming printing Signed-off-by: Yu Qiaowei Change-Id: I780056e58892cf9c265026119ebbe0c6eb3dc75d --- drivers/video/rockchip/rga2/rga2_drv.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/video/rockchip/rga2/rga2_drv.c b/drivers/video/rockchip/rga2/rga2_drv.c index 9aeb89ac682a..ca5218f3e09f 100644 --- a/drivers/video/rockchip/rga2/rga2_drv.c +++ b/drivers/video/rockchip/rga2/rga2_drv.c @@ -76,7 +76,6 @@ #define RGA2_PHY_PAGE_SIZE (((8192 * 8192 * 4) / 4096) + 1) ktime_t rga2_start; -ktime_t rga2_end; int rga2_flag; int first_RGA2_proc; static int rk3368; @@ -1179,9 +1178,11 @@ retry: #ifdef CONFIG_ROCKCHIP_RGA2_DEBUGGER if (RGA2_TEST_TIME) { - rga2_end = ktime_get(); - rga2_end = ktime_sub(rga2_end, rga2_start); - DBG("sync one cmd end time %d\n", (int)ktime_to_us(rga2_end)); + ktime_t rga2_cmd_end; + + rga2_cmd_end = ktime_get(); + rga2_cmd_end = ktime_sub(rga2_cmd_end, rga2_start); + DBG("sync one cmd end time %d us\n", (int)ktime_to_us(rga2_cmd_end)); } #endif if (ret == -ETIMEDOUT && try--) { @@ -1657,6 +1658,14 @@ static irqreturn_t rga2_irq_thread(int irq, void *dev_id) if (RGA2_INT_FLAG) INFO("irqthread INT[%x],STATS[%x]\n", rga2_read(RGA2_INT), rga2_read(RGA2_STATUS)); + + if (RGA2_TEST_TIME) { + ktime_t rga2_hw_end; + + rga2_hw_end = ktime_get(); + rga2_hw_end = ktime_sub(rga2_hw_end, rga2_start); + DBG("RGA hardware cost time %d us\n", (int)ktime_to_us(rga2_hw_end)); + } #endif RGA2_flush_page(); mutex_lock(&rga2_service.lock); From da65c04d60a23e1078ca6dfbc85647a581d89c8b Mon Sep 17 00:00:00 2001 From: Wu Liangqing Date: Wed, 11 Oct 2023 08:51:56 +0000 Subject: [PATCH 40/55] arm64: dts: rockchip: remove androidboot bootargs for px30/rk3326 boards Signed-off-by: Wu Liangqing Change-Id: Ifb5eda4386ae04b298434c4e1d175eed941c4278 --- arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10-avb.dts | 4 ---- arch/arm64/boot/dts/rockchip/rk3326-863-lp3-v10-rkisp1.dts | 4 ---- 2 files changed, 8 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10-avb.dts b/arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10-avb.dts index 7b122c5461f8..fd9b6a2a5386 100644 --- a/arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10-avb.dts +++ b/arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10-avb.dts @@ -13,10 +13,6 @@ compatible = "rockchip,px30-evb-ddr3-v10-avb", "rockchip,px30"; }; -&chosen { - bootargs_ext = "androidboot.boot_devices=ff390000.dwmmc,ff3b0000.nandc"; -}; - &dsi { status = "okay"; diff --git a/arch/arm64/boot/dts/rockchip/rk3326-863-lp3-v10-rkisp1.dts b/arch/arm64/boot/dts/rockchip/rk3326-863-lp3-v10-rkisp1.dts index d80dad694c1d..2d0fa8a8e6cc 100644 --- a/arch/arm64/boot/dts/rockchip/rk3326-863-lp3-v10-rkisp1.dts +++ b/arch/arm64/boot/dts/rockchip/rk3326-863-lp3-v10-rkisp1.dts @@ -11,10 +11,6 @@ compatible = "rockchip,rk3326-863-lp3-v10-rkisp1", "rockchip,rk3326"; }; -&chosen { - bootargs_ext = "androidboot.boot_devices=ff390000.dwmmc,ff3b0000.nandc"; -}; - &i2c2 { status = "okay"; From 07f45d2725e2363b2e20aa96ba9ce9e70eeabe8d Mon Sep 17 00:00:00 2001 From: Zhang Yubing Date: Tue, 19 Sep 2023 19:36:58 +0800 Subject: [PATCH 41/55] mfd: rkx110_x120: change panel device and driver name In the display-serdes panel driver, it has the same name "serdes-panel" as rkserdes panel device. When register rkserdes panel device, it may mismatch to the display-serdes panel driver. To avoid this issue happen, change the rkserdes panel device name. For the same reason, change the rkserdes panel driver name. Signed-off-by: Zhang Yubing Change-Id: Ic146472c40252b3e55b2d46f1a6bfd099eb013c3 --- drivers/mfd/rkx110_x120/rkx110_x120_core.c | 2 +- drivers/mfd/rkx110_x120/rkx110_x120_panel.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/rkx110_x120/rkx110_x120_core.c b/drivers/mfd/rkx110_x120/rkx110_x120_core.c index 95d875be8a57..2510e652eb4c 100644 --- a/drivers/mfd/rkx110_x120/rkx110_x120_core.c +++ b/drivers/mfd/rkx110_x120/rkx110_x120_core.c @@ -22,7 +22,7 @@ static const struct mfd_cell rkx110_x120_devs[] = { /* 2 panel device for rkx110_x120 drm panel */ { - .name = "serdes-panel", + .name = "rockchip-serdes-panel", .of_compatible = "rockchip,serdes-panel", }, }; diff --git a/drivers/mfd/rkx110_x120/rkx110_x120_panel.c b/drivers/mfd/rkx110_x120/rkx110_x120_panel.c index 38b76f06a184..2896fff4f9a5 100644 --- a/drivers/mfd/rkx110_x120/rkx110_x120_panel.c +++ b/drivers/mfd/rkx110_x120/rkx110_x120_panel.c @@ -630,7 +630,7 @@ static struct platform_driver serdes_panel_driver = { .probe = serdes_panel_probe, .remove = serdes_panel_remove, .driver = { - .name = "serdes-panel", + .name = "rockchip-serdes-panel", .of_match_table = serdes_panel_of_table, }, }; From ccaa5a718372de677a3ca0d7e48fbdb27c313dbe Mon Sep 17 00:00:00 2001 From: shengfei Xu Date: Wed, 11 Oct 2023 01:50:59 +0000 Subject: [PATCH 42/55] regulator: rk806: delete the PLDO6 enable/disable interface The PLDO6 must be always on. if the PLDO6 is off, the PMIC will be abnormal. Signed-off-by: shengfei Xu Change-Id: I5c2cf8f0d2ba3cb08cd7538d998857d8c2db55f2 --- drivers/regulator/rk806-regulator.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/rk806-regulator.c b/drivers/regulator/rk806-regulator.c index a01d7001db00..638753b9fe3a 100644 --- a/drivers/regulator/rk806-regulator.c +++ b/drivers/regulator/rk806-regulator.c @@ -942,6 +942,20 @@ static const struct regulator_ops rk806_ops_ldo = { .set_suspend_disable = rk806_set_suspend_disable, }; +static const struct regulator_ops rk806_ops_ldo6 = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + + .get_voltage_sel = rk806_get_voltage_sel_regmap, + .set_voltage = rk806_set_voltage, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + + .set_ramp_delay = rk806_set_ramp_delay, + + .set_suspend_voltage = rk806_set_suspend_voltage_range, + .resume = rk806_regulator_resume, +}; + #define RK806_REGULATOR(_name, _supply_name, _id, _ops,\ _n_voltages, _vr, _er, _lr, ctrl_bit)\ [_id] = {\ @@ -1033,7 +1047,7 @@ static const struct regulator_desc rk806_regulators[] = { RK806_LDO_SEL_CNT, RK806_PLDO5_ON_VSEL, RK806_POWER_EN5, rk806_ldo_voltage_ranges, 1), - RK806_REGULATOR("PLDO_REG6", "vcca", RK806_ID_PLDO6, rk806_ops_ldo, + RK806_REGULATOR("PLDO_REG6", "vcca", RK806_ID_PLDO6, rk806_ops_ldo6, RK806_LDO_SEL_CNT, RK806_PLDO6_ON_VSEL, RK806_POWER_EN4, rk806_ldo_voltage_ranges, 0), }; From a095b986015598f2aa1ef1e424f766a8bb65f4a3 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 11 Oct 2023 08:58:14 +0800 Subject: [PATCH 43/55] PCI: rockchip: dw: remove .link_up() hook from struct dw_pcie_ops [2.353901] rk-pcie 3c0000000.pcie Link up. LTSSM is 0x1 [2.354036] rk-pcie 3c0000000.pcie: PCI host bridge to bus 0000 :00 [2.354058] pci_bus 0000:00: root bus resource [bus 00-0f] [2.354074] pci_bus 0000:00: root bus resource [??? 0x4000000-0xf40fffff flags 0x0] ... The original link event is checking LTSSM and ensure it's in L0. However enabling ASPM will make accessing config space failed. So commit 824c99261a9a ("PCI: rockchip: dw: Update link up check state") remove the LTSSM check. But it introduce a situation that if link still in training and host bridge tries to enumerate slot, it will fail unexpectedly. Fix this by removing rk_pcie_link_up and let dwc core use its own port logic to decide the link state. Signed-off-by: Shawn Lin Change-Id: I41f05a8aa89ac33782d569ffa7d466cf95981c68 --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 4e5a2251b49b..67b11fbf9e18 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -702,24 +702,6 @@ static inline void rk_pcie_enable_ltssm(struct rk_pcie *rk_pcie) rk_pcie_writel_apb(rk_pcie, 0x0, 0xC000C); } -static int rk_pcie_link_up(struct dw_pcie *pci) -{ - struct rk_pcie *rk_pcie = to_rk_pcie(pci); - u32 val; - - if (rk_pcie->is_rk1808) { - val = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_GENERAL_DEBUG); - if ((val & (PCIE_PHY_LINKUP | PCIE_DATA_LINKUP)) == 0x3) - return 1; - } else { - val = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS); - if ((val & (RDLH_LINKUP | SMLH_LINKUP)) == 0x30000) - return 1; - } - - return 0; -} - static void rk_pcie_enable_debug(struct rk_pcie *rk_pcie) { if (!IS_ENABLED(CONFIG_DEBUG_FS)) @@ -1601,7 +1583,6 @@ MODULE_DEVICE_TABLE(of, rk_pcie_of_match); static const struct dw_pcie_ops dw_pcie_ops = { .start_link = rk_pcie_establish_link, - .link_up = rk_pcie_link_up, }; static int rk1808_pcie_fixup(struct rk_pcie *rk_pcie, struct device_node *np) From ec9514c1ad7df61a297c49b9a55a819b6c715f86 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Tue, 13 Sep 2022 08:42:43 +0000 Subject: [PATCH 44/55] media: i2c: lt6911uxc: add 4K60 dual mipi port support Signed-off-by: Jianwei Fan Change-Id: Ib70ec05ed521b924f5e10343a3399d629ee40dbe --- drivers/media/i2c/lt6911uxc.c | 220 ++++++++++++++++++++++++++-------- drivers/media/i2c/lt6911uxc.h | 5 + 2 files changed, 174 insertions(+), 51 deletions(-) diff --git a/drivers/media/i2c/lt6911uxc.c b/drivers/media/i2c/lt6911uxc.c index cab313ca5a70..c0ccb87e9d3b 100644 --- a/drivers/media/i2c/lt6911uxc.c +++ b/drivers/media/i2c/lt6911uxc.c @@ -6,6 +6,8 @@ * V0.0X01.0X00 first version. * V0.0X01.0X01 fix if plugin_gpio was not used. * V0.0X01.0X02 modify driver init level to late_initcall. + * V0.0X01.0X03 add 4K60 dual mipi support + * */ #include @@ -34,12 +36,16 @@ #include #include "lt6911uxc.h" -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x2) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x3) #define LT6911UXC_NAME "LT6911UXC" -#define LT6911UXC_LINK_FREQ_HIGH 400000000 -#define LT6911UXC_LINK_FREQ_LOW 200000000 -#define LT6911UXC_PIXEL_RATE 400000000 +#define LT6911UXC_LINK_FREQ_600M 600000000 +#define LT6911UXC_LINK_FREQ_400M 400000000 +#define LT6911UXC_LINK_FREQ_300M 300000000 +#define LT6911UXC_LINK_FREQ_200M 200000000 +#define LT6911UXC_LINK_FREQ_100M 100000000 +#define LT6911UXC_LINK_FREQ_60M 60000000 +#define LT6911UXC_PIXEL_RATE 600000000 #define I2C_MAX_XFER_SIZE 128 @@ -54,8 +60,12 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-2)"); static const s64 link_freq_menu_items[] = { - LT6911UXC_LINK_FREQ_HIGH, - LT6911UXC_LINK_FREQ_LOW, + LT6911UXC_LINK_FREQ_600M, + LT6911UXC_LINK_FREQ_400M, + LT6911UXC_LINK_FREQ_300M, + LT6911UXC_LINK_FREQ_200M, + LT6911UXC_LINK_FREQ_100M, + LT6911UXC_LINK_FREQ_60M, }; struct lt6911uxc { @@ -78,6 +88,7 @@ struct lt6911uxc { struct v4l2_dv_timings timings; struct v4l2_mbus_config_mipi_csi2 bus; struct v4l2_subdev sd; + struct rkmodule_multi_dev_info multi_dev_info; const char *len_name; const char *module_facing; const char *module_name; @@ -100,13 +111,14 @@ struct lt6911uxc_mode { u32 hts_def; u32 vts_def; u32 exp_def; + u32 mipi_freq_idx; }; static const struct v4l2_dv_timings_cap lt6911uxc_timings_cap = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 400000000, + V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 600000000, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, V4L2_DV_BT_CAP_PROGRESSIVE | @@ -117,6 +129,16 @@ static const struct v4l2_dv_timings_cap lt6911uxc_timings_cap = { static const struct lt6911uxc_mode supported_modes[] = { { + .width = 3840, + .height = 2160, + .max_fps = { + .numerator = 10000, + .denominator = 600000, + }, + .hts_def = 4400, + .vts_def = 2250, + .mipi_freq_idx = 0, + }, { .width = 3840, .height = 2160, .max_fps = { @@ -125,6 +147,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 4400, .vts_def = 2250, + .mipi_freq_idx = 0, }, { .width = 1920, .height = 1080, @@ -134,6 +157,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 2200, .vts_def = 1125, + .mipi_freq_idx = 2, }, { .width = 1920, .height = 540, @@ -141,6 +165,7 @@ static const struct lt6911uxc_mode supported_modes[] = { .numerator = 10000, .denominator = 600000, }, + .mipi_freq_idx = 3, }, { .width = 1440, .height = 240, @@ -148,6 +173,7 @@ static const struct lt6911uxc_mode supported_modes[] = { .numerator = 10000, .denominator = 600000, }, + .mipi_freq_idx = 4, }, { .width = 1440, .height = 288, @@ -155,6 +181,7 @@ static const struct lt6911uxc_mode supported_modes[] = { .numerator = 10000, .denominator = 500000, }, + .mipi_freq_idx = 4, }, { .width = 1280, .height = 720, @@ -164,6 +191,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 1650, .vts_def = 750, + .mipi_freq_idx = 3, }, { .width = 720, .height = 576, @@ -173,6 +201,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 864, .vts_def = 625, + .mipi_freq_idx = 5, }, { .width = 720, .height = 480, @@ -182,6 +211,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 858, .vts_def = 525, + .mipi_freq_idx = 5, }, }; @@ -390,6 +420,7 @@ static int lt6911uxc_get_detected_timings(struct v4l2_subdev *sd, u8 value, val_h, val_l; u32 fw_ver, mipi_byte_clk, mipi_bitrate; u8 fw_a, fw_b, fw_c, fw_d, lanes; + u8 video_fmt; int ret; memset(timings, 0, sizeof(struct v4l2_dv_timings)); @@ -421,13 +452,15 @@ static int lt6911uxc_get_detected_timings(struct v4l2_subdev *sd, i2c_rd8(sd, MIPI_LANES, &lanes); lt6911uxc->csi_lanes_in_use = lanes; + if (lt6911uxc->csi_lanes_in_use == 8) + v4l2_info(sd, "get 8 lane in use, set dual mipi mode\n"); i2c_wr8(sd, FM1_DET_CLK_SRC_SEL, AD_LMTX_WRITE_CLK); i2c_rd8(sd, FREQ_METER_H, &clk_h); i2c_rd8(sd, FREQ_METER_M, &clk_m); i2c_rd8(sd, FREQ_METER_L, &clk_l); mipi_byte_clk = (((clk_h & 0xf) << 16) | (clk_m << 8) | clk_l); mipi_bitrate = mipi_byte_clk * 8 / 1000; - v4l2_info(sd, "MIPI Byte clk: %dKHz, MIPI bitrate: %dMbps, lanes:%d\n", + v4l2_info(sd, "MIPI Byte clk: %uKHz, MIPI bitrate: %uMbps, lanes:%d\n", mipi_byte_clk, mipi_bitrate, lanes); i2c_rd8(sd, HTOTAL_H, &val_h); @@ -457,8 +490,16 @@ static int lt6911uxc_get_detected_timings(struct v4l2_subdev *sd, hbp = ((val_h << 8) | val_l) * 2; i2c_rd8(sd, VBP, &value); vbp = value; + i2c_rd8(sd, COLOR_FMT_STATUS, &video_fmt); + video_fmt = (video_fmt & GENMASK(6, 5)) >> 5; lt6911uxc_i2c_disable(sd); + if (video_fmt == 0x3) { + lt6911uxc->nosignal = true; + v4l2_err(sd, "%s ERROR: HDMI input YUV420, don't support YUV420!\n", __func__); + return -EINVAL; + } + if (!lt6911uxc_rcv_supported_res(sd, hact, vact)) { lt6911uxc->nosignal = true; v4l2_err(sd, "%s: rcv err res, return no signal!\n", __func__); @@ -694,7 +735,6 @@ static int lt6911uxc_s_dv_timings(struct v4l2_subdev *sd, } lt6911uxc->timings = *timings; - enable_stream(sd, false); return 0; @@ -818,11 +858,50 @@ static int lt6911uxc_enum_frame_interval(struct v4l2_subdev *sd, return 0; } +static int lt6911uxc_get_reso_dist(const struct lt6911uxc_mode *mode, + struct v4l2_dv_timings *timings) +{ + struct v4l2_bt_timings *bt = &timings->bt; + u32 cur_fps, dist_fps; + + cur_fps = fps_calc(bt); + dist_fps = DIV_ROUND_CLOSEST(mode->max_fps.denominator, mode->max_fps.numerator); + + return abs(mode->width - bt->width) + + abs(mode->height - bt->height) + abs(dist_fps - cur_fps); +} + +static const struct lt6911uxc_mode * +lt6911uxc_find_best_fit(struct lt6911uxc *lt6911uxc) +{ + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = lt6911uxc_get_reso_dist(&supported_modes[i], <6911uxc->timings); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + dev_info(<6911uxc->i2c_client->dev, + "find current mode: support_mode[%d], %dx%d@%dfps\n", + cur_best_fit, supported_modes[cur_best_fit].width, + supported_modes[cur_best_fit].height, + DIV_ROUND_CLOSEST(supported_modes[cur_best_fit].max_fps.denominator, + supported_modes[cur_best_fit].max_fps.numerator)); + + return &supported_modes[cur_best_fit]; +} + static int lt6911uxc_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct lt6911uxc *lt6911uxc = to_state(sd); + const struct lt6911uxc_mode *mode; mutex_lock(<6911uxc->confctl_mutex); format->format.code = lt6911uxc->mbus_fmt_code; @@ -832,6 +911,14 @@ static int lt6911uxc_get_fmt(struct v4l2_subdev *sd, lt6911uxc->timings.bt.interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; format->format.colorspace = V4L2_COLORSPACE_SRGB; + + mode = lt6911uxc_find_best_fit(lt6911uxc); + lt6911uxc->cur_mode = mode; + __v4l2_ctrl_s_ctrl_int64(lt6911uxc->pixel_rate, + LT6911UXC_PIXEL_RATE); + __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, + mode->mipi_freq_idx); + mutex_unlock(<6911uxc->confctl_mutex); v4l2_dbg(1, debug, sd, "%s: fmt code:%d, w:%d, h:%d, field mode:%s\n", @@ -841,40 +928,12 @@ static int lt6911uxc_get_fmt(struct v4l2_subdev *sd, return 0; } -static int lt6911uxc_get_reso_dist(const struct lt6911uxc_mode *mode, - struct v4l2_mbus_framefmt *framefmt) -{ - return abs(mode->width - framefmt->width) + - abs(mode->height - framefmt->height); -} - -static const struct lt6911uxc_mode * -lt6911uxc_find_best_fit(struct v4l2_subdev_format *fmt) -{ - struct v4l2_mbus_framefmt *framefmt = &fmt->format; - int dist; - int cur_best_fit = 0; - int cur_best_fit_dist = -1; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { - dist = lt6911uxc_get_reso_dist(&supported_modes[i], framefmt); - if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { - cur_best_fit_dist = dist; - cur_best_fit = i; - } - } - - return &supported_modes[cur_best_fit]; -} - static int lt6911uxc_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct lt6911uxc *lt6911uxc = to_state(sd); const struct lt6911uxc_mode *mode; - int index; /* is overwritten by get_fmt */ u32 code = format->format.code; @@ -897,20 +956,10 @@ static int lt6911uxc_set_fmt(struct v4l2_subdev *sd, return 0; lt6911uxc->mbus_fmt_code = format->format.code; - mode = lt6911uxc_find_best_fit(format); + mode = lt6911uxc_find_best_fit(lt6911uxc); lt6911uxc->cur_mode = mode; enable_stream(sd, false); - if (((mode->width == 720) && (mode->height == 576)) || - ((mode->width == 720) && (mode->height == 480))) - index = 1; - else - index = 0; - - __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, index); - v4l2_dbg(1, debug, sd, "%s res wxh:%dx%d, link freq:%llu", __func__, - mode->width, mode->height, link_freq_menu_items[index]); - return 0; } @@ -939,7 +988,9 @@ static void lt6911uxc_get_module_inf(struct lt6911uxc *lt6911uxc, static long lt6911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct lt6911uxc *lt6911uxc = to_state(sd); + struct device *dev = <6911uxc->i2c_client->dev; long ret = 0; + struct rkmodule_capture_info *capture_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -948,6 +999,16 @@ static long lt6911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) case RKMODULE_GET_HDMI_MODE: *(int *)arg = RKMODULE_HDMIIN_MODE; break; + case RKMODULE_GET_CAPTURE_MODE: + capture_info = (struct rkmodule_capture_info *)arg; + if (lt6911uxc->csi_lanes_in_use == 8) { + dev_info(dev, "8 lanes in use, set dual mipi mode\n"); + capture_info->mode = RKMODULE_MULTI_DEV_COMBINE_ONE; + capture_info->multi_dev = lt6911uxc->multi_dev_info; + } else { + capture_info->mode = 0; + } + break; default: ret = -ENOIOCTLCMD; break; @@ -964,6 +1025,7 @@ static long lt6911uxc_compat_ioctl32(struct v4l2_subdev *sd, struct rkmodule_inf *inf; long ret; int *seq; + struct rkmodule_capture_info *capture_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -996,6 +1058,21 @@ static long lt6911uxc_compat_ioctl32(struct v4l2_subdev *sd, } kfree(seq); break; + case RKMODULE_GET_CAPTURE_MODE: + capture_info = kzalloc(sizeof(*capture_info), GFP_KERNEL); + if (!capture_info) { + ret = -ENOMEM; + return ret; + } + + ret = lt6911uxc_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; @@ -1075,9 +1152,11 @@ static void lt6911uxc_reset(struct lt6911uxc *lt6911uxc) static int lt6911uxc_init_v4l2_ctrls(struct lt6911uxc *lt6911uxc) { + const struct lt6911uxc_mode *mode; struct v4l2_subdev *sd; int ret; + mode = lt6911uxc->cur_mode; sd = <6911uxc->sd; ret = v4l2_ctrl_handler_init(<6911uxc->hdl, 5); if (ret) @@ -1087,8 +1166,9 @@ static int lt6911uxc_init_v4l2_ctrls(struct lt6911uxc *lt6911uxc) V4L2_CID_LINK_FREQ, ARRAY_SIZE(link_freq_menu_items) - 1, 0, link_freq_menu_items); - v4l2_ctrl_new_std(<6911uxc->hdl, NULL, V4L2_CID_PIXEL_RATE, - 0, LT6911UXC_PIXEL_RATE, 1, LT6911UXC_PIXEL_RATE); + lt6911uxc->pixel_rate = v4l2_ctrl_new_std(<6911uxc->hdl, NULL, + V4L2_CID_PIXEL_RATE, + 0, LT6911UXC_PIXEL_RATE, 1, LT6911UXC_PIXEL_RATE); lt6911uxc->detect_tx_5v_ctrl = v4l2_ctrl_new_std(<6911uxc->hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT, @@ -1107,6 +1187,9 @@ static int lt6911uxc_init_v4l2_ctrls(struct lt6911uxc *lt6911uxc) return ret; } + __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, mode->mipi_freq_idx); + __v4l2_ctrl_s_ctrl_int64(lt6911uxc->pixel_rate, LT6911UXC_PIXEL_RATE); + if (lt6911uxc_update_controls(sd)) { ret = -ENODEV; v4l2_err(sd, "update v4l2 ctrls failed! ret:%d\n", ret); @@ -1291,6 +1374,36 @@ static struct attribute *lt6911_attrs[] = { }; ATTRIBUTE_GROUPS(lt6911); +static int lt6911uxc_get_multi_dev_info(struct lt6911uxc *lt6911uxc) +{ + struct device *dev = <6911uxc->i2c_client->dev; + struct device_node *node = dev->of_node; + struct device_node *multi_info_np; + + 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", + <6911uxc->multi_dev_info.dev_idx[0]); + of_property_read_u32(multi_info_np, "dev-idx-r", + <6911uxc->multi_dev_info.dev_idx[1]); + of_property_read_u32(multi_info_np, "combine-idx", + <6911uxc->multi_dev_info.combine_idx[0]); + of_property_read_u32(multi_info_np, "pixel-offset", + <6911uxc->multi_dev_info.pixel_offset); + of_property_read_u32(multi_info_np, "dev-num", + <6911uxc->multi_dev_info.dev_num); + dev_info(dev, + "multi dev left: mipi%d, multi dev right: mipi%d, combile mipi%d, dev num: %d\n", + lt6911uxc->multi_dev_info.dev_idx[0], lt6911uxc->multi_dev_info.dev_idx[1], + lt6911uxc->multi_dev_info.combine_idx[0], lt6911uxc->multi_dev_info.dev_num); + + return 0; +} + static int lt6911uxc_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1323,6 +1436,10 @@ static int lt6911uxc_probe(struct i2c_client *client, return err; } + err = lt6911uxc_get_multi_dev_info(lt6911uxc); + if (err) + v4l2_info(sd, "get multi dev info failed, not use dual mipi mode\n"); + err = lt6911uxc_check_chip_id(lt6911uxc); if (err < 0) return err; @@ -1477,9 +1594,10 @@ static void __exit lt6911uxc_driver_exit(void) i2c_del_driver(<6911uxc_driver); } -late_initcall(lt6911uxc_driver_init); +device_initcall_sync(lt6911uxc_driver_init); module_exit(lt6911uxc_driver_exit); MODULE_DESCRIPTION("Lontium LT6911UXC HDMI to MIPI CSI-2 bridge driver"); MODULE_AUTHOR("Dingxian Wen "); +MODULE_AUTHOR("Jianwei Fan "); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/lt6911uxc.h b/drivers/media/i2c/lt6911uxc.h index 45cb29764879..29c9217172ba 100644 --- a/drivers/media/i2c/lt6911uxc.h +++ b/drivers/media/i2c/lt6911uxc.h @@ -65,4 +65,9 @@ #define AUDIO_SAMPLE_RATAE_H 0xb0aa #define AUDIO_SAMPLE_RATAE_L 0xb0ab +#define COLOR_FMT_STATUS 0xb092 +#define STREAM_CTRL 0x811d +#define STREAM_ENABLE 0xFB +#define STREAM_DISABLE 0xBB + #endif From 81b6116b245f359b2badab53bfdca6e5e074ac27 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Wed, 11 Oct 2023 06:28:58 +0000 Subject: [PATCH 45/55] arm64: dts: rockchip: rk3568-evb: fix gc8034 power domain config Signed-off-by: Jianwei Fan Change-Id: I2c21854981952f5784de077f397bd1469835abe2 --- arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi | 1 + arch/arm64/boot/dts/rockchip/rk3568-evb5-ddr4-v10.dtsi | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi b/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi index 7ef900001f51..dc9ad4236dd3 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi @@ -273,6 +273,7 @@ clock-names = "xvclk"; pinctrl-names = "default"; pinctrl-0 = <&cif_clk>; + power-domains = <&power RK3568_PD_VI>; reset-gpios = <&gpio3 RK_PB6 GPIO_ACTIVE_LOW>; pwdn-gpios = <&gpio4 RK_PB4 GPIO_ACTIVE_LOW>; rockchip,grf = <&grf>; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb5-ddr4-v10.dtsi b/arch/arm64/boot/dts/rockchip/rk3568-evb5-ddr4-v10.dtsi index 4a51275109fb..beba4ab09426 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-evb5-ddr4-v10.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb5-ddr4-v10.dtsi @@ -304,6 +304,7 @@ clock-names = "xvclk"; pinctrl-names = "default"; pinctrl-0 = <&cif_clk>; + power-domains = <&power RK3568_PD_VI>; reset-gpios = <&gpio3 RK_PB6 GPIO_ACTIVE_LOW>; pwdn-gpios = <&gpio4 RK_PB4 GPIO_ACTIVE_LOW>; rockchip,grf = <&grf>; From 4367b7238080582187d17ac5ae30b95046b7771d Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 22 Sep 2023 09:57:32 +0800 Subject: [PATCH 46/55] dmaengine: pl330: Add support for interleaved transfer This patch add support for interleaved transfer which used for interleaved audio or 2d video data transfer. for audio situation, we add 'nump' for number of period frames. Signed-off-by: Sugar Zhang Change-Id: I502ea9c86c8403dc5b1f38abf40be8b6ee13c1dc --- drivers/dma/pl330.c | 175 ++++++++++++++++++++++++-------------- include/linux/dmaengine.h | 4 + 2 files changed, 114 insertions(+), 65 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 0ff9bd2fa5e7..255a1f1bfaf2 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -541,11 +541,9 @@ struct dma_pl330_desc { /* For cyclic capability */ bool cyclic; size_t num_periods; -#ifdef CONFIG_NO_GKI - /* interlace size */ - unsigned int src_interlace_size; - unsigned int dst_interlace_size; -#endif + + /* interleaved size */ + struct data_chunk sgl; }; struct _xfer_spec { @@ -1204,7 +1202,7 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, const struct _xfer_spec *pxs, int cyc, enum pl330_cond cond) { - int off = 0; + int off = 0, i = 0, burstn = 1; /* * do FLUSHP at beginning to clear any stale dma requests before the @@ -1212,30 +1210,36 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, */ if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); + + if (pxs->desc->sgl.size) { + WARN_ON(BYTE_MOD_BURST_LEN(pxs->desc->sgl.size, pxs->ccr)); + burstn = BYTE_TO_BURST(pxs->desc->sgl.size, pxs->ccr); + } + while (cyc--) { - off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); - off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, - pxs->desc->peri); - off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, - pxs->desc->peri); -#ifdef CONFIG_NO_GKI + for (i = 0; i < burstn; i++) { + off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); + off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, + pxs->desc->peri); + off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, + pxs->desc->peri); + } + switch (pxs->desc->rqtype) { case DMA_DEV_TO_MEM: - - if (pxs->desc->dst_interlace_size) + if (pxs->desc->sgl.dst_icg) off += _emit_ADDH(dry_run, &buf[off], DST, - pxs->desc->dst_interlace_size); + pxs->desc->sgl.dst_icg); break; case DMA_MEM_TO_DEV: - if (pxs->desc->src_interlace_size) + if (pxs->desc->sgl.src_icg) off += _emit_ADDH(dry_run, &buf[off], SRC, - pxs->desc->src_interlace_size); + pxs->desc->sgl.src_icg); break; default: WARN_ON(1); break; } -#endif } return off; @@ -1450,9 +1454,7 @@ static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], off += _emit_LPEND(dry_run, &buf[off], &lpend); } -#ifdef CONFIG_NO_GKI - if (!pxs->desc->src_interlace_size && - !pxs->desc->dst_interlace_size) { + if (!pxs->desc->sgl.src_icg && !pxs->desc->sgl.dst_icg) { num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); if (num_dregs) { @@ -1460,14 +1462,6 @@ static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); } } -#else - num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); - - if (num_dregs) { - off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); - off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); - } -#endif off += _emit_SEV(dry_run, &buf[off], ev); @@ -1535,26 +1529,18 @@ static inline int _setup_loops(struct pl330_dmac *pl330, BRST_SIZE(ccr); int off = 0; -#ifdef CONFIG_NO_GKI - if (pxs->desc->rqtype == DMA_DEV_TO_MEM) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) + - pxs->desc->dst_interlace_size); - else if (pxs->desc->rqtype == DMA_MEM_TO_DEV) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) + - pxs->desc->src_interlace_size); -#endif + if (pxs->desc->sgl.size) + bursts = x->bytes / pxs->desc->sgl.size; + while (bursts) { c = bursts; off += _loop(pl330, dry_run, &buf[off], &c, pxs); bursts -= c; } -#ifdef CONFIG_NO_GKI - if (!pxs->desc->src_interlace_size && - !pxs->desc->dst_interlace_size) + + if (!pxs->desc->sgl.src_icg && !pxs->desc->sgl.dst_icg) off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); -#else - off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); -#endif + return off; } @@ -1585,14 +1571,9 @@ static inline int _setup_xfer_cyclic(struct pl330_dmac *pl330, unsigned long bursts = BYTE_TO_BURST(x->bytes, ccr); int off = 0; -#ifdef CONFIG_NO_GKI - if (pxs->desc->rqtype == DMA_DEV_TO_MEM) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) - + pxs->desc->dst_interlace_size); - else if (pxs->desc->rqtype == DMA_MEM_TO_DEV) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) - + pxs->desc->src_interlace_size); -#endif + if (pxs->desc->sgl.size) + bursts = x->bytes / pxs->desc->sgl.size; + /* Setup Loop(s) */ off += _loop_cyclic(pl330, dry_run, &buf[off], bursts, pxs, ev); @@ -2452,10 +2433,6 @@ static int pl330_config_write(struct dma_chan *chan, pch->fifo_addr = slave_config->dst_addr; if (slave_config->dst_addr_width) pch->burst_sz = __ffs(slave_config->dst_addr_width); -#ifdef CONFIG_NO_GKI - if (slave_config->src_interlace_size) - pch->slave_config.src_interlace_size = slave_config->src_interlace_size; -#endif pch->burst_len = fixup_burst_len(slave_config->dst_maxburst, pch->dmac->quirks); } else if (direction == DMA_DEV_TO_MEM) { @@ -2463,10 +2440,6 @@ static int pl330_config_write(struct dma_chan *chan, pch->fifo_addr = slave_config->src_addr; if (slave_config->src_addr_width) pch->burst_sz = __ffs(slave_config->src_addr_width); -#ifdef CONFIG_NO_GKI - if (slave_config->dst_interlace_size) - pch->slave_config.dst_interlace_size = slave_config->dst_interlace_size; -#endif pch->burst_len = fixup_burst_len(slave_config->src_maxburst, pch->dmac->quirks); } @@ -2819,6 +2792,10 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) desc->cyclic = false; desc->num_periods = 1; + desc->sgl.size = 0; + desc->sgl.src_icg = 0; + desc->sgl.dst_icg = 0; + dma_async_tx_descriptor_init(&desc->txd, &pch->chan); return desc; @@ -2933,10 +2910,78 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( desc->cyclic = true; desc->num_periods = len / period_len; + + return &desc->txd; +} + +static struct dma_async_tx_descriptor *pl330_prep_interleaved_dma( + struct dma_chan *chan, struct dma_interleaved_template *xt, + unsigned long flags) +{ + struct dma_pl330_desc *desc = NULL; + struct dma_pl330_chan *pch = to_pchan(chan); + dma_addr_t dst = 0, src = 0; + size_t size, src_icg, dst_icg, period_bytes, buffer_bytes; + size_t nump = 0, numf = 0; + + if (!xt->numf || !xt->sgl[0].size || xt->frame_size != 1) + return NULL; + #ifdef CONFIG_NO_GKI - desc->src_interlace_size = pch->slave_config.src_interlace_size; - desc->dst_interlace_size = pch->slave_config.dst_interlace_size; + nump = xt->nump; #endif + numf = xt->numf; + size = xt->sgl[0].size; + period_bytes = size * nump; + buffer_bytes = size * numf; + + if (flags & DMA_PREP_REPEAT && (!nump || (numf % nump))) + return NULL; + + src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]); + dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]); + + pl330_config_write(chan, &pch->slave_config, xt->dir); + + if (!pl330_prep_slave_fifo(pch, xt->dir)) + return NULL; + + desc = pl330_get_desc(pch); + if (!desc) { + dev_err(chan->device->dev, "Failed to get desc\n"); + return NULL; + } + + if (xt->dir == DMA_MEM_TO_DEV) { + desc->rqcfg.src_inc = 1; + desc->rqcfg.dst_inc = 0; + src = xt->src_start; + dst = pch->fifo_dma; + } else { + desc->rqcfg.src_inc = 0; + desc->rqcfg.dst_inc = 1; + src = pch->fifo_dma; + dst = xt->dst_start; + } + + desc->rqtype = xt->dir; + desc->rqcfg.brst_size = pch->burst_sz; + desc->rqcfg.brst_len = pch->burst_len; + desc->bytes_requested = buffer_bytes; + desc->sgl.size = size; + desc->sgl.src_icg = src_icg; + desc->sgl.dst_icg = dst_icg; + + if (flags & DMA_PREP_REPEAT) { + desc->cyclic = true; + desc->num_periods = numf / nump; + fill_px(&desc->px, dst, src, period_bytes); + } else { + fill_px(&desc->px, dst, src, buffer_bytes); + } + + dev_dbg(chan->device->dev, "size: %zu, src_icg: %zu, dst_icg: %zu, nump: %zu, numf: %zu\n", + size, src_icg, dst_icg, nump, numf); return &desc->txd; } @@ -3068,10 +3113,6 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, desc->rqcfg.brst_len = pch->burst_len; desc->rqtype = direction; desc->bytes_requested = sg_dma_len(sg); -#ifdef CONFIG_NO_GKI - desc->src_interlace_size = pch->slave_config.src_interlace_size; - desc->dst_interlace_size = pch->slave_config.dst_interlace_size; -#endif } /* Return the last desc in the chain */ @@ -3306,12 +3347,16 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) dma_cap_set(DMA_SLAVE, pd->cap_mask); dma_cap_set(DMA_CYCLIC, pd->cap_mask); dma_cap_set(DMA_PRIVATE, pd->cap_mask); + dma_cap_set(DMA_INTERLEAVE, pd->cap_mask); + dma_cap_set(DMA_REPEAT, pd->cap_mask); + dma_cap_set(DMA_LOAD_EOT, pd->cap_mask); } pd->device_alloc_chan_resources = pl330_alloc_chan_resources; pd->device_free_chan_resources = pl330_free_chan_resources; pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy; pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic; + pd->device_prep_interleaved_dma = pl330_prep_interleaved_dma; pd->device_tx_status = pl330_tx_status; pd->device_prep_slave_sg = pl330_prep_slave_sg; pd->device_config = pl330_config; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 07d571b4eca7..236e5492da72 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -144,6 +144,7 @@ struct data_chunk { * Otherwise, destination is filled contiguously (icg ignored). * Ignored if dst_inc is false. * @numf: Number of frames in this template. + * @nump: Number of period frames in this template. * @frame_size: Number of chunks in a frame i.e, size of sgl[]. * @sgl: Array of {chunk,icg} pairs that make up a frame. */ @@ -156,6 +157,9 @@ struct dma_interleaved_template { bool src_sgl; bool dst_sgl; size_t numf; +#ifdef CONFIG_NO_GKI + size_t nump; +#endif size_t frame_size; struct data_chunk sgl[]; }; From 8f518bafab4127822cdce0b4d76e9f5f76c30df9 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Wed, 11 Oct 2023 17:20:39 +0800 Subject: [PATCH 47/55] arm64: dts: rockchip: rk3588s-evb3: disable gt1x in bt1120 display board The pins of gt1x and sii9022 are multiplexed. In addition, add support for bt656 by the same display ext board. Signed-off-by: Damon Ding Change-Id: I9164bd0a354a215cf5e04eec99946714c0638465 --- ...3588s-evb3-lp4x-v10-sii9022-bt1120-to-hdmi.dts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-evb3-lp4x-v10-sii9022-bt1120-to-hdmi.dts b/arch/arm64/boot/dts/rockchip/rk3588s-evb3-lp4x-v10-sii9022-bt1120-to-hdmi.dts index e2d5f25d53bf..4a03bc67aeb1 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-evb3-lp4x-v10-sii9022-bt1120-to-hdmi.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-evb3-lp4x-v10-sii9022-bt1120-to-hdmi.dts @@ -19,6 +19,13 @@ status = "disabled"; }; +/* + * The pins of gt1x and sii9022 are multiplexed + */ +>1x { + status = "disabled"; +}; + &i2c4 { clock-frequency = <400000>; status = "okay"; @@ -32,6 +39,10 @@ interrupts = ; reset-gpio = <&gpio1 RK_PA7 GPIO_ACTIVE_LOW>; enable-gpio = <&gpio1 RK_PB5 GPIO_ACTIVE_HIGH>; + /* + * MEDIA_BUS_FMT_UYVY8_1X16 for bt1120 + * MEDIA_BUS_FMT_UYVY8_2X8 for bt656 + */ bus-format = ; ports { @@ -60,6 +71,10 @@ &rgb { status = "okay"; pinctrl-names = "default"; + /* + * <&bt1120_pins> for bt1120 + * <&bt656_pins> for bt656 + */ pinctrl-0 = <&bt1120_pins>; ports { From fdb8c50bcde36586447fdaf533ed369586039519 Mon Sep 17 00:00:00 2001 From: Wangqiang Guo Date: Tue, 10 Oct 2023 07:09:20 +0000 Subject: [PATCH 48/55] input: sensors: add icm4260x driver. Change-Id: Ic1951c136bb81c0a93229e75e39605303cef3105 Signed-off-by: Wangqiang Guo --- drivers/input/sensors/accel/Kconfig | 6 + drivers/input/sensors/accel/Makefile | 1 + drivers/input/sensors/accel/icm4260x_acc.c | 470 +++++++++++++++++++++ drivers/input/sensors/gyro/Kconfig | 6 + drivers/input/sensors/gyro/Makefile | 1 + drivers/input/sensors/gyro/icm4260x_gyro.c | 211 +++++++++ include/linux/icm4260x.h | 432 +++++++++++++++++++ include/linux/sensor-dev.h | 2 + 8 files changed, 1129 insertions(+) create mode 100644 drivers/input/sensors/accel/icm4260x_acc.c create mode 100644 drivers/input/sensors/gyro/icm4260x_gyro.c create mode 100644 include/linux/icm4260x.h diff --git a/drivers/input/sensors/accel/Kconfig b/drivers/input/sensors/accel/Kconfig index 4576b8ccdb69..fb10b4c50c83 100644 --- a/drivers/input/sensors/accel/Kconfig +++ b/drivers/input/sensors/accel/Kconfig @@ -158,6 +158,12 @@ config ICM2060X_ACC To have support for your specific gsesnor you will have to select the proper drivers which depend on this option. +config ICM4260X_ACC + tristate "gsensor icm4260x" + help + To have support for your specific gsesnor you will have to + select the proper drivers which depend on this option. + config IAM20680_ACC tristate "gsensor iam20680" default n diff --git a/drivers/input/sensors/accel/Makefile b/drivers/input/sensors/accel/Makefile index 96f6cc6ee8e9..67e0bcdde3da 100644 --- a/drivers/input/sensors/accel/Makefile +++ b/drivers/input/sensors/accel/Makefile @@ -21,5 +21,6 @@ obj-$(CONFIG_GS_DA215S) += da215s/ obj-$(CONFIG_GS_DA223) += da223.o obj-$(CONFIG_GS_DA228E) += da228e/ obj-$(CONFIG_ICM2060X_ACC) += icm2060x_acc.o +obj-$(CONFIG_ICM4260X_ACC) += icm4260x_acc.o da223-y := da223_cust.o da223_core.o obj-$(CONFIG_IAM20680_ACC) += iam20680_acc.o diff --git a/drivers/input/sensors/accel/icm4260x_acc.c b/drivers/input/sensors/accel/icm4260x_acc.c new file mode 100644 index 000000000000..afc67123a844 --- /dev/null +++ b/drivers/input/sensors/accel/icm4260x_acc.c @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Rockchip Co.,Ltd. + * Author: Wangqiang Guo + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include + +/** + * icm4260x_set_idle() - Set Idle bit in PWR_MGMT_0 register + * @client: struct i2c_client.. + * + * Set ACCEL_LP_CLK_SEL as well when necessary with a proper wait + * + * Return: 0 when successful. + */ +static int icm4260x_set_idle(struct i2c_client *client) +{ + u8 reg_pwr_mgmt_0; + u8 d; + int ret = 0; + + reg_pwr_mgmt_0 = sensor_read_reg(client, ICM4260X_PWR_MGMT_0); + /* set Idle bit. + * when accel LPM is already enabled, set ACCEL_LP_CLK_SEL bit as well. + */ + d = reg_pwr_mgmt_0; + d |= BIT_IDLE; + if ((d & BIT_ACCEL_MODE_MASK) == BIT_ACCEL_MODE_LPM) + d |= BIT_ACCEL_LP_CLK_SEL; + + ret = sensor_write_reg(client, ICM4260X_PWR_MGMT_0, d); + usleep_range(20, 21); + + return ret; +} + +/** + * icm4260x_mreg_read() - Multiple byte read from MREG area. + * @client: struct i2c_client. + * @addr: MREG register start address including bank in upper byte. + * @len: length to read in byte. + * @data: pointer to store read data. + * + * Return: 0 when successful. + */ +static int icm4260x_mreg_read(struct i2c_client *client, int addr, int len, u8 *data) +{ + int ret; + u8 reg_pwr_mgmt_0; + + reg_pwr_mgmt_0 = sensor_read_reg(client, ICM4260X_PWR_MGMT_0); + + ret = icm4260x_set_idle(client); + if (ret) + return ret; + + ret = sensor_write_reg(client, ICM4260X_BLK_SEL_R, (addr >> 8) & 0xff); + usleep_range(INV_ICM42607_BLK_SEL_WAIT_US, + INV_ICM42607_BLK_SEL_WAIT_US + 1); + if (ret) + goto restore_bank; + + ret = sensor_write_reg(client, ICM4260X_MADDR_R, addr & 0xff); + usleep_range(INV_ICM42607_MADDR_WAIT_US, + INV_ICM42607_MADDR_WAIT_US + 1); + if (ret) + goto restore_bank; + + *data = ICM4260X_M_R; + ret = sensor_rx_data(client, data, len); + usleep_range(INV_ICM42607_M_RW_WAIT_US, + INV_ICM42607_M_RW_WAIT_US + 1); + if (ret) + goto restore_bank; + +restore_bank: + ret |= sensor_write_reg(client, ICM4260X_BLK_SEL_R, 0); + usleep_range(INV_ICM42607_BLK_SEL_WAIT_US, + INV_ICM42607_BLK_SEL_WAIT_US + 1); + + ret |= sensor_write_reg(client, ICM4260X_PWR_MGMT_0, reg_pwr_mgmt_0); + + return ret; +} + +/** + * icm4260x_mreg_single_write() - Single byte write to MREG area. + * @client: struct i2c_client. + * @addr: MREG register address including bank in upper byte. + * @data: data to write. + * + * Return: 0 when successful. + */ +static int icm4260x_mreg_single_write(struct i2c_client *client, int addr, u8 data) +{ + int ret; + u8 reg_pwr_mgmt_0; + + reg_pwr_mgmt_0 = sensor_read_reg(client, ICM4260X_PWR_MGMT_0); + + ret = icm4260x_set_idle(client); + if (ret) + return ret; + + ret = sensor_write_reg(client, ICM4260X_BLK_SEL_W, (addr >> 8) & 0xff); + usleep_range(INV_ICM42607_BLK_SEL_WAIT_US, + INV_ICM42607_BLK_SEL_WAIT_US + 1); + if (ret) + goto restore_bank; + + ret = sensor_write_reg(client, ICM4260X_MADDR_W, addr & 0xff); + usleep_range(INV_ICM42607_MADDR_WAIT_US, + INV_ICM42607_MADDR_WAIT_US + 1); + if (ret) + goto restore_bank; + + ret = sensor_write_reg(client, ICM4260X_M_W, data); + usleep_range(INV_ICM42607_M_RW_WAIT_US, + INV_ICM42607_M_RW_WAIT_US + 1); + if (ret) + goto restore_bank; + +restore_bank: + ret |= sensor_write_reg(client, ICM4260X_BLK_SEL_W, 0); + usleep_range(INV_ICM42607_BLK_SEL_WAIT_US, + INV_ICM42607_BLK_SEL_WAIT_US + 1); + + ret |= sensor_write_reg(client, ICM4260X_PWR_MGMT_0, reg_pwr_mgmt_0); + + return ret; +} + +/* + * OTP reload procedure. + */ +static int icm4260x_otp_reload(struct i2c_client *client) +{ + int ret; + u8 rb = 0; + + /* set idle bit */ + ret = icm4260x_set_idle(client); + if (ret) + return ret; + + /* Set OTP_COPY_MODE to 2'b01 */ + ret = icm4260x_mreg_read(client, ICM4260X_OTP_CONFIG_MREG_TOP1, 1, &rb); + if (ret) + return ret; + rb &= ~OTP_COPY_MODE_MASK; + rb |= BIT_OTP_COPY_NORMAL; + ret = icm4260x_mreg_single_write(client, ICM4260X_OTP_CONFIG_MREG_TOP1, rb); + if (ret) + return ret; + + /* set OTP_PWR_DOWN to 1'b0 and wait for 300us */ + ret = icm4260x_mreg_read(client, ICM4260X_OTP_CTRL7_MREG_OTP, 1, &rb); + if (ret) + return ret; + rb &= ~BIT_OTP_PWR_DOWN; + ret = icm4260x_mreg_single_write(client, ICM4260X_OTP_CTRL7_MREG_OTP, rb); + if (ret) + return ret; + usleep_range(300, 400); + + /* set OTP_RELOAD to 1'b1 and wait for 280us */ + ret = icm4260x_mreg_read(client, ICM4260X_OTP_CTRL7_MREG_OTP, 1, &rb); + if (ret) + return ret; + rb |= BIT_OTP_RELOAD; + ret = icm4260x_mreg_single_write(client, ICM4260X_OTP_CTRL7_MREG_OTP, rb); + if (ret) + return ret; + usleep_range(280, 380); + + return 0; +} + +static int sensor_active(struct i2c_client *client, int enable, int rate) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + u8 status = 0; + + sensor->ops->ctrl_data = sensor_read_reg(client, sensor->ops->ctrl_reg); + + if (!enable) { + status = (0xff & ~BIT_ACCEL_MODE_MASK); + sensor->ops->ctrl_data &= status; + } else { + status = BIT_ACCEL_MODE_LNM; + sensor->ops->ctrl_data |= status; + sensor->ops->ctrl_data &= ~BIT_IDLE; + } + + result = sensor_write_reg(client, sensor->ops->ctrl_reg, + sensor->ops->ctrl_data); + if (result) { + dev_err(&client->dev, + "%s: fail to set pwr_mgmt0(%d)\n", __func__, result); + return result; + } + usleep_range(250, 260); + + return result; +} + +/* + * write POR value + */ + +static int icm4260x_set_default_register(struct i2c_client *client) +{ + int status = 0; + + status |= sensor_write_reg(client, ICM4260X_GYRO_CONFIG0, 0x06); + status |= sensor_write_reg(client, ICM4260X_ACCEL_CONFIG0, 0x06); + status |= sensor_write_reg(client, ICM4260X_APEX_CONFIG0, 0x08); + status |= sensor_write_reg(client, ICM4260X_APEX_CONFIG1, 0x02); + status |= sensor_write_reg(client, ICM4260X_WOM_CONFIG, 0); + status |= sensor_write_reg(client, ICM4260X_FIFO_CONFIG1, 0x01); + status |= sensor_write_reg(client, ICM4260X_FIFO_CONFIG2, 0); + status |= sensor_write_reg(client, ICM4260X_FIFO_CONFIG3, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_FIFO_CONFIG5_MREG_TOP1, 0x20); + status |= icm4260x_mreg_single_write(client, ICM4260X_ST_CONFIG_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_INT_SOURCE7_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_INT_SOURCE8_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_INT_SOURCE9_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_INT_SOURCE10_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG2_MREG_TOP1, 0xA2); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG3_MREG_TOP1, 0x85); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG4_MREG_TOP1, 0x51); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG5_MREG_TOP1, 0x80); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG9_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG10_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG11_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_ACCEL_WOM_X_THR_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_ACCEL_WOM_Y_THR_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_ACCEL_WOM_Z_THR_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER0_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER1_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER2_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER3_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER4_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER5_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER6_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER7_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER8_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG12_MREG_TOP1, 0); + + if (status) + return -EIO; + + return 0; +} + +static int sensor_init(struct i2c_client *client) +{ + int ret = 0; + u8 device_id = 0, value = 0; + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + device_id = sensor_read_reg(client, ICM4260X_WHO_AM_I); + if (device_id != ICM42607_DEVICE_ID) { + dev_err(&client->dev, "%s: check id err, read_id: %d\n", + __func__, device_id); + return -1; + } + + ret = icm4260x_otp_reload(client); + if (ret) { + dev_err(&client->dev, + "ICM4260X OTP reload error,ret: %d!\n", ret); + return ret; + } + + + ret = icm4260x_set_default_register(client); + if (ret) { + dev_err(&client->dev, + "set ICM4260X default_register error,ret: %d!\n", ret); + return ret; + } + + /* SPI or I2C only + * FIFO count : byte mode, big endian + * sensor data : big endian + */ + value |= BIT_FIFO_COUNT_ENDIAN; + value |= BIT_SENSOR_DATA_ENDIAN; + ret = sensor_write_reg(client, ICM4260X_INTF_CONFIG0, value); + if (ret) + return ret; + + /* configure clock */ + value = BIT_CLK_SEL_PLL | BIT_I3C_SDR_EN | BIT_I3C_DDR_EN; + ret = sensor_write_reg(client, ICM4260X_INTF_CONFIG1, value); + if (ret) + return ret; + + /* INT pin configuration */ + /* + * value = (INT_POLARITY << SHIFT_INT1_POLARITY) | + * (INT_DRIVE_CIRCUIT << SHIFT_INT1_DRIVE_CIRCUIT) | + * (INT_MODE << SHIFT_INT1_MODE); + * ret = sensor_write_reg(client, ICM4260X_INT_CONFIG_REG, value); + * if (ret) + * return ret; + */ + + /* disable sensors */ + ret = sensor_write_reg(client, ICM4260X_PWR_MGMT_0, 0); + if (ret) + return ret; + + /* set Full scale select for accelerometer UI interface output*/ + value = sensor_read_reg(client, ICM4260X_ACCEL_CONFIG0); + value &= ~BIT_ACCEL_FSR; + value |= ACCEL_FS_SEL << SHIFT_ACCEL_FS_SEL; + ret = sensor_write_reg(client, ICM4260X_ACCEL_CONFIG0, value); + if (ret) + return ret; + + /* turn on accelerometer*/ + ret = sensor->ops->active(client, 0, sensor->pdata->poll_delay_ms); + if (ret) { + dev_err(&client->dev, + "%s: fail to active sensor(%d)\n", __func__, ret); + return ret; + } + + return ret; +} + +static int gsensor_report_value(struct i2c_client *client, + struct sensor_axis *axis) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + if (sensor->status_cur == SENSOR_ON) { + /* Report acceleration sensor information */ + input_report_abs(sensor->input_dev, ABS_X, axis->x); + input_report_abs(sensor->input_dev, ABS_Y, axis->y); + input_report_abs(sensor->input_dev, ABS_Z, axis->z); + input_sync(sensor->input_dev); + } + + return 0; +} + +static int sensor_report_value(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + struct sensor_platform_data *pdata = sensor->pdata; + int ret = 0; + short x, y, z; + struct sensor_axis axis; + u8 buffer[6] = {0}; + + if (sensor->ops->read_len < 6) { + dev_err(&client->dev, "%s: length is error, len = %d\n", + __func__, sensor->ops->read_len); + return -EINVAL; + } + + /* Data bytes from hardware xH, xL, yH, yL, zH, zL */ + *buffer = sensor->ops->read_reg; + ret = sensor_rx_data(client, buffer, sensor->ops->read_len); + if (ret < 0) { + dev_err(&client->dev, + "%s: read data failed, ret = %d\n", __func__, ret); + return ret; + } + x = ((buffer[0] << 8) & 0xff00) + (buffer[1] & 0xFF); + y = ((buffer[2] << 8) & 0xff00) + (buffer[3] & 0xFF); + z = ((buffer[4] << 8) & 0xff00) + (buffer[5] & 0xFF); + + //printk("%s,x:%d, y:%d, z:%d\n", __func__, x, y, z); + axis.x = (pdata->orientation[0]) * x + (pdata->orientation[1]) * y + + (pdata->orientation[2]) * z; + axis.y = (pdata->orientation[3]) * x + (pdata->orientation[4]) * y + + (pdata->orientation[5]) * z; + axis.z = (pdata->orientation[6]) * x + (pdata->orientation[7]) * y + + (pdata->orientation[8]) * z; + + gsensor_report_value(client, &axis); + + mutex_lock(&(sensor->data_mutex)); + sensor->axis = axis; + mutex_unlock(&(sensor->data_mutex)); + + return ret; +} + +static struct sensor_operate gsensor_icm4260x_ops = { + .name = "icm4260x_acc", + .type = SENSOR_TYPE_ACCEL, + .id_i2c = ACCEL_ID_ICM4260X, + .read_reg = ICM4260X_ACCEL_DATA_X0, + .read_len = 6, + .id_reg = SENSOR_UNKNOW_DATA, + .id_data = SENSOR_UNKNOW_DATA, + .precision = ICM4260X_PRECISION, + .ctrl_reg = ICM4260X_PWR_MGMT_0, + .int_status_reg = ICM4260X_INT_STATUS, + .range = {-32768, 32768}, + .trig = IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + .active = sensor_active, + .init = sensor_init, + .report = sensor_report_value, +}; + +/****************operate according to sensor chip:end************/ +static int gsensor_icm4260x_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + client->addr = ICM42607_ADDR; + return sensor_register_device(client, NULL, devid, &gsensor_icm4260x_ops); +} + +static void gsensor_icm4260x_remove(struct i2c_client *client) +{ + sensor_unregister_device(client, NULL, &gsensor_icm4260x_ops); +} + +static const struct i2c_device_id gsensor_icm4260x_id[] = { + {"icm42607_acc", ACCEL_ID_ICM4260X}, + {} +}; + +static struct i2c_driver gsensor_icm4260x_driver = { + .probe = gsensor_icm4260x_probe, + .remove = gsensor_icm4260x_remove, + .shutdown = sensor_shutdown, + .id_table = gsensor_icm4260x_id, + .driver = { + .name = "gsensor_icm4260x", +#ifdef CONFIG_PM + .pm = &sensor_pm_ops, +#endif + }, +}; + +module_i2c_driver(gsensor_icm4260x_driver); + +MODULE_AUTHOR("Wangqiang Guo "); +MODULE_DESCRIPTION("icm4260x_acc 3-Axis accelerometer driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/sensors/gyro/Kconfig b/drivers/input/sensors/gyro/Kconfig index 5b27389831ee..58b9b6dc0540 100644 --- a/drivers/input/sensors/gyro/Kconfig +++ b/drivers/input/sensors/gyro/Kconfig @@ -40,6 +40,12 @@ config GYRO_LSM330 config GYRO_ICM2060X tristate "gyroscope icm2060x_gyro" +config GYRO_ICM4260X + tristate "gyroscope icm4260x_gyro" + help + To have support for your specific gyro you will have to + select the proper drivers which depend on this option. + config GYRO_IAM20680 tristate "gyroscope iam20680_gyro" default n diff --git a/drivers/input/sensors/gyro/Makefile b/drivers/input/sensors/gyro/Makefile index 8c0a42734c65..d8c149baabc4 100644 --- a/drivers/input/sensors/gyro/Makefile +++ b/drivers/input/sensors/gyro/Makefile @@ -9,4 +9,5 @@ obj-$(CONFIG_GYRO_MPU6500) += mpu6500_gyro.o obj-$(CONFIG_GYRO_MPU6880) += mpu6880_gyro.o obj-$(CONFIG_GYRO_LSM330) += lsm330_gyro.o obj-$(CONFIG_GYRO_ICM2060X) += icm2060x_gyro.o +obj-$(CONFIG_GYRO_ICM4260X) += icm4260x_gyro.o obj-$(CONFIG_GYRO_IAM20680) += iam20680_gyro.o diff --git a/drivers/input/sensors/gyro/icm4260x_gyro.c b/drivers/input/sensors/gyro/icm4260x_gyro.c new file mode 100644 index 000000000000..e66729d25116 --- /dev/null +++ b/drivers/input/sensors/gyro/icm4260x_gyro.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Rockchip Co.,Ltd. + * Author: Wangqiang Guo + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include + +static int sensor_active(struct i2c_client *client, int enable, int rate) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + int status = 0; + + sensor->ops->ctrl_data = sensor_read_reg(client, sensor->ops->ctrl_reg); + + if (!enable) { + status = (0xff & ~BIT_GYRO_MODE_MASK); + sensor->ops->ctrl_data &= status; + } else { + status = BIT_GYRO_MODE_LNM; + sensor->ops->ctrl_data |= status; + sensor->ops->ctrl_data &= ~BIT_IDLE; + } + + result = sensor_write_reg(client, sensor->ops->ctrl_reg, + sensor->ops->ctrl_data); + if (result) { + dev_err(&client->dev, + "%s: fail to set pwr_mgmt0(%d)\n", __func__, result); + return result; + } + /* Gyroscope needs to be kept ON for a minimum of 45ms */ + usleep_range(45*1000, 45*1010); + + return result; +} + +static int sensor_init(struct i2c_client *client) +{ + int ret = 0; + u8 value; + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + /* + * init on icm42607_acc.c + */ + + /* set Full scale select for accelerometer UI interface output*/ + value = sensor_read_reg(client, ICM4260X_GYRO_CONFIG0); + value &= ~BIT_GYRO_FSR; + value |= GYRO_FS_SEL << SHIFT_GYRO_FS_SEL; + ret = sensor_write_reg(client, ICM4260X_GYRO_CONFIG0, value); + if (ret) + return ret; + + /* turn on accelerometer*/ + ret = sensor->ops->active(client, 0, sensor->pdata->poll_delay_ms); + if (ret) { + dev_err(&client->dev, + "%s: fail to active sensor(%d)\n", __func__, ret); + return ret; + } + + return ret; +} + +static int gyro_report_value(struct i2c_client *client, + struct sensor_axis *axis) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + if (sensor->status_cur == SENSOR_ON) { + /* Report acceleration sensor information */ + input_report_abs(sensor->input_dev, ABS_RX, axis->x); + input_report_abs(sensor->input_dev, ABS_RY, axis->y); + input_report_abs(sensor->input_dev, ABS_RZ, axis->z); + input_sync(sensor->input_dev); + } + + return 0; +} + +static int sensor_report_value(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + struct sensor_platform_data *pdata = sensor->pdata; + int ret = 0; + short x, y, z; + struct sensor_axis axis; + u8 buffer[6] = {0}; + + if (sensor->ops->read_len < 6) { + dev_err(&client->dev, "%s: length is error, len = %d\n", + __func__, sensor->ops->read_len); + return -EINVAL; + } + + *buffer = sensor->ops->read_reg; + ret = sensor_rx_data(client, buffer, sensor->ops->read_len); + if (ret < 0) { + dev_err(&client->dev, + "%s: read data failed, ret = %d\n", __func__, ret); + return ret; + } + + x = ((buffer[0] << 8) & 0xff00) + (buffer[1] & 0xFF); + y = ((buffer[2] << 8) & 0xff00) + (buffer[3] & 0xFF); + z = ((buffer[4] << 8) & 0xff00) + (buffer[5] & 0xFF); + + axis.x = (pdata->orientation[0]) * x + (pdata->orientation[1]) * y + + (pdata->orientation[2]) * z; + axis.y = (pdata->orientation[3]) * x + (pdata->orientation[4]) * y + + (pdata->orientation[5]) * z; + axis.z = (pdata->orientation[6]) * x + (pdata->orientation[7]) * y + + (pdata->orientation[8]) * z; + + gyro_report_value(client, &axis); + + mutex_lock(&(sensor->data_mutex)); + sensor->axis = axis; + mutex_unlock(&(sensor->data_mutex)); + + return ret; +} + +static struct sensor_operate gyro_icm4260x_ops = { + .name = "icm4260x_gyro", + .type = SENSOR_TYPE_GYROSCOPE, + .id_i2c = GYRO_ID_ICM4260X, + .read_reg = ICM4260X_GYRO_DATA_X0, + .read_len = 6, + .id_reg = SENSOR_UNKNOW_DATA, + .id_data = SENSOR_UNKNOW_DATA, + .precision = ICM4260X_PRECISION, + .ctrl_reg = ICM4260X_PWR_MGMT_0, + .int_status_reg = ICM4260X_INT_STATUS, + .range = {-32768, 32768}, + .trig = IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + .active = sensor_active, + .init = sensor_init, + .report = sensor_report_value, +}; + +/****************operate according to sensor chip:end************/ +static int gyro_icm4260x_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + client->addr = ICM42607_ADDR; + return sensor_register_device(client, NULL, devid, &gyro_icm4260x_ops); +} + +static void gyro_icm4260x_remove(struct i2c_client *client) +{ + sensor_unregister_device(client, NULL, &gyro_icm4260x_ops); +} + +static const struct i2c_device_id gyro_icm4260x_id[] = { + {"icm42607_gyro", GYRO_ID_ICM4260X}, + {} +}; + +static struct i2c_driver gyro_icm4260x_driver = { + .probe = gyro_icm4260x_probe, + .remove = gyro_icm4260x_remove, + .shutdown = sensor_shutdown, + .id_table = gyro_icm4260x_id, + .driver = { + .name = "gyro_icm4260x", +#ifdef CONFIG_PM + .pm = &sensor_pm_ops, +#endif + }, +}; + +static int __init gyro_icm4260x_init(void) +{ + return i2c_add_driver(&gyro_icm4260x_driver); +} + +static void __exit gyro_icm4260x_exit(void) +{ + i2c_del_driver(&gyro_icm4260x_driver); +} +/* must register after icm4260x_acc */ +device_initcall_sync(gyro_icm4260x_init); +module_exit(gyro_icm4260x_exit); + +MODULE_AUTHOR("Wangqiang Guo "); +MODULE_DESCRIPTION("icm4260x_gyro 3-Axis accelerometer driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/icm4260x.h b/include/linux/icm4260x.h new file mode 100644 index 000000000000..8917d8bc307e --- /dev/null +++ b/include/linux/icm4260x.h @@ -0,0 +1,432 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Definitions for icm4260x chip. + */ +#ifndef __ICM4260X_ACC_H +#define __ICM4260X_ACC_H + +/* Registers and associated bit definitions */ +/* Bank 0 */ +#define ICM4260X_MISC_1 0x00 +#define ICM4260X_CHIP_CONFIG_REG 0x01 +#define ICM4260X_SIGNAL_PATH_RESET 0x02 +#define ICM4260X_DRIVE_CONFIG_REG1 0x03 +#define ICM4260X_DRIVE_CONFIG_REG2 0x04 +#define ICM4260X_DRIVE_CONFIG_REG3 0x05 +#define ICM4260X_INT_CONFIG_REG 0x06 +#define ICM4260X_ODRGRID0 0x07 +#define ICM4260X_ODRGRID1 0x08 +#define ICM4260X_TEMP_DATA0 0x09 +#define ICM4260X_TEMP_DATA1 0x0a +#define ICM4260X_ACCEL_DATA_X0 0x0b +#define ICM4260X_ACCEL_DATA_X1 0x0c +#define ICM4260X_ACCEL_DATA_Y0 0x0d +#define ICM4260X_ACCEL_DATA_Y1 0x0e +#define ICM4260X_ACCEL_DATA_Z0 0x0f +#define ICM4260X_ACCEL_DATA_Z1 0x10 +#define ICM4260X_GYRO_DATA_X0 0x11 +#define ICM4260X_GYRO_DATA_X1 0x12 +#define ICM4260X_GYRO_DATA_Y0 0x13 +#define ICM4260X_GYRO_DATA_Y1 0x14 +#define ICM4260X_GYRO_DATA_Z0 0x15 +#define ICM4260X_GYRO_DATA_Z1 0x16 +#define ICM4260X_TMST_FSYNC1 0x17 +#define ICM4260X_TMST_FSYNC2 0x18 +#define ICM4260X_ODR_LP_STATUS 0x19 +#define ICM4260X_PWR_MGMT_0 0x1f +#define ICM4260X_GYRO_CONFIG0 0x20 +#define ICM4260X_ACCEL_CONFIG0 0x21 +#define ICM4260X_TEMP_CONFIG0 0x22 +#define ICM4260X_GYRO_CONFIG1 0x23 +#define ICM4260X_ACCEL_CONFIG1 0x24 +#define ICM4260X_APEX_CONFIG0 0x25 +#define ICM4260X_APEX_CONFIG1 0x26 +#define ICM4260X_WOM_CONFIG 0x27 +#define ICM4260X_FIFO_CONFIG1 0x28 +#define ICM4260X_FIFO_CONFIG2 0x29 +#define ICM4260X_FIFO_CONFIG3 0x2a +#define ICM4260X_INT_SOURCE0 0x2b +#define ICM4260X_INT_SOURCE1 0x2c +#define ICM4260X_INT_SOURCE3 0x2d +#define ICM4260X_INT_SOURCE4 0x2e +#define ICM4260X_FIFO_LOST_PKT0 0x2f +#define ICM4260X_FIFO_LOST_PKT1 0x30 +#define ICM4260X_APEX_DATA0 0x31 +#define ICM4260X_APEX_DATA1 0x32 +#define ICM4260X_APEX_DATA2 0x33 +#define ICM4260X_APEX_DATA3 0x34 +#define ICM4260X_INTF_CONFIG0 0x35 +#define ICM4260X_INTF_CONFIG1 0x36 +#define ICM4260X_INT_STATUS_DRDY 0x39 +#define ICM4260X_INT_STATUS 0x3a +#define ICM4260X_INT_STATUS2 0x3b +#define ICM4260X_INT_STATUS3 0x3c +#define ICM4260X_FIFO_BYTE_COUNT1 0x3d +#define ICM4260X_FIFO_BYTE_COUNT2 0x3e +#define ICM4260X_FIFO_DATA_REG 0x3f +#define ICM4260X_S4S_GYRO_TPH1 0x40 +#define ICM4260X_S4S_GYRO_TPH2 0x41 +#define ICM4260X_S4S_ACCEL_TPH1 0x42 +#define ICM4260X_S4S_ACCEL_TPH2 0x43 +#define ICM4260X_S4S_RR 0x44 +#define ICM4260X_GYR_BIAS_CFG1 0x46 +#define ICM4260X_WHO_AM_I 0x75 +#define ICM4260X_S4S_ST 0x76 +#define ICM4260X_S4S_ST_CLONE 0x77 +#define ICM4260X_S4S_DT 0x78 +#define ICM4260X_BLK_SEL_W 0x79 +#define ICM4260X_MADDR_W 0x7a +#define ICM4260X_M_W 0x7b +#define ICM4260X_BLK_SEL_R 0x7c +#define ICM4260X_MADDR_R 0x7d +#define ICM4260X_M_R 0x7e + +/* MREG_TOP1 */ +#define ICM4260X_TMST_CONFIG1_MREG_TOP1 0x00 +#define ICM4260X_FIFO_CONFIG5_MREG_TOP1 0x01 +#define ICM4260X_FIFO_CONFIG6_MREG_TOP1 0x02 +#define ICM4260X_FSYNC_CONFIG_MREG_TOP1 0x03 +#define ICM4260X_INT_CONFIG0_MREG_TOP1 0x04 +#define ICM4260X_INT_CONFIG1_MREG_TOP1 0x05 +#define ICM4260X_AFSR_CONFIG0_MREG_TOP1 0x07 +#define ICM4260X_AFSR_CONFIG1_MREG_TOP1 0x08 +#define ICM4260X_TBC_RCOSC_MREG_TOP1 0x0d +#define ICM4260X_TBC_PLL_MREG_TOP1 0x0e +#define ICM4260X_ST_CONFIG_MREG_TOP1 0x13 +#define ICM4260X_SELFTEST_MREG_TOP1 0x14 +#define ICM4260X_PADS_CONFIG3_MREG_TOP1 0x17 +#define ICM4260X_TEMP_CONFIG1_MREG_TOP1 0x1c +#define ICM4260X_TEMP_CONFIG3_MREG_TOP1 0x1e +#define ICM4260X_S4S_CONFIG1_MREG_TOP1 0x1f +#define ICM4260X_S4S_CONFIG2_MREG_TOP1 0x20 +#define ICM4260X_S4S_FREQ_RATIO1_MREG_TOP1 0x21 +#define ICM4260X_S4S_FREQ_RATIO2_MREG_TOP1 0x22 +#define ICM4260X_INTF_CONFIG6_MREG_TOP1 0x23 +#define ICM4260X_INTF_CONFIG10_MREG_TOP1 0x25 +#define ICM4260X_INTF_CONFIG7_MREG_TOP1 0x28 +#define ICM4260X_OTP_CONFIG_MREG_TOP1 0x2b +#define ICM4260X_INT_SOURCE6_MREG_TOP1 0x2f +#define ICM4260X_INT_SOURCE7_MREG_TOP1 0x30 +#define ICM4260X_INT_SOURCE8_MREG_TOP1 0x31 +#define ICM4260X_INT_SOURCE9_MREG_TOP1 0x32 +#define ICM4260X_INT_SOURCE10_MREG_TOP1 0x33 +#define ICM4260X_GYRO_PWR_CFG0_MREG_TOP1 0x38 +#define ICM4260X_ACCEL_CP_CFG0_MREG_TOP1 0x39 +#define ICM4260X_APEX_CONFIG2_MREG_TOP1 0x44 +#define ICM4260X_APEX_CONFIG3_MREG_TOP1 0x45 +#define ICM4260X_APEX_CONFIG4_MREG_TOP1 0x46 +#define ICM4260X_APEX_CONFIG5_MREG_TOP1 0x47 +#define ICM4260X_APEX_CONFIG9_MREG_TOP1 0x48 +#define ICM4260X_APEX_CONFIG10_MREG_TOP1 0x49 +#define ICM4260X_APEX_CONFIG11_MREG_TOP1 0x4a +#define ICM4260X_APEX_CONFIG12_MREG_TOP1 0x67 +#define ICM4260X_ACCEL_WOM_X_THR_MREG_TOP1 0x4b +#define ICM4260X_ACCEL_WOM_Y_THR_MREG_TOP1 0x4c +#define ICM4260X_ACCEL_WOM_Z_THR_MREG_TOP1 0x4d +#define ICM4260X_GOS_USER0_MREG_TOP1 0x4e +#define ICM4260X_GOS_USER1_MREG_TOP1 0x4f +#define ICM4260X_GOS_USER2_MREG_TOP1 0x50 +#define ICM4260X_GOS_USER3_MREG_TOP1 0x51 +#define ICM4260X_GOS_USER4_MREG_TOP1 0x52 +#define ICM4260X_GOS_USER5_MREG_TOP1 0x53 +#define ICM4260X_GOS_USER6_MREG_TOP1 0x54 +#define ICM4260X_GOS_USER7_MREG_TOP1 0x55 +#define ICM4260X_GOS_USER8_MREG_TOP1 0x56 +#define ICM4260X_ST_STATUS1_MREG_TOP1 0x63 +#define ICM4260X_ST_STATUS2_MREG_TOP1 0x64 + +/* MMEM_TOP */ +#define ICM4260X_XA_ST_DATA_MMEM_TOP 0x5000 +#define ICM4260X_YA_ST_DATA_MMEM_TOP 0x5001 +#define ICM4260X_ZA_ST_DATA_MMEM_TOP 0x5002 +#define ICM4260X_XG_ST_DATA_MMEM_TOP 0x5003 +#define ICM4260X_YG_ST_DATA_MMEM_TOP 0x5004 +#define ICM4260X_ZG_ST_DATA_MMEM_TOP 0x5005 + +/* MREG_OTP */ +#define ICM4260X_OTP_CTRL7_MREG_OTP 0x2806 + +/* Bank0 REG_GYRO_CONFIG0/REG_ACCEL_CONFIG0 */ +#define SHIFT_GYRO_FS_SEL 5 +#define SHIFT_ACCEL_FS_SEL 5 +#define SHIFT_ODR_CONF 0 +#define BIT_GYRO_FSR 0x60 +#define BIT_GYRO_ODR 0x0F +#define BIT_ACCEL_FSR 0x60 +#define ACCEL_FS_SEL 3 //(-2G, +2G) +#define GYRO_FS_SEL 3 //00~11:2000dps\1000dps\500dps\250dps +#define BIT_ACCEL_ODR 0x0F +#define BIT_SENSOR_ODR_800HZ 0x06 +#define BIT_SENSOR_ODR_400HZ 0x07 +#define BIT_SENSOR_ODR_200HZ 0x08 +#define BIT_SENSOR_ODR_100HZ 0x09 +#define BIT_SENSOR_ODR_50HZ 0x0A +#define BIT_SENSOR_ODR_25HZ 0x0B +#define BIT_SENSOR_ODR_12HZ 0x0C +#define BIT_SENSOR_ODR_6HZ 0x0D +#define BIT_SENSOR_ODR_3HZ 0x0E + +/* Bank0 REG_GYRO_CONFIG1 */ +#define BIT_GYR_UI_FLT_BW_BYPASS 0x00 +#define BIT_GYR_UI_FLT_BW_180HZ 0x01 +#define BIT_GYR_UI_FLT_BW_121HZ 0x02 +#define BIT_GYR_UI_FLT_BW_73HZ 0x03 +#define BIT_GYR_UI_FLT_BW_53HZ 0x04 +#define BIT_GYR_UI_FLT_BW_34HZ 0x05 +#define BIT_GYR_UI_FLT_BW_25HZ 0x06 +#define BIT_GYR_UI_FLT_BW_16HZ 0x07 +#define BIT_GYR_UI_AVG_IND_2X 0x00 +#define BIT_GYR_UI_AVG_IND_4X 0x10 +#define BIT_GYR_UI_AVG_IND_8X 0x20 +#define BIT_GYR_UI_AVG_IND_16X 0x30 +#define BIT_GYR_UI_AVG_IND_32X 0x40 +#define BIT_GYR_UI_AVG_IND_64X 0x50 + +/* Bank0 REG_ACCEL_CONFIG1 */ +#define BIT_ACC_FILT_BW_IND_BYPASS 0x00 +#define BIT_ACC_FILT_BW_IND_180HZ 0x01 +#define BIT_ACC_FILT_BW_IND_121HZ 0x02 +#define BIT_ACC_FILT_BW_IND_73HZ 0x03 +#define BIT_ACC_FILT_BW_IND_53HZ 0x04 +#define BIT_ACC_FILT_BW_IND_34HZ 0x05 +#define BIT_ACC_FILT_BW_IND_25HZ 0x06 +#define BIT_ACC_FILT_BW_IND_16HZ 0x07 +#define BIT_ACC_UI_AVG_IND_2X 0x00 +#define BIT_ACC_UI_AVG_IND_4X 0x10 +#define BIT_ACC_UI_AVG_IND_8X 0x20 +#define BIT_ACC_UI_AVG_IND_16X 0x30 +#define BIT_ACC_UI_AVG_IND_32X 0x40 +#define BIT_ACC_UI_AVG_IND_64X 0x50 + +/* Bank0 REG_INT_CONFIG_REG */ +#define SHIFT_INT1_MODE 0x02 +#define SHIFT_INT1_DRIVE_CIRCUIT 0x01 +#define SHIFT_INT1_POLARITY 0x00 + +/* Bank0 REG_PWR_MGMT_0 */ +#define BIT_ACCEL_MODE_OFF 0x00 +#define BIT_ACCEL_MODE_LPM 0x02 +#define BIT_ACCEL_MODE_LNM 0x03 +#define BIT_ACCEL_MODE_MASK 0x03 +#define BIT_GYRO_MODE_OFF 0x00 +#define BIT_GYRO_MODE_STBY 0x04 +#define BIT_GYRO_MODE_LPM 0x08 +#define BIT_GYRO_MODE_LNM 0x0c +#define BIT_GYRO_MODE_MASK 0x0c +#define BIT_IDLE 0x10 +#define BIT_ACCEL_LP_CLK_SEL 0x80 + +/* Bank0 REG_SIGNAL_PATH_RESET */ +#define BIT_FIFO_FLUSH 0x04 +#define BIT_SOFT_RESET_CHIP_CONFIG 0x10 + +/* Bank0 REG_INTF_CONFIG0 */ +#define BIT_SIFS_CFG_I2C_ONLY 0x02 +#define BIT_SIFS_CFG_SPI_ONLY 0x03 +#define BIT_SENSOR_DATA_ENDIAN 0x10 +#define BIT_FIFO_COUNT_ENDIAN 0x20 +#define BIT_FIFO_COUNT_FORMAT 0x40 +#define BIT_FIFO_SREG_INVALID_IND_DIS 0x80 + +/* Bank0 REG_INTF_CONFIG1 */ +#define BIT_CLK_SEL_RC 0x00 +#define BIT_CLK_SEL_PLL 0x01 +#define BIT_CLK_SEL_DIS 0x03 +#define BIT_I3C_DDR_EN 0x04 +#define BIT_I3C_SDR_EN 0x08 +#define BIT_GYRO_AFSR_MODE_LFS 0x00 +#define BIT_GYRO_AFSR_MODE_HFS 0x20 +#define BIT_GYRO_AFSR_MODE_DYN 0x40 + +/* Bank0 REG_FIFO_CONFIG1 */ +#define BIT_FIFO_MODE_NO_BYPASS 0x00 +#define BIT_FIFO_MODE_BYPASS 0x01 +#define BIT_FIFO_MODE_STREAM 0x00 +#define BIT_FIFO_MODE_STOPFULL 0x02 + +/* Bank 0 REG_INT_SOURCE0 */ +#define BIT_INT_AGC_RDY_INT1_EN 0x01 +#define BIT_INT_FIFO_FULL_INT1_EN 0x02 +#define BIT_INT_FIFO_THS_INT1_EN 0x04 +#define BIT_INT_DRDY_INT_EN 0x08 +#define BIT_INT_RESET_DONE_INT1_EN 0x10 +#define BIT_INT_PLL_RDY_INT1_EN 0x20 +#define BIT_INT_FSYNC_INT1_EN 0x40 +#define BIT_INT_ST_DONE_INT1_EN 0x80 + +/* Bank 0 REG_INT_SOURCE1 */ +#define BIT_INT_WOM_X_INT1_EN 0x01 +#define BIT_INT_WOM_Y_INT1_EN 0x02 +#define BIT_INT_WOM_Z_INT1_EN 0x04 +#define BIT_INT_WOM_XYZ_INT1_EN (BIT_INT_WOM_X_INT1_EN | \ + BIT_INT_WOM_Y_INT1_EN | BIT_INT_WOM_Z_INT1_EN) +#define BIT_INT_SMD_INT1_EN 0x08 +#define BIT_INT_I3C_PROTCL_ERR_INT1_EN 0x40 + +/* Bank0 REG_INT_STATUS_DRDY */ +#define BIT_INT_STATUS_DRDY 0x01 + +/* Bank0 REG_INT_STATUS */ +#define BIT_INT_STATUS_AGC_RDY 0x01 +#define BIT_INT_STATUS_FIFO_FULL 0x02 +#define BIT_INT_STATUS_FIFO_THS 0x04 +#define BIT_INT_STATUS_RESET_DONE 0x10 +#define BIT_INT_STATUS_PLL_RDY 0x20 +#define BIT_INT_STATUS_FSYNC 0x40 +#define BIT_INT_STATUS_ST_DONE 0x80 + +/* Bank0 REG_INT_STATUS2 */ +#define BIT_INT_STATUS_WOM_Z 0x01 +#define BIT_INT_STATUS_WOM_Y 0x02 +#define BIT_INT_STATUS_WOM_X 0x04 +#define BIT_INT_STATUS_WOM_XYZ (BIT_INT_STATUS_WOM_X | \ + BIT_INT_STATUS_WOM_Y | BIT_INT_STATUS_WOM_Z) +#define BIT_INT_STATUS_SMD 0x08 + +/* Bank 0 REG_INT_STATUS3 */ +#define BIT_INT_STATUS_LOWG_DET 0x02 +#define BIT_INT_STATUS_FF_DET 0x04 +#define BIT_INT_STATUS_TILT_DET 0x08 +#define BIT_INT_STATUS_STEP_CNT_OVFL 0x10 +#define BIT_INT_STATUS_STEP_DET 0x20 + +/* Bank0 REG_WOM_CONFIG */ +#define BIT_WOM_EN_OFF 0x00 +#define BIT_WOM_EN_ON 0x01 +#define BIT_WOM_MODE_INITIAL 0x00 +#define BIT_WOM_MODE_PREV 0x02 +#define BIT_WOM_INT_MODE_OR 0x00 +#define BIT_WOM_INT_MODE_AND 0x04 +#define BIT_WOM_INT_DUR_LEGACY 0x00 +#define BIT_WOM_INT_DUR_2ND 0x08 +#define BIT_WOM_INT_DUR_3RD 0x10 +#define BIT_WOM_INT_DUR_4TH 0x18 + +/* Bank0 REG_APEX_CONFIG0 */ +#define BIT_DMP_SRAM_RESET_APEX 0x01 +#define BIT_DMP_INIT_EN 0x04 +#define BIT_DMP_POWER_SAVE_EN 0x08 + +/* Bank0 REG_APEX_CONFIG1 */ +#define BIT_DMP_ODR_25HZ 0x00 +#define BIT_DMP_ODR_50HZ 0x02 +#define BIT_DMP_ODR_100HZ 0x03 +#define BIT_DMP_PEDO_EN 0x08 +#define BIT_DMP_TILT_EN 0x10 +#define BIT_DMP_FF_EN 0x20 +#define BIT_DMP_SMD_EN 0x40 + +/* REG_OTP_CONFIG_MREG_TOP1 */ +#define BIT_OTP_COPY_NORMAL 0x04 +#define BIT_OTP_COPY_ST_DATA 0x0C +#define OTP_COPY_MODE_MASK 0x0C + +/* REG_INT_SOURCE6_MREG_TOP1 */ +#define BIT_INT_TLT_DET_INT1_EN 0x08 +#define BIT_INT_STEP_CNT_OVFL_INT1_EN 0x10 +#define BIT_INT_STEP_DET_INT1_EN 0x20 +#define BIT_INT_LOWG_INT1_EN 0x40 +#define BIT_INT_FF_INT1_EN 0x80 + +/* REG_TMST_CONFIG1_MREG_TOP1 */ +#define BIT_TMST_EN 0x01 +#define BIT_TMST_FSYNC_EN 0x02 +#define BIT_TMST_DELTA_EN 0x04 +#define BIT_TMST_RESOL 0x08 +#define BIT_TMST_ON_SREG_EN 0x10 +#define BIT_ODR_EN_WITHOUT_SENSOR 0x40 + +/* REG_FIFO_CONFIG5_MREG_TOP1 */ +#define BIT_FIFO_ACCEL_EN 0x01 +#define BIT_FIFO_GYRO_EN 0x02 +#define BIT_FIFO_TMST_FSYNC_EN 0x04 +#define BIT_FIFO_HIRES_EN 0x08 +#define BIT_RESUME_PARTIAL_RD 0x10 +#define BIT_WM_GT_TH 0x20 + +/* REG_SELFTEST_MREG_TOP1 */ +#define BIT_EN_AX_ST 0x01 +#define BIT_EN_AY_ST 0x02 +#define BIT_EN_AZ_ST 0x04 +#define BIT_EN_GX_ST 0x08 +#define BIT_EN_GY_ST 0x10 +#define BIT_EN_GZ_ST 0x20 +#define BIT_ACCEL_ST_EN 0x40 +#define BIT_GYRO_ST_EN 0x80 + +/* REG_ST_CONFIG_MREG_TOP1 */ +#define BIT_PD_ACCEL_CP45_ST_REG 0x80 +#define SHIFT_GYRO_ST_LIM 0 +#define SHIFT_ACCEL_ST_LIM 3 +#define SHIFT_ST_NUM_SAMPLE 6 + +/* REG_ST_STATUS1_MREG_TOP1 */ +#define BIT_DMP_AX_ST_PASS 0x02 +#define BIT_DMP_AY_ST_PASS 0x04 +#define BIT_DMP_AZ_ST_PASS 0x08 +#define BIT_DMP_ACCEL_ST_DONE 0x10 +#define BIT_DMP_ACCEL_ST_PASS 0x20 + +/* REG_ST_STATUS2_MREG_TOP1 */ +#define BIT_DMP_GX_ST_PASS 0x02 +#define BIT_DMP_GY_ST_PASS 0x04 +#define BIT_DMP_GZ_ST_PASS 0x08 +#define BIT_DMP_GYRO_ST_DONE 0x10 +#define BIT_DMP_GYRO_ST_PASS 0x20 +#define BIT_DMP_ST_INCOMPLETE 0x40 + +/* REG_OTP_CTRL7_MREG_OTP */ +#define BIT_OTP_RELOAD 0x08 +#define BIT_OTP_PWR_DOWN 0x02 + + +/* fifo data packet header */ +#define BIT_FIFO_HEAD_MSG 0x80 +#define BIT_FIFO_HEAD_ACCEL 0x40 +#define BIT_FIFO_HEAD_GYRO 0x20 +#define BIT_FIFO_HEAD_20 0x10 +#define BIT_FIFO_HEAD_TMSP_ODR 0x08 +#define BIT_FIFO_HEAD_TMSP_NO_ODR 0x04 +#define BIT_FIFO_HEAD_TMSP_FSYNC 0x0C +#define BIT_FIFO_HEAD_ODR_ACCEL 0x02 +#define BIT_FIFO_HEAD_ODR_GYRO 0x01 + +/* data definitions */ +#define FIFO_PACKET_BYTE_SINGLE 8 +#define FIFO_PACKET_BYTE_6X 16 +#define FIFO_PACKET_BYTE_HIRES 20 +#define FIFO_COUNT_BYTE 2 + +/* sensor startup time */ +#define INV_ICM43600_GYRO_START_TIME 100 +#define INV_ICM43600_ACCEL_START_TIME 100 + +/* sensor stop time */ +#define INV_ICM43600_GYRO_STOP_TIME 20 + +/* M-reg access wait tile */ +#define INV_ICM42607_MCLK_WAIT_US 20 +#define INV_ICM42607_BLK_SEL_WAIT_US 10 +#define INV_ICM42607_MADDR_WAIT_US 10 +#define INV_ICM42607_M_RW_WAIT_US 10 + +/* temperature sensor */ +#define TEMP_SCALE 100 /* scale by 100 */ +#define TEMP_LSB_PER_DEG 2 /* 2LSB=1degC */ +#define TEMP_OFFSET 25 /* 25 degC */ + +/* + * INT configurations + * Polarity: 0 -> Active Low, 1 -> Active High + * Drive circuit: 0 -> Open Drain, 1 -> Push-Pull + * Mode: 0 -> Pulse, 1 -> Latch + */ +#define INT_POLARITY 1 +#define INT_DRIVE_CIRCUIT 1 +#define INT_MODE 0 + +#define ICM42607_DEVICE_ID 0x61 +#define ICM4260X_PRECISION 16 +#define ICM42607_ADDR 0x68 + +#endif diff --git a/include/linux/sensor-dev.h b/include/linux/sensor-dev.h index 81d3aa1ea38b..a29d512ec0d1 100644 --- a/include/linux/sensor-dev.h +++ b/include/linux/sensor-dev.h @@ -70,6 +70,7 @@ enum sensor_id { ACCEL_ID_DA215S, ACCEL_ID_DA228E, ACCEL_ID_IAM20680, + ACCEL_ID_ICM4260X, COMPASS_ID_ALL, COMPASS_ID_AK8975, COMPASS_ID_AK8963, @@ -97,6 +98,7 @@ enum sensor_id { GYRO_ID_LSM330, GYRO_ID_ICM2060X, GYRO_ID_IAM20680, + GYRO_ID_ICM4260X, LIGHT_ID_ALL, LIGHT_ID_CM3217, LIGHT_ID_CM3218, From 813ac004fbf74435248c0a05ce1d7e4beb8e023a Mon Sep 17 00:00:00 2001 From: Wang Xiaobin Date: Mon, 9 Oct 2023 10:05:54 +0800 Subject: [PATCH 49/55] media: i2c: add cam-tb-setup Thunder boot related camera parameters. Signed-off-by: Wang Xiaobin Change-Id: I221e22041677325842a3d192c232fbff9d1bc992 --- drivers/media/i2c/Makefile | 1 + drivers/media/i2c/cam-tb-setup.c | 97 ++++++++++++++++++++++++++++++++ drivers/media/i2c/cam-tb-setup.h | 33 +++++++++++ 3 files changed, 131 insertions(+) create mode 100644 drivers/media/i2c/cam-tb-setup.c create mode 100644 drivers/media/i2c/cam-tb-setup.h diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 9099ca01f9c3..dee3639506e8 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -190,6 +190,7 @@ obj-$(CONFIG_VIDEO_RDACM21) += rdacm21.o obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_VIDEO_RK628) += rk628/ obj-$(CONFIG_VIDEO_RK_IRCUT) += rk_ircut.o +obj-$(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) += cam-tb-setup.o obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ obj-$(CONFIG_VIDEO_S5K3L6XX) += s5k3l6xx.o obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o diff --git a/drivers/media/i2c/cam-tb-setup.c b/drivers/media/i2c/cam-tb-setup.c new file mode 100644 index 000000000000..e300dc62731c --- /dev/null +++ b/drivers/media/i2c/cam-tb-setup.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2023 Rockchip Electronics Co., Ltd + +#include +#include +#include +#include "cam-tb-setup.h" + +static u32 rk_cam_w; +static u32 rk_cam_h; +static u32 rk_cam_hdr; +static u32 rk_cam_fps; + +static int __init rk_cam_w_setup(char *str) +{ + int ret = 0; + unsigned long val = 0; + + ret = kstrtoul(str, 0, &val); + if (!ret) + rk_cam_w = (u32)val; + else + pr_err("get rk_cam_w fail\n"); + + return 0; +} + +u32 get_rk_cam_w(void) +{ + return rk_cam_w; +} +EXPORT_SYMBOL(get_rk_cam_w); + +static int __init rk_cam_h_setup(char *str) +{ + int ret = 0; + unsigned long val = 0; + + ret = kstrtoul(str, 0, &val); + if (!ret) + rk_cam_h = (u32)val; + else + pr_err("get rk_cam_h fail\n"); + + return 0; +} + +u32 get_rk_cam_h(void) +{ + return rk_cam_h; +} +EXPORT_SYMBOL(get_rk_cam_h); + +static int __init rk_cam_hdr_setup(char *str) +{ + int ret = 0; + unsigned long val = 0; + + ret = kstrtoul(str, 0, &val); + if (!ret) + rk_cam_hdr = (u32)val; + else + pr_err("get rk_cam_hdr fail\n"); + + return 0; +} + +u32 get_rk_cam_hdr(void) +{ + return rk_cam_hdr; +} +EXPORT_SYMBOL(get_rk_cam_hdr); + +static int __init __maybe_unused rk_cam_fps_setup(char *str) +{ + int ret = 0; + unsigned long val = 0; + + ret = kstrtoul(str, 0, &val); + if (!ret) + rk_cam_fps = (u32)val; + else + pr_err("get rk_cam_fps fail\n"); + + return 0; +} + +u32 get_rk_cam_fps(void) +{ + return rk_cam_fps; +} +EXPORT_SYMBOL(get_rk_cam_fps); + +__setup("rk_cam_w=", rk_cam_w_setup); +__setup("rk_cam_h=", rk_cam_h_setup); +__setup("rk_cam_hdr=", rk_cam_hdr_setup); +__setup("rk_cam_fps=", rk_cam_fps_setup); diff --git a/drivers/media/i2c/cam-tb-setup.h b/drivers/media/i2c/cam-tb-setup.h new file mode 100644 index 000000000000..61afc32c20b7 --- /dev/null +++ b/drivers/media/i2c/cam-tb-setup.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023 Rockchip Electronics Co., Ltd. */ + +#ifndef CAM_TB_SETUP_H +#define CAM_TB_SETUP_H + +#include + +#ifdef CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP +u32 get_rk_cam_w(void); +u32 get_rk_cam_h(void); +u32 get_rk_cam_hdr(void); +u32 get_rk_cam_fps(void); +#else +static inline u32 get_rk_cam_w(void) +{ + return 0; +} +static inline u32 get_rk_cam_h(void) +{ + return 0; +} +static inline u32 get_rk_cam_hdr(void) +{ + return 0; +} +static inline u32 get_rk_cam_fps(void) +{ + return 0; +} +#endif + +#endif From 28a6652322c4f3aaa16bc4c64b6d6a17775e5f1e Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 10 Oct 2023 08:46:01 +0800 Subject: [PATCH 50/55] PCI: rockchip: dw: remove .link_up() hook from struct dw_pcie_ops [2.353901] rk-pcie 3c0000000.pcie Link up. LTSSM is 0x1 [2.354036] rk-pcie 3c0000000.pcie: PCI host bridge to bus 0000 :00 [2.354058] pci_bus 0000:00: root bus resource [bus 00-0f] [2.354074] pci_bus 0000:00: root bus resource [??? 0x4000000-0xf40fffff flags 0x0] ... The original link event is checking LTSSM and ensure it's in L0. However enabling ASPM will make accessing config space failed. So commit 824c99261a9a ("PCI: rockchip: dw: Update link up check state") remove the LTSSM check. But it introduce a situation that if link still in training and host bridge tries to enumerate slot, it will fail unexpectedly. Fix this by removing rk_pcie_link_up and let dwc core use its own port logic to decide the link state. Signed-off-by: Shawn Lin Change-Id: I41f05a8aa89ac33782d569ffa7d466cf95981c68 --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 4e3c6d1c89fc..4b360402c711 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -658,18 +658,6 @@ static inline void rk_pcie_enable_ltssm(struct rk_pcie *rk_pcie) rk_pcie_writel_apb(rk_pcie, 0x0, 0xC000C); } -static int rk_pcie_link_up(struct dw_pcie *pci) -{ - struct rk_pcie *rk_pcie = to_rk_pcie(pci); - u32 val; - - val = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS); - if ((val & (RDLH_LINKUP | SMLH_LINKUP)) == 0x30000) - return 1; - - return 0; -} - static void rk_pcie_enable_debug(struct rk_pcie *rk_pcie) { if (!IS_ENABLED(CONFIG_DEBUG_FS)) @@ -1489,7 +1477,6 @@ MODULE_DEVICE_TABLE(of, rk_pcie_of_match); static const struct dw_pcie_ops dw_pcie_ops = { .start_link = rk_pcie_establish_link, - .link_up = rk_pcie_link_up, }; static void rk_pcie_fast_link_setup(struct rk_pcie *rk_pcie) From 0b926bd8ddffa804be646bc27ac2f416a92052a1 Mon Sep 17 00:00:00 2001 From: Huibin Hong Date: Wed, 11 Oct 2023 09:08:31 +0000 Subject: [PATCH 51/55] fiq_debugger: set uart irq no balancing When IRQ BALANCING is enable, the log below is show: fiq_debugger:cpu 0 not responding,reverting to cpu 6 Signed-off-by: Huibin Hong Change-Id: Ic5a1786ecb72dc4b28e9b9fa8428065e111e55ee --- drivers/soc/rockchip/fiq_debugger/fiq_debugger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c b/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c index 1f8e88e73acc..77850ffc7009 100644 --- a/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c +++ b/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c @@ -1556,7 +1556,7 @@ static int fiq_debugger_probe(struct platform_device *pdev) pr_err("%s: could not install nmi irq handler\n", __func__); irq_clear_status_flags(state->uart_irq, IRQ_NOAUTOEN); ret = request_irq(state->uart_irq, fiq_debugger_uart_irq, - IRQF_NO_SUSPEND, "debug", state); + IRQF_NO_SUSPEND | IRQF_NOBALANCING, "debug", state); } else { enable_nmi(state->uart_irq); } From 3b4cfe2daed105062e199dd261db090d401a345a Mon Sep 17 00:00:00 2001 From: Rimon Xu Date: Sun, 8 Oct 2023 18:00:53 +0800 Subject: [PATCH 52/55] video: rockchip: vtunnel: fix compile errors on android 14 Change-Id: If3ef85ec97616507a337fa8a898155b80397f9f2 Signed-off-by: Rimon Xu --- drivers/video/rockchip/vtunnel/rkvtunnel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/rockchip/vtunnel/rkvtunnel.c b/drivers/video/rockchip/vtunnel/rkvtunnel.c index 238a6516fc32..a2ce451b7cda 100644 --- a/drivers/video/rockchip/vtunnel/rkvtunnel.c +++ b/drivers/video/rockchip/vtunnel/rkvtunnel.c @@ -731,7 +731,7 @@ rkvt_reset_proc(struct rkvt_ctrl_data *data, struct rkvt_session *session) { struct rkvt_dev *vt_dev = session->vt_dev; struct rkvt_instance *inst; - long read_buf_id; + long long read_buf_id; inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); if (!inst) @@ -1191,7 +1191,7 @@ rkvt_release_buf(struct rkvt_buf_data *data, struct rkvt_session *session) struct rkvt_buffer *buffer = NULL; int i; int ret = 0; - long read_buf_id; + long long read_buf_id; inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); if (!inst) From 206d4855f156e7fc828842d7239f35325172c646 Mon Sep 17 00:00:00 2001 From: Wangqiang Guo Date: Wed, 11 Oct 2023 06:15:56 +0000 Subject: [PATCH 53/55] arm64: rockchip_defconfig: enable icm4260x sensor. support RK3588-EVB7 Change-Id: I1899ae8f1bc8b102a68bd7b00ca9d698760f8f19 Signed-off-by: Wangqiang Guo --- arch/arm64/configs/rockchip_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/configs/rockchip_defconfig b/arch/arm64/configs/rockchip_defconfig index e411b933e58c..b19d854fc7da 100644 --- a/arch/arm64/configs/rockchip_defconfig +++ b/arch/arm64/configs/rockchip_defconfig @@ -475,6 +475,7 @@ CONFIG_GS_LSM303D=y CONFIG_LSM330_ACC=y CONFIG_BMA2XX_ACC=y CONFIG_GS_DA223=y +CONFIG_ICM4260X_ACC=y CONFIG_COMPASS_DEVICE=y CONFIG_COMPASS_AK8975=y CONFIG_COMPASS_AK8963=y @@ -485,6 +486,7 @@ CONFIG_GYRO_EWTSA=y CONFIG_GYRO_MPU6500=y CONFIG_GYRO_MPU6880=y CONFIG_GYRO_LSM330=y +CONFIG_GYRO_ICM4260X=y CONFIG_LIGHT_DEVICE=y CONFIG_LS_CM3217=y CONFIG_LS_CM3218=y From 89b28f5bba3ad712e3d8be0e7d88be7a8717abce Mon Sep 17 00:00:00 2001 From: Wangqiang Guo Date: Wed, 11 Oct 2023 06:17:35 +0000 Subject: [PATCH 54/55] arm64: rockchip_gki.config: enable icm4260x sensor. support RK3588-EVB7 Change-Id: I1b365c1bf344a1cd106ae6ba55c1a177cb759861 Signed-off-by: Wangqiang Guo --- arch/arm64/configs/rockchip_gki.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/configs/rockchip_gki.config b/arch/arm64/configs/rockchip_gki.config index 160bfa284ae9..d1dd1e15dcc7 100644 --- a/arch/arm64/configs/rockchip_gki.config +++ b/arch/arm64/configs/rockchip_gki.config @@ -85,6 +85,7 @@ CONFIG_GS_SC7A20=m CONFIG_GS_SC7A30=m CONFIG_GYROSCOPE_DEVICE=m CONFIG_GYRO_EWTSA=m +CONFIG_GYRO_ICM4260X=m CONFIG_GYRO_L3G20D=m CONFIG_GYRO_L3G4200D=m CONFIG_GYRO_LSM330=m @@ -139,6 +140,7 @@ CONFIG_I2C_CHARDEV=m CONFIG_I2C_GPIO=m CONFIG_I2C_HID=m CONFIG_I2C_RK3X=m +CONFIG_ICM4260X_ACC=m CONFIG_IEP=m CONFIG_IIO_BUFFER_CB=m CONFIG_INPUT_RK805_PWRKEY=m From 372c139deef48d827f8fe6ca47cb8210b28914a4 Mon Sep 17 00:00:00 2001 From: Johnson Ding Date: Wed, 11 Oct 2023 09:04:00 +0800 Subject: [PATCH 55/55] video: rockchip: mpp: fix JPEG Enc dma sync problem Signed-off-by: Johnson Ding Change-Id: I5947652b7291cd997ee4555c89942a0bd7af53c1 --- drivers/video/rockchip/mpp/mpp_rkvenc2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/rockchip/mpp/mpp_rkvenc2.c b/drivers/video/rockchip/mpp/mpp_rkvenc2.c index 5ea47d99163e..fc55395dafc0 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvenc2.c +++ b/drivers/video/rockchip/mpp/mpp_rkvenc2.c @@ -1469,7 +1469,7 @@ static int rkvenc_finish(struct mpp_dev *mpp, struct mpp_task *mpp_task) if (task->bs_buf) { u32 bs_size = mpp_read(mpp, 0x4064); - mpp_dma_buf_sync(task->bs_buf, 0, bs_size / 8 + task->offset_bs, + mpp_dma_buf_sync(task->bs_buf, 0, bs_size + task->offset_bs, DMA_FROM_DEVICE, true); }