media: i2c: rk628: add csi output yuv422 10bit support

Signed-off-by: Jianwei Fan <jianwei.fan@rock-chips.com>
Change-Id: Ibdbb2d39287b13115babb36e6643c0a0c60eadeb
This commit is contained in:
Jianwei Fan
2025-08-16 15:55:07 +08:00
committed by 范建威
parent e2c0f45bcb
commit 41c01fb27b
5 changed files with 103 additions and 23 deletions

View File

@@ -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, ...) \

View File

@@ -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

View File

@@ -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:

View File

@@ -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;

View File

@@ -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