From 41c01fb27bcf5c725753d1ecd697d191405afb93 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Sat, 16 Aug 2025 15:55:07 +0800 Subject: [PATCH] media: i2c: rk628: add csi output yuv422 10bit support Signed-off-by: Jianwei Fan Change-Id: Ibdbb2d39287b13115babb36e6643c0a0c60eadeb --- drivers/media/i2c/rk628/rk628.h | 1 + drivers/media/i2c/rk628/rk628_csi.h | 14 +++ drivers/media/i2c/rk628/rk628_csi_v4l2.c | 105 ++++++++++++++++++----- drivers/media/i2c/rk628/rk628_hdmirx.c | 1 + drivers/media/i2c/rk628/rk628_hdmirx.h | 5 +- 5 files changed, 103 insertions(+), 23 deletions(-) diff --git a/drivers/media/i2c/rk628/rk628.h b/drivers/media/i2c/rk628/rk628.h index f8312b71a132..cc2e08b718c4 100644 --- a/drivers/media/i2c/rk628/rk628.h +++ b/drivers/media/i2c/rk628/rk628.h @@ -314,6 +314,7 @@ struct rk628 { struct gpio_desc *hdmirx_det_gpio; bool last_mipi_status; bool is_suspend; + bool is_10bit; }; #define rk628_dbg(rk628, format, ...) \ diff --git a/drivers/media/i2c/rk628/rk628_csi.h b/drivers/media/i2c/rk628/rk628_csi.h index 62438abaff38..28a3f7d34c41 100644 --- a/drivers/media/i2c/rk628/rk628_csi.h +++ b/drivers/media/i2c/rk628/rk628_csi.h @@ -115,4 +115,18 @@ #define CSI_SKIP_FRAME_NORMAL 1 +enum csi_pixfmt { + CSI_RAW8 = 0, + CSI_RAW10, + CSI_PIXEL10, + CSI_PIXEL128, + CSI_RAW12, + CSI_PIXEL12, +}; + +enum csi_datatype { + YUV422_8BIT = 0x1e, + YUV422_10BIT = 0x1f, +}; + #endif diff --git a/drivers/media/i2c/rk628/rk628_csi_v4l2.c b/drivers/media/i2c/rk628/rk628_csi_v4l2.c index a10a381bc69c..56ff4868500b 100644 --- a/drivers/media/i2c/rk628/rk628_csi_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_csi_v4l2.c @@ -152,8 +152,10 @@ struct rk628_edid { }; static const s64 link_freq_menu_items[] = { - RK628_CSI_LINK_FREQ_LOW, - RK628_CSI_LINK_FREQ_HIGH, + RK628_CSI_LINK_FREQ_350M, + RK628_CSI_LINK_FREQ_450M, + RK628_CSI_LINK_FREQ_650M, + RK628_CSI_LINK_FREQ_750M, RK628_CSI_LINK_FREQ_925M, }; @@ -484,11 +486,19 @@ static int rk628_csi_get_detected_timings(struct v4l2_subdev *sd, struct rk628_csi *csi = to_csi(sd); struct v4l2_bt_timings *bt = &timings->bt; int ret; + u32 val, eotf; + csi->rk628->is_10bit = false; ret = rk628_hdmirx_get_timings(csi->rk628, timings); if (ret) return ret; + rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_DRM_HB, &val); + rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_DRM_PAYLOAD0, &val); + eotf = (val >> 8) & 0xff; + if (eotf == 0x02 && csi->rk628->color_format == BUS_FMT_YUV422) + csi->rk628->is_10bit = true; + v4l2_dbg(1, debug, sd, "hfp:%d, hs:%d, hbp:%d, vfp:%d, vs:%d, vbp:%d, interlace:%d\n", bt->hfrontporch, bt->hsync, bt->hbackporch, bt->vfrontporch, bt->vsync, bt->vbackporch, bt->interlaced); @@ -506,6 +516,13 @@ static int rk628_csi_get_detected_timings(struct v4l2_subdev *sd, csi->rk628->dual_mipi = false; } + if (csi->plat_data->tx_mode == CSI_MODE) { + if (csi->rk628->is_10bit) + csi->mbus_fmt_code = MEDIA_BUS_FMT_YUYV10_2X10; + else + csi->mbus_fmt_code = MEDIA_BUS_FMT_UYVY8_2X8; + } + return ret; } @@ -1060,15 +1077,31 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) struct rk628_csi *csi = to_csi(sd); u8 video_fmt; u8 lanes = csi->csi_lanes_in_use; - u8 lane_num; - u32 wc_usrdef, val; + u8 lane_num, yc_swap; + u32 wc_usrdef, val, data_type, pixfmt; int avi_rdy; lane_num = lanes - 1; csi->rk628->dphy_lane_en = (1 << (lanes + 1)) - 1; - wc_usrdef = csi->timings.bt.width * 2; - if (csi->rk628->dual_mipi) + + if (csi->rk628->dual_mipi && !csi->rk628->is_10bit) wc_usrdef = csi->timings.bt.width; + else if (csi->rk628->dual_mipi && csi->rk628->is_10bit) + wc_usrdef = div_u64(csi->timings.bt.width * 10, 8); + else if (!csi->rk628->dual_mipi && csi->rk628->is_10bit) + wc_usrdef = div_u64(csi->timings.bt.width * 2 * 10, 8); + else + wc_usrdef = csi->timings.bt.width * 2; + + if (csi->rk628->is_10bit) { + pixfmt = CSI_RAW10; + data_type = YUV422_10BIT; + yc_swap = 1; + } else { + pixfmt = CSI_RAW8; + data_type = YUV422_8BIT; + yc_swap = 0; + } v4l2_info(sd, "%s mipi mode, word count user define: %d\n", csi->rk628->dual_mipi ? "dual" : "single", wc_usrdef); rk628_csi_disable_stream(sd); @@ -1109,6 +1142,7 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) BYPASS_SELECT(1)); } else { rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN, + VOP_YU_SWAP_MASK | VOP_UV_SWAP_MASK | VOP_YUV422_EN_MASK | VOP_YUV422_MODE_MASK | @@ -1116,6 +1150,7 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) LANE_NUM_MASK | DPHY_EN_MASK | CSITX_EN_MASK, + VOP_YU_SWAP(yc_swap) | VOP_UV_SWAP(0) | VOP_YUV422_EN(1) | VOP_YUV422_MODE(2) | @@ -1149,8 +1184,8 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) rk628_i2c_write(csi->rk628, CSITX_VOP_PATH_CTRL, VOP_WC_USERDEFINE(wc_usrdef) | - VOP_DT_USERDEFINE(YUV422_8BIT) | - VOP_PIXEL_FORMAT(0) | + VOP_DT_USERDEFINE(data_type) | + VOP_PIXEL_FORMAT(pixfmt) | VOP_WC_USERDEFINE_EN(1) | VOP_DT_USERDEFINE_EN(1) | VOP_PATH_EN(1)); @@ -1165,6 +1200,7 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) if (csi->rk628->version >= RK628F_VERSION) { rk628_i2c_update_bits(csi->rk628, CSITX1_CSITX_EN, + VOP_YU_SWAP_MASK | VOP_UV_SWAP_MASK | VOP_YUV422_EN_MASK | VOP_YUV422_MODE_MASK | @@ -1172,6 +1208,7 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) LANE_NUM_MASK | DPHY_EN_MASK | CSITX_EN_MASK, + VOP_YU_SWAP(yc_swap) | VOP_UV_SWAP(0) | VOP_YUV422_EN(1) | VOP_YUV422_MODE(2) | @@ -1203,8 +1240,8 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) rk628_i2c_write(csi->rk628, CSITX1_VOP_PATH_CTRL, VOP_WC_USERDEFINE(wc_usrdef) | - VOP_DT_USERDEFINE(YUV422_8BIT) | - VOP_PIXEL_FORMAT(0) | + VOP_DT_USERDEFINE(data_type) | + VOP_PIXEL_FORMAT(pixfmt) | VOP_WC_USERDEFINE_EN(1) | VOP_DT_USERDEFINE_EN(1) | VOP_PATH_EN(1)); @@ -2185,7 +2222,7 @@ static int rk628_csi_enum_frame_interval(struct v4l2_subdev *sd, static u32 rk628_csi_get_lane_rate_mbps(struct rk628_csi *csi) { u32 lane_rate; - u32 max_lane_rate = 1300; + u32 max_lane_rate = 1800; u8 bpp, lanes; u64 pixelclock = csi->timings.bt.pixelclock; @@ -2196,23 +2233,48 @@ static u32 rk628_csi_get_lane_rate_mbps(struct rk628_csi *csi) lane_rate = div_u64(lane_rate, lanes); if (csi->rk628->dual_mipi) lane_rate /= 2; + if (csi->rk628->is_10bit) + lane_rate = div_u64(lane_rate * 5, 4); - if (lane_rate > 1300) + if (lane_rate > 1500) lane_rate = max_lane_rate; - else if (lane_rate > 700 && lane_rate <= 1300) + else if (lane_rate > 1300 && lane_rate <= 1500) + lane_rate = 1500; + else if (lane_rate > 900 && lane_rate <= 1300) lane_rate = 1300; + else if (lane_rate > 700 && lane_rate <= 900) + lane_rate = 900; else lane_rate = 700; return lane_rate; } +static int rk628_find_best_link_freq(u32 rate) +{ + u32 dist; + int cur_best_fit = 0; + u32 cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { + dist = abs(div_u64(link_freq_menu_items[i] * 2, 1000000) - rate); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return cur_best_fit; +} + static int rk628_csi_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct rk628_csi *csi = to_csi(sd); u32 rate; + int index; if (!tx_5v_power_present(sd) || csi->nosignal) { v4l2_info(sd, "%s hdmirx no signal\n", __func__); @@ -2239,13 +2301,8 @@ static int rk628_csi_get_fmt(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s mipi bitrate:%u mbps\n", __func__, rate); - if (rate > 1300) - __v4l2_ctrl_s_ctrl(csi->link_freq, 2); - else if (rate <= 1300 && rate > 700) - __v4l2_ctrl_s_ctrl(csi->link_freq, 1); - else - __v4l2_ctrl_s_ctrl(csi->link_freq, 0); - + index = rk628_find_best_link_freq(rate); + __v4l2_ctrl_s_ctrl(csi->link_freq, index); __v4l2_ctrl_s_ctrl_int64(csi->pixel_rate, RK628_CSI_PIXEL_RATE_HIGH); mutex_unlock(&csi->confctl_mutex); @@ -2300,11 +2357,15 @@ static int rk628_csi_set_fmt(struct v4l2_subdev *sd, switch (code) { case MEDIA_BUS_FMT_UYVY8_2X8: - if (csi->plat_data->bus_fmt == MEDIA_BUS_FMT_UYVY8_2X8) + if (csi->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_2X8) break; return -EINVAL; case MEDIA_BUS_FMT_RGB888_1X24: - if (csi->plat_data->bus_fmt == MEDIA_BUS_FMT_RGB888_1X24) + if (csi->mbus_fmt_code == MEDIA_BUS_FMT_RGB888_1X24) + break; + return -EINVAL; + case MEDIA_BUS_FMT_YUYV10_2X10: + if (csi->mbus_fmt_code == MEDIA_BUS_FMT_YUYV10_2X10) break; return -EINVAL; default: diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.c b/drivers/media/i2c/rk628/rk628_hdmirx.c index 1cd59b023287..319b552615bd 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.c +++ b/drivers/media/i2c/rk628/rk628_hdmirx.c @@ -1753,6 +1753,7 @@ static int rk628_hdmirx_read_timing(struct rk628 *rk628, hfp = hfp * 2 * 8 / 10; hbp = hbp * 2 * 8 / 10; hs = hs * 2 * 8 / 10; + rk628->is_10bit = true; } else { htotal *= 2; hact *= 2; diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.h b/drivers/media/i2c/rk628/rk628_hdmirx.h index ed72aa6d8007..ea0e091e82b0 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.h +++ b/drivers/media/i2c/rk628/rk628_hdmirx.h @@ -460,6 +460,10 @@ #define RK628_CSI_LINK_FREQ_LOW 350000000 #define RK628_CSI_LINK_FREQ_HIGH 650000000 +#define RK628_CSI_LINK_FREQ_350M 350000000 +#define RK628_CSI_LINK_FREQ_450M 450000000 +#define RK628_CSI_LINK_FREQ_650M 650000000 +#define RK628_CSI_LINK_FREQ_750M 750000000 #define RK628_CSI_LINK_FREQ_925M 925000000 #define RK628_CSI_PIXEL_RATE_LOW 400000000 #define RK628_CSI_PIXEL_RATE_HIGH 600000000 @@ -471,7 +475,6 @@ #define CSITX_ERR_RETRY_TIMES 3 #define USE_4_LANES 4 -#define YUV422_8BIT 0x1e #define SCDC_CED_ERR_CNT 0xfff