From 61a57d98f246110b87bdbdc5b681ffd9d332cca3 Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Thu, 13 Jun 2024 12:05:40 +0800 Subject: [PATCH 01/19] ASoC: es8323: add support 'ADC Data Select' control node This patch supports select and swap ADC data, the item 'Left Right' is default. For example, swap left and right for ADC: ---- amixer -c 0 sset 'ADC Data Select' 'Right Left' Simple mixer control 'ADC Data Select',0 Capabilities: enum Items: 'Left Right' 'Left Left' 'Right Right' 'Right Left' Item0: 'Right Left' Change-Id: Ied9296929010e93773cf335f210c3ec7be12481e Signed-off-by: Xing Zheng --- sound/soc/codecs/es8323.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c index ed7416411a7d..36c961b2bb67 100644 --- a/sound/soc/codecs/es8323.c +++ b/sound/soc/codecs/es8323.c @@ -144,6 +144,10 @@ static const char * const es8323_diff_sel[] = { "Line 1", "Line 2" }; +static const char * const es8323_adc_data_sel[] = { + "Left Right", "Left Left", "Right Right", "Right Left" +}; + SOC_VALUE_ENUM_SINGLE_DECL(es8323_left_dac_enum, ES8323_ADCCONTROL2, 6, 3, es8323_pga_sell, es8323_line_values); SOC_VALUE_ENUM_SINGLE_DECL(es8323_right_dac_enum, ES8323_ADCCONTROL2, 4, 3, es8323_pga_selr, es8323_line_values); static SOC_ENUM_SINGLE_DECL(es8323_diff_enum, ES8323_ADCCONTROL3, 7, es8323_diff_sel); @@ -163,6 +167,7 @@ static const struct soc_enum es8323_enum[] = { SOC_ENUM_SINGLE(ES8323_ADCCONTROL6, 6, 4, adcpol_txt), SOC_ENUM_SINGLE(ES8323_ADCCONTROL3, 3, 3, es8323_mono_mux), SOC_ENUM_SINGLE(ES8323_ADCCONTROL3, 7, 2, es8323_diff_sel), + SOC_ENUM_SINGLE(ES8323_ADCCONTROL4, 6, 4, es8323_adc_data_sel), }; static const DECLARE_TLV_DB_SCALE(adc_tlv, -9600, 50, 1); @@ -208,6 +213,7 @@ static const struct snd_kcontrol_new es8323_snd_controls[] = { ES8323_DACCONTROL25, 0, 33, 0, out_tlv), SOC_DOUBLE_R_TLV("Output 2 Playback Volume", ES8323_DACCONTROL26, ES8323_DACCONTROL27, 0, 33, 0, out_tlv), + SOC_ENUM("ADC Data Select", es8323_enum[11]), }; static const struct snd_kcontrol_new es8323_left_line_controls = From c92f3fa91025578a8fade015a40d3eb38f76cb92 Mon Sep 17 00:00:00 2001 From: Wangqiang Guo Date: Thu, 13 Jun 2024 19:43:56 +0800 Subject: [PATCH 02/19] media: rockchip: hdmirx: fix low_latency NULL pointer err. Type: Fix Redmine ID: #483313 Associated modifications: null Test: 1.echo 1 > /sys/module/rockchip_hdmirx/parameters/low_latency 2.reboot during preview. Change-Id: Ie55efc60a9520202e63d915c71316dea27af8fae Signed-off-by: Wangqiang Guo --- drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c index 2350333aa333..7217eddf83d4 100644 --- a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c +++ b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c @@ -2195,6 +2195,7 @@ static void hdmirx_free_fence(struct rk_hdmirx_dev *hdmirx_dev) unsigned long lock_flags = 0; struct hdmirx_fence *vb_fence, *done_fence; struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev; + struct files_struct *files = current->files; LIST_HEAD(local_list); spin_lock_irqsave(&hdmirx_dev->fence_lock, lock_flags); @@ -2216,7 +2217,8 @@ static void hdmirx_free_fence(struct rk_hdmirx_dev *hdmirx_dev) v4l2_dbg(2, debug, v4l2_dev, "%s: free qbuf_fence fd:%d\n", __func__, vb_fence->fence_fd); dma_fence_put(vb_fence->fence); - put_unused_fd(vb_fence->fence_fd); + if (files) + put_unused_fd(vb_fence->fence_fd); kfree(vb_fence); } @@ -2229,7 +2231,8 @@ static void hdmirx_free_fence(struct rk_hdmirx_dev *hdmirx_dev) v4l2_dbg(2, debug, v4l2_dev, "%s: free done_fence fd:%d\n", __func__, done_fence->fence_fd); dma_fence_put(done_fence->fence); - put_unused_fd(done_fence->fence_fd); + if (files) + put_unused_fd(done_fence->fence_fd); kfree(done_fence); } } From 97bf3e44dfad4b11c744031fbb310fd361e6dc71 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Tue, 11 Jun 2024 09:44:36 +0800 Subject: [PATCH 03/19] arm64: dts: rockchip: rk3576: fix apb clk to PCLK_HDPTX_APB for edp The parent clock of PCLK_HDPTX_APB is PCLK_PMUPHY_ROOT, which must be always on. In order to reduce power consumption, replace apb clock PCLK_PMUPHY_ROOT by controllable PCLK_HDPTX_APB in suspending and resuming. Change-Id: I1103b9bf542bacfd021de9a2553265fd6960e6d5 Signed-off-by: Damon Ding --- arch/arm64/boot/dts/rockchip/rk3576.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi index 10fbfbeb5639..850d5fed2f70 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi @@ -5186,7 +5186,7 @@ hdptxphy: phy@2b000000 { compatible = "rockchip,rk3576-hdptx-phy", "rockchip,rk3588-hdptx-phy"; reg = <0x0 0x2b000000 0x0 0x2000>; - clocks = <&cru CLK_PHY_REF_SRC>, <&cru PCLK_PMUPHY_ROOT>; + clocks = <&cru CLK_PHY_REF_SRC>, <&cru PCLK_HDPTX_APB>; clock-names = "ref", "apb"; resets = <&cru SRST_P_HDPTX_APB>, <&cru SRST_HDPTX_INIT>, <&cru SRST_HDPTX_CMN>, <&cru SRST_HDPTX_LANE>; From 8667847bb906bc1521ae2c8376c3f73ef2364781 Mon Sep 17 00:00:00 2001 From: Algea Cao Date: Thu, 30 May 2024 16:21:22 +0800 Subject: [PATCH 04/19] drm/rockchip: dw-hdmi-qp: Enable/disable hdcp without hdmi enable/disable The original on/off hdcp process will cause hdmi and vop to be on/off, which will affect other display interfaces in the mirror scenario. Change-Id: I1f83063e800beb0b78cd793971e5f4f65b8bf55e Signed-off-by: Algea Cao --- drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 77 ++++++++++---------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c index 55c465053f0a..d067d3ccd6e8 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c @@ -2075,9 +2075,8 @@ static u8 dw_hdmi_qp_hdcp_capable(struct dw_hdmi_qp *hdmi) } static void dw_hdmi_qp_hdcp_enable(struct dw_hdmi_qp *hdmi, - struct drm_connector *connector) + const struct drm_connector_state *conn_state) { - const struct drm_connector_state *conn_state = connector->state; void *data = hdmi->plat_data->phy_data; hdmi_writel(hdmi, 0, HDCP2LOGIC_ESM_GPIO_IN); @@ -2715,8 +2714,31 @@ static bool check_hdr_color_change(struct drm_connector_state *old_state, return false; } -static bool check_dw_hdcp_state_changed(struct drm_connector *conn, - struct drm_atomic_state *state) +static void dw_hdmi_qp_hdcp_disable(struct dw_hdmi_qp *hdmi, + const struct drm_connector_state *conn_state) +{ + void *data = hdmi->plat_data->phy_data; + + hdmi_writel(hdmi, 0, AVP_1_INT_MASK_N); + hdmi_writel(hdmi, 0, AVP_3_INT_MASK_N); + if (hdmi->hdcp && hdmi->hdcp->hdcp_stop) + hdmi->hdcp->hdcp_stop(hdmi->hdcp); + + hdmi_writel(hdmi, 0x34, HDCP2LOGIC_ESM_GPIO_IN); + hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); + + if (conn_state->content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + drm_hdcp_update_content_protection(hdmi->curr_conn, + DRM_MODE_CONTENT_PROTECTION_DESIRED); + + hdmi->hdcp_status = 0; + if (hdmi->plat_data->set_hdcp_status) + hdmi->plat_data->set_hdcp_status(data, hdmi->hdcp_status); +} + +static void set_dw_hdmi_hdcp_enable(struct dw_hdmi_qp *hdmi, + struct drm_connector *conn, + struct drm_atomic_state *state) { struct drm_connector_state *old_state, *new_state; u64 old_cp, new_cp; @@ -2726,24 +2748,14 @@ static bool check_dw_hdcp_state_changed(struct drm_connector *conn, old_cp = old_state->content_protection; new_cp = new_state->content_protection; - if (old_state->hdcp_content_type != new_state->hdcp_content_type && - new_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { - new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; - return true; + if (old_cp != new_cp) { + if (new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED && + old_cp == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + dw_hdmi_qp_hdcp_enable(hdmi, new_state); + else if (new_cp == DRM_MODE_CONTENT_PROTECTION_UNDESIRED && + old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED) + dw_hdmi_qp_hdcp_disable(hdmi, new_state); } - - if (!new_state->crtc) { - if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED) - new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; - return false; - } - - if (old_cp == new_cp || - (old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED && - new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)) - return false; - - return true; } static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, @@ -2890,9 +2902,6 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, } } - if (check_dw_hdcp_state_changed(connector, state)) - crtc_state->mode_changed = true; - return 0; } @@ -2908,6 +2917,8 @@ static void dw_hdmi_connector_atomic_commit(struct drm_connector *connector, hdmi_writel(hdmi, 2, PKTSCHED_PKT_CONTROL0); hdmi->update = false; } + + set_dw_hdmi_hdcp_enable(hdmi, connector, state); } void dw_hdmi_qp_set_output_type(struct dw_hdmi_qp *hdmi, u64 val) @@ -3078,21 +3089,7 @@ static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge, hdmi_writel(hdmi, 1, PKTSCHED_PKT_CONTROL0); mdelay(50); - hdmi_writel(hdmi, 0, AVP_1_INT_MASK_N); - hdmi_writel(hdmi, 0, AVP_3_INT_MASK_N); - if (hdmi->hdcp && hdmi->hdcp->hdcp_stop) - hdmi->hdcp->hdcp_stop(hdmi->hdcp); - - hdmi_writel(hdmi, 0x34, HDCP2LOGIC_ESM_GPIO_IN); - hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); - - if (conn_state->content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) - drm_hdcp_update_content_protection(hdmi->curr_conn, - DRM_MODE_CONTENT_PROTECTION_DESIRED); - - hdmi->hdcp_status = 0; - if (hdmi->plat_data->set_hdcp_status) - hdmi->plat_data->set_hdcp_status(data, hdmi->hdcp_status); + dw_hdmi_qp_hdcp_disable(hdmi, conn_state); extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false); handle_plugged_change(hdmi, false); @@ -3165,7 +3162,7 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, if (hdmi->panel) drm_panel_enable(hdmi->panel); - dw_hdmi_qp_hdcp_enable(hdmi, hdmi->curr_conn); + dw_hdmi_qp_hdcp_enable(hdmi, hdmi->curr_conn->state); } static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { From b3b89be50d7c0807d917349dd1c9c4ec05da97af Mon Sep 17 00:00:00 2001 From: Mingwei Yan Date: Wed, 17 Apr 2024 14:11:40 +0800 Subject: [PATCH 05/19] media: rockchip: vpss: offline support 8k Signed-off-by: Mingwei Yan Change-Id: Ida1b1d32570bdbdade6d7dc1bd216da6d21c5248 --- drivers/media/platform/rockchip/vpss/common.h | 10 +- .../platform/rockchip/vpss/vpss_offline.c | 1323 +++++++++++++---- .../platform/rockchip/vpss/vpss_offline.h | 18 + include/uapi/linux/rk-vpss-config.h | 3 + 4 files changed, 1042 insertions(+), 312 deletions(-) diff --git a/drivers/media/platform/rockchip/vpss/common.h b/drivers/media/platform/rockchip/vpss/common.h index c0acec04ed67..ef595721658a 100644 --- a/drivers/media/platform/rockchip/vpss/common.h +++ b/drivers/media/platform/rockchip/vpss/common.h @@ -20,11 +20,12 @@ #define RKVPSS_DEFAULT_WIDTH 1920 #define RKVPSS_DEFAULT_HEIGHT 1080 - #define RKVPSS_MAX_WIDTH 4672 #define RKVPSS_MAX_HEIGHT 3504 #define RKVPSS_MIN_WIDTH 32 #define RKVPSS_MIN_HEIGHT 32 +#define RKVPSS_UNITE_MAX_WIDTH 8192 +#define RKVPSS_UNITE_MAX_HEIGHT 6144 #define RKVPSS_VIDEO_NAME_LEN 16 #define RKVPSS_REG_CACHE_SYNC 0xeeeeeeee @@ -43,6 +44,13 @@ enum rkvpss_fmt_pix_type { FMT_RGB, }; +enum rkvpss_rotate { + ROTATE_0 = 0, + ROTATE_90, + ROTATE_180, + ROTATE_270, +}; + /* One structure per video node */ struct rkvpss_vdev_node { struct vb2_queue buf_queue; diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.c b/drivers/media/platform/rockchip/vpss/vpss_offline.c index 5c1ce67a1180..b71b90979200 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.c +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.c @@ -254,8 +254,9 @@ static void rkvpss_ofl_buf_del(struct file *file, struct rkvpss_buf_info *info) buf_del(file, info->dev_id, info->buf_fd[i], false, false); } -static void poly_phase_scale(struct rkvpss_offline_dev *ofl, - struct rkvpss_output_cfg *cfg) +static void poly_phase_scale(struct rkvpss_frame_cfg *frame_cfg, + struct rkvpss_offline_dev *ofl, + struct rkvpss_output_cfg *cfg, bool unite, bool left) { struct rkvpss_hw_dev *hw = ofl->hw; u32 in_w = cfg->crop_width, in_h = cfg->crop_height; @@ -282,12 +283,35 @@ static void poly_phase_scale(struct rkvpss_offline_dev *ofl, out_div = 1; } - val = (in_w - 1) | ((in_h - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); - val = (out_w - 1) | ((out_h - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_DST_SIZE, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_DST_SIZE, val); + if (unite) { + if (left) { + if (in_w == out_w) + val = (cfg->crop_width / 2 - 1) | + ((cfg->crop_height - 1) << 16); + else + val = (cfg->crop_width / 2 + UNITE_ENLARGE - 1) | + ((cfg->crop_height - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); + } else { + val = (ALIGN(ofl->unite_params[0].right_scl_need_size_y + 3, 2) - 1) | + ((cfg->crop_height - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); + val = (ALIGN(ofl->unite_params[0].right_scl_need_size_c + 6, 2) - 1) | + ((cfg->crop_height - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); + } + val = (cfg->scl_width / 2 - 1) | ((cfg->scl_height - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_DST_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_DST_SIZE, val); + } else { + val = (in_w - 1) | ((in_h - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); + val = (out_w - 1) | ((out_h - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_DST_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_DST_SIZE, val); + } ctrl = RKVPSS_ZME_XSCL_MODE | RKVPSS_ZME_YSCL_MODE; if (dering_en) { @@ -320,8 +344,15 @@ static void poly_phase_scale(struct rkvpss_offline_dev *ofl, y_xscl_fac = 0; uv_xscl_fac = 0; } - rkvpss_hw_write(hw, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac); + if (unite && !left) { + rkvpss_hw_write(hw, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac | + (ofl->unite_params[0].y_w_phase << 16)); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac | + (ofl->unite_params[0].c_w_phase << 16)); + } else { + rkvpss_hw_write(hw, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac); + } if (in_h != out_h) { if (in_h > out_h) { @@ -354,19 +385,39 @@ static void poly_phase_scale(struct rkvpss_offline_dev *ofl, rkvpss_hw_write(hw, RKVPSS_ZME_Y_SCL_CTRL, ctrl); rkvpss_hw_write(hw, RKVPSS_ZME_UV_SCL_CTRL, ctrl); + if (unite) { + val = cfg->scl_width / 2; + rkvpss_hw_write(hw, RKVPSS_ZME_H_SIZE, (val << 16) | val); + rkvpss_hw_write(hw, RKVPSS_ZME_H_OFFS, 0); + if (!left) { + val = cfg->scl_width / 2 - ALIGN_DOWN(cfg->scl_width / 2, 16); + rkvpss_hw_write(hw, RKVPSS_ZME_H_OFFS, (3 << 20) | (3 << 16) | + (ofl->unite_params[0].scl_in_crop_w_y << 8) | + (ofl->unite_params[0].scl_in_crop_w_c << 12) | + (val << 4) | val); + } + } + ctrl = RKVPSS_ZME_GATING_EN; if (yuv420_in) ctrl |= RKVPSS_ZME_SCL_YUV420_REAL_EN; if (yuv422_to_420) ctrl |= RKVPSS_ZME_422TO420_EN; + if (unite) { + if (left) + ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_8K_EN; + else + ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_IN_CLIP_EN | RKVPSS_ZME_8K_EN; + } rkvpss_hw_write(hw, RKVPSS_ZME_CTRL, ctrl); end: val = RKVPSS_ZME_GEN_UPD | RKVPSS_ZME_FORCE_UPD; rkvpss_hw_write(hw, RKVPSS_ZME_UPDATE, val); } -static void bilinear_scale(struct rkvpss_offline_dev *ofl, - struct rkvpss_output_cfg *cfg, int idx) +static void bilinear_scale(struct rkvpss_frame_cfg *frame_cfg, + struct rkvpss_offline_dev *ofl, + struct rkvpss_output_cfg *cfg, int idx, bool unite, bool left) { struct rkvpss_hw_dev *hw = ofl->hw; u32 in_w = cfg->crop_width, in_h = cfg->crop_height; @@ -388,41 +439,85 @@ static void bilinear_scale(struct rkvpss_offline_dev *ofl, return; } - if (in_w == out_w && in_h == out_w) - goto end; + if (!unite) { + if (in_w == out_w && in_h == out_w) + goto end; - /* TODO diff for input and output format */ - if (yuv420_in) { - in_div = 2; - out_div = 2; - } else if (yuv422_to_420) { - in_div = 1; - out_div = 2; + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + val = in_w | (in_h << 16); + rkvpss_hw_write(hw, reg_base + 0x8, val); + val = out_w | (out_h << 16); + rkvpss_hw_write(hw, reg_base + 0xc, val); + + if (in_w != out_w) { + val = (in_w - 1) * 4096 / (out_w - 1); + rkvpss_hw_write(hw, reg_base + 0x10, val); + val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1); + rkvpss_hw_write(hw, reg_base + 0x14, val); + + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + if (in_h != out_h) { + val = (in_h - 1) * 4096 / (out_h - 1); + rkvpss_hw_write(hw, reg_base + 0x18, val); + val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1); + rkvpss_hw_write(hw, reg_base + 0x1c, val); + + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } } else { - in_div = 1; - out_div = 1; - } - - val = in_w | (in_h << 16); - rkvpss_hw_write(hw, reg_base + 0x8, val); - val = out_w | (out_h << 16); - rkvpss_hw_write(hw, reg_base + 0xc, val); - - if (in_w != out_w) { - val = (in_w - 1) * 4096 / (out_w - 1); - rkvpss_hw_write(hw, reg_base + 0x10, val); - val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1); - rkvpss_hw_write(hw, reg_base + 0x14, val); - - ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; - } - if (in_h != out_h) { - val = (in_h - 1) * 4096 / (out_h - 1); - rkvpss_hw_write(hw, reg_base + 0x18, val); - val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1); - rkvpss_hw_write(hw, reg_base + 0x1c, val); - - ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + if (left) { + rkvpss_hw_write(hw, reg_base + 0x50, 0); + rkvpss_hw_write(hw, reg_base + 0x20, 0); + rkvpss_hw_write(hw, reg_base + 0x24, 0); + rkvpss_hw_write(hw, reg_base + 0x48, 0); + rkvpss_hw_write(hw, reg_base + 0x4c, 0); + if (in_w == out_w) + val = (cfg->crop_width / 2) | (cfg->crop_height << 16); + else + val = (cfg->crop_width / 2 + UNITE_ENLARGE) | + (cfg->crop_height << 16); + rkvpss_hw_write(hw, reg_base + 0x8, val); + val = cfg->scl_width / 2 | (cfg->scl_height << 16); + rkvpss_hw_write(hw, reg_base + 0xc, val); + ctrl |= RKVPSS_SCL_CLIP_EN; + } else { + val = ofl->unite_params[idx].scl_in_crop_w_y | + (ofl->unite_params[idx].scl_in_crop_w_c << 4); + rkvpss_hw_write(hw, reg_base + 0x50, val); + rkvpss_hw_write(hw, reg_base + 0x20, ofl->unite_params[idx].y_w_phase); + rkvpss_hw_write(hw, reg_base + 0x24, ofl->unite_params[idx].c_w_phase); + val = cfg->scl_width / 2 - ALIGN_DOWN(cfg->scl_width / 2, 16); + rkvpss_hw_write(hw, reg_base + 0x48, val); + rkvpss_hw_write(hw, reg_base + 0x4c, val); + val = (cfg->crop_width / 2 + ofl->unite_right_enlarge) | + (cfg->crop_height << 16); + rkvpss_hw_write(hw, reg_base + 0x8, val); + val = cfg->scl_width / 2 | (cfg->scl_height << 16); + rkvpss_hw_write(hw, reg_base + 0xc, val); + ctrl |= RKVPSS_SCL_CLIP_EN | RKVPSS_SCL_IN_CLIP_EN; + } + if (cfg->scl_width != frame_cfg->input.width) { + rkvpss_hw_write(hw, reg_base + 0x10, ofl->unite_params[idx].y_w_fac); + rkvpss_hw_write(hw, reg_base + 0x14, ofl->unite_params[idx].c_w_fac); + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + if (cfg->scl_height != frame_cfg->input.height) { + rkvpss_hw_write(hw, reg_base + 0x18, ofl->unite_params[idx].y_h_fac); + rkvpss_hw_write(hw, reg_base + 0x1c, ofl->unite_params[idx].c_h_fac); + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } } end: @@ -431,22 +526,35 @@ end: rkvpss_hw_write(hw, reg_base + 0x4, val); } -static void scale_config(struct rkvpss_offline_dev *ofl, - struct rkvpss_output_cfg *cfg, int idx) +static void scale_config(struct file *file, + struct rkvpss_frame_cfg *cfg, bool unite, bool left) { - if (idx == RKVPSS_OUTPUT_CH0) - poly_phase_scale(ofl, cfg); - else - bilinear_scale(ofl, cfg, idx); + struct rkvpss_offline_dev *ofl = video_drvdata(file); + int i; + + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!cfg->output[i].enable) + continue; + + if (i == RKVPSS_OUTPUT_CH0) + poly_phase_scale(cfg, ofl, &cfg->output[i], unite, left); + else + bilinear_scale(cfg, ofl, &cfg->output[i], i, unite, left); + } } static void cmsc_config(struct rkvpss_offline_dev *ofl, - struct rkvpss_frame_cfg *cfg) + struct rkvpss_frame_cfg *cfg, bool unite, bool left) { struct rkvpss_hw_dev *hw = ofl->hw; - struct rkvpss_cmsc_cfg *cmsc_cfg, tmp_cfg = { 0 }; - u32 i, j, k, slope, hor, win_en, win_mode, val, ctrl = 0; - bool is_en = false; + struct rkvpss_cmsc_cfg *cmsc_cfg, tmp_cfg = {0}; + struct rkvpss_cmsc_win *win; + struct rkvpss_cmsc_point *point; + int i, j, k; + u32 ch_win_en[RKVPSS_OUTPUT_MAX]; + u32 ch_win_mode[RKVPSS_OUTPUT_MAX]; + u32 win_color[RKVPSS_CMSC_WIN_MAX]; + u32 val, slope, hor, mask, mosaic_block = 0, ctrl = 0; if (!hw->is_ofl_cmsc) return; @@ -454,51 +562,138 @@ static void cmsc_config(struct rkvpss_offline_dev *ofl, for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { if (!cfg->output[i].enable) continue; - win_en = 0; - win_mode = 0; + ch_win_en[i] = 0; + ch_win_mode[i] = 0; cmsc_cfg = &cfg->output[i].cmsc; for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + if (i == 0) + win_color[j] = 0; if (!cmsc_cfg->win[j].win_en) continue; - win_en |= BIT(j); - win_mode |= cmsc_cfg->win[j].mode ? BIT(j) : 0; - if (!cmsc_cfg->win[j].mode) { - tmp_cfg.win[j].cover_color_y = cmsc_cfg->win[j].cover_color_y; - tmp_cfg.win[j].cover_color_u = cmsc_cfg->win[j].cover_color_u; - tmp_cfg.win[j].cover_color_v = cmsc_cfg->win[j].cover_color_v; - tmp_cfg.win[j].cover_color_a = cmsc_cfg->win[j].cover_color_a; - if (tmp_cfg.win[j].cover_color_a > 15) - tmp_cfg.win[j].cover_color_a = 15; + ch_win_en[i] |= BIT(j); + ch_win_mode[i] |= cmsc_cfg->win[j].mode ? BIT(j) : 0; + /** mosaic_block use the last channel **/ + if (cmsc_cfg->win[j].mode) + mosaic_block = cfg->output[i].cmsc.mosaic_block; + /** window cover all channel consistent **/ + if (!cfg->output[i].cmsc.win[j].mode) { + win_color[j] = RKVPSS_CMSK_WIN_YUV(cfg->output[i].cmsc.win[j].cover_color_y, + cfg->output[i].cmsc.win[j].cover_color_u, + cfg->output[i].cmsc.win[j].cover_color_v); + if (cfg->output[i].cmsc.win[j].cover_color_a > 15) + cfg->output[i].cmsc.win[j].cover_color_a = 15; + win_color[j] |= RKVPSS_CMSC_WIN_ALPHA(cfg->output[i].cmsc.win[j].cover_color_a); } - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { tmp_cfg.win[j].point[k] = cmsc_cfg->win[j].point[k]; + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s input params dev_id:%d, unite:%d left:%d ch:%d win:%d point:%d x:%u y:%u", + __func__, cfg->dev_id, unite, left, i, j, k, + tmp_cfg.win[j].point[k].x, + tmp_cfg.win[j].point[k].y); + } } - if (win_en) - is_en = true; - tmp_cfg.win[i].win_en = win_en; - tmp_cfg.win[i].mode = win_mode; - if (tmp_cfg.mosaic_block < cmsc_cfg->mosaic_block) - tmp_cfg.mosaic_block = cmsc_cfg->mosaic_block; } - if (!is_en) { - rkvpss_hw_write(hw, RKVPSS_CMSC_CTRL, 0); - rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_WIN, 0); - rkvpss_hw_write(hw, RKVPSS_CMSC_CHN1_WIN, 0); - rkvpss_hw_write(hw, RKVPSS_CMSC_CHN2_WIN, 0); - rkvpss_hw_write(hw, RKVPSS_CMSC_CHN3_WIN, 0); - goto end; + /* deal unite left params */ + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!unite || !left) + break; + if (!cfg->output[i].enable) + continue; + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + win = &tmp_cfg.win[j]; + if (!(ch_win_en[i] & BIT(j))) + continue; + mask = 0; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + point = &win->point[k]; + if (point->x >= cfg->input.width / 2) + mask |= BIT(k); + else + mask &= ~BIT(k); + } + if (mask == 0xf) { + /** all right **/ + ch_win_en[i] &= ~BIT(j); + } else if (mask != 0) { + /** middle need avoid pentagon **/ + if (win->point[0].x != win->point[3].x || + win->point[1].x != win->point[2].x) { + ch_win_en[i] &= ~BIT(j); + } else { + win->point[1].x = cfg->input.width / 2; + win->point[2].x = cfg->input.width / 2; + } + } + } + } + + /* deal unite right params */ + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!unite || left) + break; + if (!cfg->output[i].enable) + continue; + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + win = &tmp_cfg.win[j]; + if (!(ch_win_en[i] & BIT(j))) + continue; + mask = 0; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + point = &win->point[k]; + if (point->x <= cfg->input.width / 2) + mask |= BIT(k); + else + mask &= ~BIT(k); + } + if (mask == 0xf) { + /** all left **/ + ch_win_en[i] &= ~BIT(j); + } else if (mask != 0) { + /** middle need avoid pentagon **/ + if (win->point[0].x != win->point[3].x || + win->point[1].x != win->point[2].x) { + ch_win_en[i] &= ~BIT(j); + } else { + win->point[0].x = ofl->unite_right_enlarge; + win->point[3].x = ofl->unite_right_enlarge; + win->point[1].x = win->point[1].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + win->point[2].x = win->point[2].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + } + } else { + /** all right **/ + win->point[0].x = win->point[0].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + win->point[1].x = win->point[1].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + win->point[2].x = win->point[2].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + win->point[3].x = win->point[3].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + } + } } for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - win_en = tmp_cfg.win[i].win_en; - if (win_en) + if (!cfg->output[i].enable) + continue; + if (ch_win_en[i]) { + ctrl |= RKVPSS_CMSC_EN; ctrl |= RKVPSS_CMSC_CHN_EN(i); - rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_WIN + i * 4, win_en); - win_mode = tmp_cfg.win[i].mode; - rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_MODE + i * 4, win_mode); - for (j = 0; j < RKVPSS_CMSC_WIN_MAX && win_en; j++) { - if (!(win_en & BIT(j))) + } + rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_WIN + i * 4, ch_win_en[i]); + rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_MODE + i * 4, ch_win_mode[i]); + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + if (!(ch_win_en[i] & BIT(j))) continue; for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { val = RKVPSS_CMSC_WIN_VTX(tmp_cfg.win[j].point[k].x, @@ -510,64 +705,78 @@ static void cmsc_config(struct rkvpss_offline_dev *ofl, &slope, &hor); val = RKVPSS_CMSC_WIN_SLP(slope, hor); rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_L0_SLP + k * 8 + j * 32, val); + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s dev_id:%d, unite:%d left:%d ch:%d win:%d point:%d x:%u y:%u", + __func__, cfg->dev_id, unite, left, i, j, k, + tmp_cfg.win[j].point[k].x, + tmp_cfg.win[j].point[k].y); } - - if ((win_mode & BIT(j))) + if ((ch_win_mode[i] & BIT(j))) continue; - val = RKVPSS_CMSK_WIN_YUV(tmp_cfg.win[j].cover_color_y, - tmp_cfg.win[j].cover_color_u, - tmp_cfg.win[j].cover_color_v); - val |= RKVPSS_CMSC_WIN_ALPHA(tmp_cfg.win[j].cover_color_a); - rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_PARA + j * 4, val); + rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_PARA + j * 4, win_color[j]); } } - ctrl |= RKVPSS_CMSC_EN; - ctrl |= RKVPSS_CMSC_BLK_SZIE(tmp_cfg.mosaic_block); + + ctrl |= RKVPSS_CMSC_BLK_SZIE(mosaic_block); rkvpss_hw_write(hw, RKVPSS_CMSC_CTRL, ctrl); -end: + val = RKVPSS_CMSC_GEN_UPD | RKVPSS_CMSC_FORCE_UPD; rkvpss_hw_write(hw, RKVPSS_CMSC_UPDATE, val); + + v4l2_dbg(4, rkvpss_debug, &ofl->v4l2_dev, + "%s dev_id:%d, unite:%d left:%d ctrl:0x%x update_val:0x%x", + __func__, cfg->dev_id, unite, left, ctrl, val); } -static void aspt_config(struct rkvpss_offline_dev *ofl, - struct rkvpss_output_cfg *cfg, int idx) +static void aspt_config(struct file *file, + struct rkvpss_frame_cfg *cfg) { + struct rkvpss_offline_dev *ofl = video_drvdata(file); struct rkvpss_hw_dev *hw = ofl->hw; u32 reg_base, val; + int i; - switch (idx) { - case RKVPSS_OUTPUT_CH0: - reg_base = RKVPSS_RATIO0_BASE; - break; - case RKVPSS_OUTPUT_CH1: - reg_base = RKVPSS_RATIO1_BASE; - break; - case RKVPSS_OUTPUT_CH2: - reg_base = RKVPSS_RATIO2_BASE; - break; - case RKVPSS_OUTPUT_CH3: - reg_base = RKVPSS_RATIO3_BASE; - break; - default: - return; - } + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!cfg->output[i].enable) + continue; - if (!cfg->aspt.enable) { - rkvpss_hw_write(hw, reg_base, 0); - goto end; + switch (i) { + case RKVPSS_OUTPUT_CH0: + reg_base = RKVPSS_RATIO0_BASE; + break; + case RKVPSS_OUTPUT_CH1: + reg_base = RKVPSS_RATIO1_BASE; + break; + case RKVPSS_OUTPUT_CH2: + reg_base = RKVPSS_RATIO2_BASE; + break; + case RKVPSS_OUTPUT_CH3: + reg_base = RKVPSS_RATIO3_BASE; + break; + default: + return; + } + + if (!cfg->output[i].aspt.enable) { + rkvpss_hw_write(hw, reg_base, 0); + val = RKVPSS_RATIO_FORCE_UPD | RKVPSS_RATIO_GEN_UPD; + rkvpss_hw_write(hw, reg_base + 0x4, val); + continue; + } + val = cfg->output[i].scl_width | (cfg->output[i].scl_height << 16); + rkvpss_hw_write(hw, reg_base + 0x10, val); + val = cfg->output[i].aspt.width | (cfg->output[i].aspt.height << 16); + rkvpss_hw_write(hw, reg_base + 0x14, val); + val = cfg->output[i].aspt.h_offs | (cfg->output[i].aspt.v_offs << 16); + rkvpss_hw_write(hw, reg_base + 0x18, val); + val = cfg->output[i].aspt.color_y | + (cfg->output[i].aspt.color_u << 16) | + (cfg->output[i].aspt.color_v << 24); + rkvpss_hw_write(hw, reg_base + 0x1c, val); + rkvpss_hw_write(hw, reg_base, RKVPSS_RATIO_EN); + val = RKVPSS_RATIO_FORCE_UPD | RKVPSS_RATIO_GEN_UPD; + rkvpss_hw_write(hw, reg_base + 0x4, val); } - val = cfg->scl_width | (cfg->scl_height << 16); - rkvpss_hw_write(hw, reg_base + 0x10, val); - val = cfg->aspt.width | (cfg->aspt.height << 16); - rkvpss_hw_write(hw, reg_base + 0x14, val); - val = cfg->aspt.h_offs | (cfg->aspt.v_offs << 16); - rkvpss_hw_write(hw, reg_base + 0x18, val); - val = cfg->aspt.color_y | (cfg->aspt.color_u << 16) | (cfg->aspt.color_v << 24); - rkvpss_hw_write(hw, reg_base + 0x1c, val); - rkvpss_hw_write(hw, reg_base, RKVPSS_RATIO_EN); -end: - val = RKVPSS_RATIO_FORCE_UPD | RKVPSS_RATIO_GEN_UPD; - rkvpss_hw_write(hw, reg_base + 0x4, val); } static void add_cfginfo(struct rkvpss_offline_dev *ofl, struct rkvpss_frame_cfg *cfg) @@ -602,59 +811,23 @@ static void add_cfginfo(struct rkvpss_offline_dev *ofl, struct rkvpss_frame_cfg if (count >= 5) { first_cfg = list_first_entry(&ofl->cfginfo_list, struct rkvpss_ofl_cfginfo, list); list_del_init(&first_cfg->list); + kfree(first_cfg); list_add_tail(&new_cfg->list, &ofl->cfginfo_list); } else { list_add_tail(&new_cfg->list, &ofl->cfginfo_list); } mutex_unlock(&ofl->ofl_lock); } -static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) + + +static int read_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) { struct rkvpss_offline_dev *ofl = video_drvdata(file); struct rkvpss_hw_dev *hw = ofl->hw; const struct vb2_mem_ops *mem_ops = hw->mem_ops; struct sg_table *sg_tbl; struct rkvpss_offline_buf *buf; - struct rkvpss_output_ch out_ch[RKVPSS_OUTPUT_MAX] = { 0 }; - u32 in_ctrl, in_size, in_c_offs, update, mi_update; - u32 w, h, val, reg, mask, crop_en, flip_en; - bool ch_en = false; - ktime_t t = 0; - s64 us = 0; - int ret, i, tile_num = 0; - bool wr_uv_swap = false; - u64 ns; - - if (rkvpss_debug >= 2) { - v4l2_info(&ofl->v4l2_dev, - "%s dev_id:%d seq:%d mirror:%d input:%dx%d buffd:%d format:%c%c%c%c\n", - __func__, cfg->dev_id, cfg->sequence, cfg->mirror, - cfg->input.width, cfg->input.height, cfg->input.buf_fd, - cfg->input.format, cfg->input.format >> 8, - cfg->input.format >> 16, cfg->input.format >> 24); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - v4l2_info(&ofl->v4l2_dev, - "\t\t\tch%d crop:(%d,%d)/%dx%d scl:%dx%d flip:%d buffd:%d format:%c%c%c%c\n", - i, cfg->output[i].crop_h_offs, cfg->output[i].crop_v_offs, - cfg->output[i].crop_width, cfg->output[i].crop_height, - cfg->output[i].scl_width, cfg->output[i].scl_height, - cfg->output[i].flip, cfg->output[i].buf_fd, - cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24); - } - t = ktime_get(); - } - - add_cfginfo(ofl, cfg); - - ns = ktime_get_ns(); - ofl->dev_rate[cfg->dev_id].in_rate = ns - ofl->dev_rate[cfg->dev_id].in_timestamp; - ofl->dev_rate[cfg->dev_id].in_timestamp = ns; - - init_completion(&ofl->cmpl); - ofl->mode_sel_en = false; + u32 in_ctrl, in_size, in_c_offs, unite_r_offs, val, mask, unite_off = 0, enlarge = 0; in_c_offs = 0; in_ctrl = 0; @@ -665,6 +838,7 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) in_c_offs = cfg->input.stride * cfg->input.height; in_size = cfg->input.stride * cfg->input.height * 2; in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; + unite_off = 8; break; case V4L2_PIX_FMT_NV12: if (cfg->input.stride < ALIGN(cfg->input.width, 16)) @@ -672,6 +846,7 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) in_c_offs = cfg->input.stride * cfg->input.height; in_size = cfg->input.stride * cfg->input.height * 3 / 2; in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; + unite_off = 8; break; case V4L2_PIX_FMT_NV61: if (cfg->input.stride < ALIGN(cfg->input.width, 16)) @@ -679,6 +854,7 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) in_c_offs = cfg->input.stride * cfg->input.height; in_size = cfg->input.stride * cfg->input.height * 2; in_ctrl |= RKVPSS_MI_RD_INPUT_422SP | RKVPSS_MI_RD_UV_SWAP; + unite_off = 8; break; case V4L2_PIX_FMT_NV21: if (cfg->input.stride < ALIGN(cfg->input.width, 16)) @@ -686,42 +862,49 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) in_c_offs = cfg->input.stride * cfg->input.height; in_size = cfg->input.stride * cfg->input.height * 3 / 2; in_ctrl |= RKVPSS_MI_RD_INPUT_420SP | RKVPSS_MI_RD_UV_SWAP; + unite_off = 8; break; case V4L2_PIX_FMT_RGB565: if (cfg->input.stride < ALIGN(cfg->input.width * 2, 16)) cfg->input.stride = ALIGN(cfg->input.width * 2, 16); in_size = cfg->input.stride * cfg->input.height; in_ctrl |= RKVPSS_MI_RD_INPUT_BGR565; + unite_off = 16; break; case V4L2_PIX_FMT_RGB565X: if (cfg->input.stride < ALIGN(cfg->input.width * 2, 16)) cfg->input.stride = ALIGN(cfg->input.width * 2, 16); in_size = cfg->input.stride * cfg->input.height; in_ctrl |= RKVPSS_MI_RD_INPUT_BGR565 | RKVPSS_MI_RD_RB_SWAP; + unite_off = 16; break; case V4L2_PIX_FMT_RGB24: if (cfg->input.stride < ALIGN(cfg->input.width * 3, 16)) cfg->input.stride = ALIGN(cfg->input.width * 3, 16); in_size = cfg->input.stride * cfg->input.height; in_ctrl |= RKVPSS_MI_RD_INPUT_BGR888; + unite_off = 24; break; case V4L2_PIX_FMT_BGR24: if (cfg->input.stride < ALIGN(cfg->input.width * 3, 16)) cfg->input.stride = ALIGN(cfg->input.width * 3, 16); in_size = cfg->input.stride * cfg->input.height; in_ctrl |= RKVPSS_MI_RD_INPUT_BGR888 | RKVPSS_MI_RD_RB_SWAP; + unite_off = 24; break; case V4L2_PIX_FMT_XRGB32: if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) cfg->input.stride = ALIGN(cfg->input.width * 4, 16); in_size = cfg->input.stride * cfg->input.height; in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 | RKVPSS_MI_RD_RB_SWAP; + unite_off = 32; break; case V4L2_PIX_FMT_XBGR32: if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) cfg->input.stride = ALIGN(cfg->input.width * 4, 16); in_size = cfg->input.stride * cfg->input.height; in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 | RKVPSS_MI_RD_RB_SWAP; + unite_off = 32; break; case V4L2_PIX_FMT_RGBX32: if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) @@ -730,6 +913,7 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 | RKVPSS_MI_RD_RB_SWAP | RKVPSS_MI_RD_ALPHA_SWAP; + unite_off = 32; break; case V4L2_PIX_FMT_BGRX32: if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) @@ -738,6 +922,7 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 | RKVPSS_MI_RD_RB_SWAP | RKVPSS_MI_RD_ALPHA_SWAP; + unite_off = 32; break; case V4L2_PIX_FMT_FBC0: if (cfg->input.stride < ALIGN(cfg->input.width, 16)) @@ -781,6 +966,192 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) return -EINVAL; } + buf = buf_add(file, cfg->dev_id, cfg->input.buf_fd, in_size); + if (!buf) + return -ENOMEM; + + sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); + + if (!unite) { + val = cfg->input.width; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); + val = cfg->input.height; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_HEIGHT, val); + val = sg_dma_address(sg_tbl->sgl); + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); + val += in_c_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); + } else { + val = cfg->input.height; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_HEIGHT, val); + ofl->unite_right_enlarge = ALIGN(cfg->input.width / 2, 16) - + (cfg->input.width / 2) + 16; + + if (left) { + if (!cfg->mirror) + enlarge = UNITE_LEFT_ENLARGE; + else + enlarge = ofl->unite_right_enlarge; + val = cfg->input.width / 2 + enlarge; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); + val = sg_dma_address(sg_tbl->sgl); + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); + val += in_c_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); + } else { + if (!cfg->mirror) + enlarge = ofl->unite_right_enlarge; + else + enlarge = UNITE_LEFT_ENLARGE; + val = cfg->input.width / 2 + enlarge; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); + val = (cfg->input.width / 2 - enlarge) * unite_off; + unite_r_offs = ALIGN_DOWN(val / 8, 16); + val = sg_dma_address(sg_tbl->sgl) + unite_r_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); + val += in_c_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); + } + } + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d width:%d height:%d y_base:0x%x\n", + __func__, unite, left, + rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_WIDTH), + rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_HEIGHT), + rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_BASE)); + + if (cfg->input.format == V4L2_PIX_FMT_FBC0 || + cfg->input.format == V4L2_PIX_FMT_FBC2 || + cfg->input.format == V4L2_PIX_FMT_FBC4) { + in_ctrl |= RKVPSS_MI_RD_MODE(2) | RKVPSS_MI_RD_FBCD_OPT_DIS; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, 0); + } else { + if (cfg->input.format == V4L2_PIX_FMT_TILE420 || + cfg->input.format == V4L2_PIX_FMT_TILE422) { + in_ctrl |= RKVPSS_MI_RD_MODE(1); + switch (cfg->input.rotate) { + case ROTATE_90: + in_ctrl |= RKVPSS_MI_RD_ROT_90; + break; + case ROTATE_180: + in_ctrl |= RKVPSS_MI_RD_ROT_180; + break; + case ROTATE_270: + in_ctrl |= RKVPSS_MI_RD_ROT_270; + break; + default: + in_ctrl |= RKVPSS_MI_RD_ROT_0; + break; + } + } + val = cfg->input.stride; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, val); + } + + mask = RKVPSS_MI_RD_GROUP_MODE(3) | RKVPSS_MI_RD_BURST16_LEN; + rkvpss_hw_set_bits(hw, RKVPSS_MI_RD_CTRL, ~mask, in_ctrl); + rkvpss_hw_write(hw, RKVPSS_MI_RD_INIT, RKVPSS_MI_RD_FORCE_UPD); + + return 0; +} + +static void crop_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + int i; + u32 reg, val, crop_en; + + crop_en = 0; + if (!unite) { + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!cfg->output[i].enable) + continue; + reg = RKVPSS_CROP0_0_H_OFFS; + val = cfg->output[i].crop_h_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_OFFS; + val = cfg->output[i].crop_v_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_H_SIZE; + val = cfg->output[i].crop_width; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_SIZE; + val = cfg->output[i].crop_height; + rkvpss_hw_write(hw, reg + i * 0x10, val); + crop_en |= RKVPSS_CROP_CHN_EN(i); + } + } else { + if (left) { + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!cfg->output[i].enable) + continue; + + reg = RKVPSS_CROP0_0_H_OFFS; + val = cfg->output[i].crop_h_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_OFFS; + val = cfg->output[i].crop_v_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_H_SIZE; + /*if no scale, left don't enlarge*/ + if (cfg->output[i].crop_width == cfg->output[i].scl_width) + val = cfg->output[i].crop_width / 2; + else + val = cfg->output[i].crop_width / 2 + UNITE_LEFT_ENLARGE; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_SIZE; + val = cfg->output[i].crop_height; + rkvpss_hw_write(hw, reg + i * 0x10, val); + crop_en |= RKVPSS_CROP_CHN_EN(i); + } + } else { + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!cfg->output[i].enable) + continue; + reg = RKVPSS_CROP0_0_H_OFFS; + val = ofl->unite_params[i].quad_crop_w; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_OFFS; + val = cfg->output[i].crop_v_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_H_SIZE; + val = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + ofl->unite_params[i].quad_crop_w; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_SIZE; + val = cfg->output[i].crop_height; + rkvpss_hw_write(hw, reg + i * 0x10, val); + crop_en |= RKVPSS_CROP_CHN_EN(i); + } + } + } + rkvpss_hw_write(hw, RKVPSS_CROP0_CTRL, crop_en); + rkvpss_hw_write(hw, RKVPSS_CROP0_UPDATE, RKVPSS_CROP_FORCE_UPD); + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d h_offs:%d v_offs:%d width:%d height:%d\n", + __func__, unite, left, + rkvpss_hw_read(hw, RKVPSS_CROP0_0_H_OFFS), + rkvpss_hw_read(hw, RKVPSS_CROP0_0_V_OFFS), + rkvpss_hw_read(hw, RKVPSS_CROP0_0_H_SIZE), + rkvpss_hw_read(hw, RKVPSS_CROP0_0_V_SIZE)); +} + +static int write_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *mem_ops = hw->mem_ops; + struct sg_table *sg_tbl; + struct rkvpss_offline_buf *buf; + struct rkvpss_output_ch out_ch[RKVPSS_OUTPUT_MAX] = { 0 }; + int i; + u32 w, h, val, reg, mask, mi_update, flip_en, unite_off = 0; + bool ch_en = false, wr_uv_swap = false; + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { if (!hw->is_ofl_ch[i] && cfg->output[i].enable) { v4l2_err(&ofl->v4l2_dev, @@ -792,57 +1163,6 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) if (!cfg->output[i].enable) continue; ch_en = true; - cfg->output[i].crop_h_offs = ALIGN(cfg->output[i].crop_h_offs, 2); - cfg->output[i].crop_v_offs = ALIGN(cfg->output[i].crop_v_offs, 2); - cfg->output[i].crop_width = ALIGN(cfg->output[i].crop_width, 2); - cfg->output[i].crop_height = ALIGN(cfg->output[i].crop_height, 2); - - if (!cfg->input.rotate || cfg->input.rotate == 2) { - if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > cfg->input.width) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ech%d inval crop(offs:%d w:%d) input width:%d\n", - i, cfg->dev_id, cfg->output[i].crop_h_offs, - cfg->output[i].crop_width, cfg->input.width); - cfg->output[i].crop_h_offs = 0; - cfg->output[i].crop_width = cfg->input.width; - } - if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > cfg->input.height) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d inval crop(offs:%d h:%d) input height:%d\n", - cfg->dev_id, i, cfg->output[i].crop_v_offs, - cfg->output[i].crop_height, cfg->input.height); - cfg->output[i].crop_v_offs = 0; - cfg->output[i].crop_height = cfg->input.height; - } - } else { - if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > cfg->input.height) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ech%d rotate inval crop(offs:%d w:%d) input height:%d\n", - i, cfg->dev_id, cfg->output[i].crop_h_offs, - cfg->output[i].crop_width, cfg->input.height); - cfg->output[i].crop_h_offs = 0; - cfg->output[i].crop_width = cfg->input.height; - } - if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > cfg->input.width) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d rotate inval crop(offs:%d h:%d) input width:%d\n", - cfg->dev_id, i, cfg->output[i].crop_v_offs, - cfg->output[i].crop_height, cfg->input.width); - cfg->output[i].crop_v_offs = 0; - cfg->output[i].crop_height = cfg->input.width; - } - } - - if (i == RKVPSS_OUTPUT_CH2 || i == RKVPSS_OUTPUT_CH3) { - if (cfg->output[i].crop_width != cfg->output[i].scl_width && - cfg->output[i].scl_width > 1920) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d scale max width 1920\n", - cfg->dev_id, i); - cfg->output[i].scl_width = 1920; - } - if (cfg->output[i].crop_height != cfg->output[i].scl_height && - cfg->output[i].scl_height > 1080) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d scale max height 1080\n", - cfg->dev_id, i); - cfg->output[i].scl_height = 1080; - } - } if (cfg->output[i].aspt.enable) { w = cfg->output[i].aspt.width; @@ -970,57 +1290,7 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) return -EINVAL; } - buf = buf_add(file, cfg->dev_id, cfg->input.buf_fd, in_size); - if (!buf) - goto err; - - sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); - val = sg_dma_address(sg_tbl->sgl); - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); - val += in_c_offs; - rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); - - val = cfg->input.width; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); - val = cfg->input.height; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_HEIGHT, val); - - if (cfg->input.format == V4L2_PIX_FMT_FBC0 || - cfg->input.format == V4L2_PIX_FMT_FBC2 || - cfg->input.format == V4L2_PIX_FMT_FBC4) { - in_ctrl |= RKVPSS_MI_RD_MODE(2) | RKVPSS_MI_RD_FBCD_OPT_DIS; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, 0); - } else { - if (cfg->input.format == V4L2_PIX_FMT_TILE420 || - cfg->input.format == V4L2_PIX_FMT_TILE422) { - in_ctrl |= RKVPSS_MI_RD_MODE(1); - switch (cfg->input.rotate) { - case 1: - in_ctrl |= RKVPSS_MI_RD_ROT_90; - break; - case 2: - in_ctrl |= RKVPSS_MI_RD_ROT_180; - break; - case 3: - in_ctrl |= RKVPSS_MI_RD_ROT_270; - break; - default: - in_ctrl |= RKVPSS_MI_RD_ROT_0; - break; - } - } - val = cfg->input.stride; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, val); - } - - mask = RKVPSS_MI_RD_GROUP_MODE(3) | RKVPSS_MI_RD_BURST16_LEN; - rkvpss_hw_set_bits(hw, RKVPSS_MI_RD_CTRL, ~mask, in_ctrl); - rkvpss_hw_write(hw, RKVPSS_MI_RD_INIT, RKVPSS_MI_RD_FORCE_UPD); - - cmsc_config(ofl, cfg); - mi_update = 0; - crop_en = 0; flip_en = 0; mask = 0; for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { @@ -1032,22 +1302,8 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) if (!buf) goto free_buf; - reg = RKVPSS_CROP0_0_H_OFFS; - val = cfg->output[i].crop_h_offs; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_OFFS; - val = cfg->output[i].crop_v_offs; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_H_SIZE; - val = cfg->output[i].crop_width; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_SIZE; - val = cfg->output[i].crop_height; - rkvpss_hw_write(hw, reg + i * 0x10, val); - crop_en |= RKVPSS_CROP_CHN_EN(i); - - scale_config(ofl, &cfg->output[i], i); - aspt_config(ofl, &cfg->output[i], i); + if (unite && !left) + unite_off = (ALIGN_DOWN(cfg->output[i].scl_width / 2, 16) * 8) / 8; if (cfg->output[i].aspt.enable) h = cfg->output[i].aspt.height; @@ -1055,7 +1311,7 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) h = cfg->output[i].scl_height; sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); - val = sg_dma_address(sg_tbl->sgl); + val = sg_dma_address(sg_tbl->sgl) + unite_off; reg = RKVPSS_MI_CHN0_WR_Y_BASE; rkvpss_hw_write(hw, reg + i * 0x100, val); reg = RKVPSS_MI_CHN0_WR_CB_BASE; @@ -1104,13 +1360,20 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) rkvpss_hw_write(hw, reg + i * 0x100, val); } mi_update |= (RKVPSS_MI_CHN0_FORCE_UPD << i); + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d ch:%d y_size:%d y_stride:%d y_pic_size:%d y_base:0x%x", + __func__, unite, left, i, + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_SIZE + i * 100), + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_STRIDE + i * 100), + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_PIC_SIZE + i * 100), + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_BASE + i * 100)); } - rkvpss_hw_write(hw, RKVPSS_CROP0_CTRL, crop_en); - rkvpss_hw_write(hw, RKVPSS_CROP0_UPDATE, RKVPSS_CROP_FORCE_UPD); if (flip_en) rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_VFLIP_CTRL, mask, flip_en); + /* config output uv swap */ for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { if (cfg->output[i].enable && (cfg->output[i].format == V4L2_PIX_FMT_VYUY || @@ -1118,7 +1381,6 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) cfg->output[i].format == V4L2_PIX_FMT_NV61)) wr_uv_swap = true; } - if (wr_uv_swap) { for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { if (cfg->output[i].enable && (cfg->output[i].format == V4L2_PIX_FMT_UYVY || @@ -1131,7 +1393,6 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) } } } - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { if (cfg->output[i].format == V4L2_PIX_FMT_VYUY || cfg->output[i].format == V4L2_PIX_FMT_NV21 || @@ -1143,22 +1404,175 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) } } - for (i = 0; i < 2; i++) { + for (i = 0; i <= RKVPSS_OUTPUT_CH1; i++) { if (cfg->output[i].format == V4L2_PIX_FMT_TILE420 || cfg->output[i].format == V4L2_PIX_FMT_TILE422) { - tile_num++; - if (tile_num > 1) { - v4l2_err(&ofl->v4l2_dev, - "dev_id:%d only one ch can tile write\n", cfg->dev_id); - return -EINVAL; - } mask = RKVPSS_MI_WR_TILE_SEL(3); val = RKVPSS_MI_WR_TILE_SEL(i + 1); rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_CTRL, mask, val); } } + /* need update two for online2 mode */ rkvpss_hw_write(hw, RKVPSS_MI_WR_INIT, mi_update); + rkvpss_hw_write(hw, RKVPSS_MI_WR_INIT, mi_update); + + return 0; + +free_buf: + for (i -= 1; i >= 0; i--) { + if (!cfg->output[i].enable) + continue; + buf_del(file, cfg->dev_id, cfg->output[i].buf_fd, false, true); + } + buf_del(file, cfg->dev_id, cfg->input.buf_fd, false, true); + return -ENOMEM; +} + +static void calc_unite_scl_params(struct file *file, struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_unite_scl_params *params; + int i; + u32 right_scl_need_size_y, right_scl_need_size_c; + u32 left_in_used_size_y, left_in_used_size_c; + u32 right_fst_position_y, right_fst_position_c; + u32 right_y_crop_total; + u32 right_c_crop_total; + + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (cfg->output[i].enable == 0) + continue; + params = &ofl->unite_params[i]; + params->y_w_fac = (cfg->output[i].crop_width - 1) * 4096 / + (cfg->output[i].scl_width - 1); + params->c_w_fac = (cfg->output[i].crop_width / 2 - 1) * 4096 / + (cfg->output[i].scl_width / 2 - 1); + params->y_h_fac = (cfg->output[i].crop_height - 1) * 4096 / + (cfg->output[i].scl_height - 1); + params->c_h_fac = (cfg->output[i].crop_height - 1) * 4096 / + (cfg->output[i].scl_height - 1); + + right_fst_position_y = cfg->output[i].scl_width / 2 * + params->y_w_fac; + right_fst_position_c = cfg->output[i].scl_width / 2 / 2 * + params->c_w_fac; + + left_in_used_size_y = right_fst_position_y >> 12; + left_in_used_size_c = (right_fst_position_c >> 12) * 2; + + params->y_w_phase = right_fst_position_y & 0xfff; + params->c_w_phase = right_fst_position_c & 0xfff; + + right_scl_need_size_y = cfg->output[i].crop_width - + left_in_used_size_y; + params->right_scl_need_size_y = right_scl_need_size_y; + right_scl_need_size_c = cfg->output[i].crop_width - + left_in_used_size_c; + params->right_scl_need_size_c = right_scl_need_size_c; + + if (i == 0 && cfg->output[i].crop_width != cfg->output[i].scl_width) { + right_y_crop_total = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + right_scl_need_size_y - 3; + right_c_crop_total = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + right_scl_need_size_c - 6; + } else { + right_y_crop_total = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + right_scl_need_size_y; + right_c_crop_total = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + right_scl_need_size_c; + } + + params->quad_crop_w = ALIGN_DOWN(min(right_y_crop_total, right_c_crop_total), 2); + + params->scl_in_crop_w_y = right_y_crop_total - params->quad_crop_w; + params->scl_in_crop_w_c = right_c_crop_total - params->quad_crop_w; + + if (rkvpss_debug >= 2) { + v4l2_info(&ofl->v4l2_dev, + "%s dev_id:%d seq:%d ch:%d y_w_fac:%u c_w_fac:%u y_h_fac:%u c_h_fac:%u\n", + __func__, cfg->dev_id, cfg->sequence, i, params->y_w_fac, + params->c_w_fac, params->y_h_fac, params->c_h_fac); + v4l2_info(&ofl->v4l2_dev, + "\t%s unite_right_enlarge:%u", + __func__, ofl->unite_right_enlarge); + v4l2_info(&ofl->v4l2_dev, + "\t%s y_w_phase:%u c_w_phase:%u quad_crop_w:%u scl_in_crop_w_y:%u scl_in_crop_w_c:%u\n", + __func__, params->y_w_phase, params->c_w_phase, + params->quad_crop_w, + params->scl_in_crop_w_y, params->scl_in_crop_w_c); + v4l2_info(&ofl->v4l2_dev, + "\t%s right_scl_need_size_y:%u right_scl_need_size_c:%u\n", + __func__, params->right_scl_need_size_y, + params->right_scl_need_size_c); + } + } +} + +static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + u32 update, val, mask; + ktime_t t = 0; + s64 us = 0; + int ret, i; + u64 ns; + bool left_tmp; + + if (rkvpss_debug >= 2) { + v4l2_info(&ofl->v4l2_dev, + "%s dev_id:%d seq:%d mirror:%d input:%dx%d buffd:%d format:%c%c%c%c\n", + __func__, cfg->dev_id, cfg->sequence, cfg->mirror, + cfg->input.width, cfg->input.height, cfg->input.buf_fd, + cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24); + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!cfg->output[i].enable) + continue; + v4l2_info(&ofl->v4l2_dev, + "\t\t\tch%d crop:(%d,%d)/%dx%d scl:%dx%d flip:%d buffd:%d format:%c%c%c%c\n", + i, cfg->output[i].crop_h_offs, cfg->output[i].crop_v_offs, + cfg->output[i].crop_width, cfg->output[i].crop_height, + cfg->output[i].scl_width, cfg->output[i].scl_height, + cfg->output[i].flip, cfg->output[i].buf_fd, + cfg->output[i].format, cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24); + } + t = ktime_get(); + } + + if (!unite || left) + add_cfginfo(ofl, cfg); + + ns = ktime_get_ns(); + ofl->dev_rate[cfg->dev_id].in_rate = ns - ofl->dev_rate[cfg->dev_id].in_timestamp; + ofl->dev_rate[cfg->dev_id].in_timestamp = ns; + + init_completion(&ofl->cmpl); + ofl->mode_sel_en = false; + + ret = read_config(file, cfg, unite, left); + if (ret < 0) + return ret; + + if (unite && cfg->mirror) + left_tmp = !left; + else + left_tmp = left; + + cmsc_config(ofl, cfg, unite, left_tmp); + crop_config(file, cfg, unite, left_tmp); + scale_config(file, cfg, unite, left_tmp); + if (!unite) + aspt_config(file, cfg); + ret = write_config(file, cfg, unite, left_tmp); + if (ret < 0) + return ret; mask = 0; val = 0; @@ -1182,9 +1596,22 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) } update |= RKVPSS_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD | RKVPSS_MIR_GEN_UPD; rkvpss_hw_write(hw, RKVPSS_VPSS_UPDATE, update); - rkvpss_hw_set_bits(hw, RKVPSS_VPSS_IMSC, 0, RKVPSS_ALL_FRM_END); + if (rkvpss_debug >=5) { + v4l2_info(&ofl->v4l2_dev, + "%s dev_id%d unite:%d left:%d\n", + __func__, cfg->dev_id, unite, left); + for (i = 0; i < 16128; i += 16) { + printk("%04x: %08x %08x %08x %08x\n", + i, + rkvpss_hw_read(hw, i), + rkvpss_hw_read(hw, i + 4), + rkvpss_hw_read(hw, i + 8), + rkvpss_hw_read(hw, i + 12)); + } + } + rkvpss_hw_write(hw, RKVPSS_MI_RD_START, RKVPSS_MI_RD_ST); ret = wait_for_completion_timeout(&ofl->cmpl, msecs_to_jiffies(500)); @@ -1194,6 +1621,7 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) } else { ret = 0; } + if (rkvpss_debug >= 2) { us = ktime_us_delta(ktime_get(), t); v4l2_info(&ofl->v4l2_dev, @@ -1208,15 +1636,6 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) ofl->dev_rate[cfg->dev_id].in_timestamp; return ret; -free_buf: - for (i -= 1; i >= 0; i--) { - if (!cfg->output[i].enable) - continue; - buf_del(file, cfg->dev_id, cfg->output[i].buf_fd, false, true); - } - buf_del(file, cfg->dev_id, cfg->input.buf_fd, false, true); -err: - return -ENOMEM; } static int rkvpss_module_get(struct file *file, @@ -1276,10 +1695,289 @@ unlock: return ret; } +static int rkvpss_check_params(struct file *file, struct rkvpss_frame_cfg *cfg, bool *unite) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + int i, ret = 0, tile_num = 0; + + /* set unite mode */ + if (cfg->input.width > RKVPSS_MAX_WIDTH) + *unite = true; + else + *unite = false; + + /* check input format */ + switch (cfg->input.format) { + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_XRGB32: + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_RGBX32: + case V4L2_PIX_FMT_BGRX32: + case V4L2_PIX_FMT_FBC0: + case V4L2_PIX_FMT_FBC2: + case V4L2_PIX_FMT_FBC4: + case V4L2_PIX_FMT_TILE420: + case V4L2_PIX_FMT_TILE422: + break; + default: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support input format:%c%c%c%c\n", + cfg->dev_id, cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24); + ret = -EINVAL; + goto end; + } + + /* check input size */ + if (cfg->input.width > RKVPSS_UNITE_MAX_WIDTH || + cfg->input.height > RKVPSS_UNITE_MAX_HEIGHT || + cfg->input.width < RKVPSS_MIN_WIDTH || + cfg->input.height < RKVPSS_MIN_HEIGHT) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d input size not support width:%d height:%d\n", + cfg->dev_id, cfg->input.width, cfg->input.height); + ret = -EINVAL; + goto end; + } + + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!cfg->output[i].enable) + continue; + /* check output format */ + switch (cfg->output[i].format) { + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV21: + break; + case V4L2_PIX_FMT_TILE420: + case V4L2_PIX_FMT_TILE422: + if (i == RKVPSS_OUTPUT_CH0 || i == RKVPSS_OUTPUT_CH1) { + tile_num++; + if (tile_num > 1) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d only ch0 or ch1 can tile write\n", + cfg->dev_id); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].flip) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d tile write no support flip\n", + cfg->dev_id, i); + ret = -EINVAL; + goto end; + } + } else { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", + cfg->dev_id, i, cfg->output[i].format, cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24); + ret = -EINVAL; + goto end; + } + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_XRGB32: + if (i != RKVPSS_OUTPUT_CH1) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", + cfg->dev_id, i, cfg->output[i].format, cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24); + ret = -EINVAL; + goto end; + } + break; + default: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", + cfg->dev_id, i, cfg->output[i].format, cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24); + ret = -EINVAL; + goto end; + } + + /* check output size */ + if (cfg->output[i].scl_width > RKVPSS_UNITE_MAX_WIDTH || + cfg->output[i].scl_height > RKVPSS_UNITE_MAX_HEIGHT || + cfg->output[i].scl_width < RKVPSS_MIN_WIDTH || + cfg->output[i].scl_height < RKVPSS_MIN_HEIGHT) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d output size not support width:%d height:%d\n", + cfg->dev_id, cfg->output[i].scl_width, cfg->output[i].scl_height); + ret = -EINVAL; + goto end; + } + + /* check crop */ + cfg->output[i].crop_h_offs = ALIGN(cfg->output[i].crop_h_offs, 2); + cfg->output[i].crop_v_offs = ALIGN(cfg->output[i].crop_v_offs, 2); + cfg->output[i].crop_width = ALIGN(cfg->output[i].crop_width, 2); + cfg->output[i].crop_height = ALIGN(cfg->output[i].crop_height, 2); + if (!cfg->input.rotate || cfg->input.rotate == 2) { + if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > cfg->input.width) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d inval crop(offs:%d w:%d) input width:%d\n", + i, cfg->dev_id, cfg->output[i].crop_h_offs, + cfg->output[i].crop_width, cfg->input.width); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > cfg->input.height) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d inval crop(offs:%d h:%d) input height:%d\n", + cfg->dev_id, i, cfg->output[i].crop_v_offs, + cfg->output[i].crop_height, cfg->input.height); + ret = -EINVAL; + goto end; + } + } else { + if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > cfg->input.height) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d rotate inval crop(offs:%d w:%d) input height:%d\n", + i, cfg->dev_id, cfg->output[i].crop_h_offs, + cfg->output[i].crop_width, cfg->input.height); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > cfg->input.width) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d rotate inval crop(offs:%d h:%d) input width:%d\n", + cfg->dev_id, i, cfg->output[i].crop_v_offs, + cfg->output[i].crop_height, cfg->input.width); + ret = -EINVAL; + goto end; + } + } + if (*unite) { + if (cfg->output[i].crop_h_offs != (cfg->input.width - + (cfg->output[i].crop_h_offs + + cfg->output[i].crop_width))) { + v4l2_err(&ofl->v4l2_dev, " dev_id:%d ch%d unite crop_v need centered crop(h_offs:%d w:%d) input width:%d\n", + cfg->dev_id, i, cfg->output[i].crop_h_offs, + cfg->output[i].crop_width, cfg->input.width); + ret = -EINVAL; + goto end; + } + } + + /* check scale */ + if (i == RKVPSS_OUTPUT_CH2 || i == RKVPSS_OUTPUT_CH3) { + if (cfg->output[i].crop_width != cfg->output[i].scl_width && + cfg->output[i].crop_height != cfg->output[i].scl_height) { + if ((!*unite && cfg->output[i].scl_width > 1920) || + (*unite && cfg->output[i].scl_width > 1920 * 2)) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d single scale max width 1920\n", + cfg->dev_id, i); + ret = -EINVAL; + goto end; + } + } + } + } + + /* check rotate */ + switch (cfg->input.rotate) { + case ROTATE_90: + case ROTATE_180: + case ROTATE_270: + if (cfg->input.format != V4L2_PIX_FMT_TILE420 && + cfg->input.format != V4L2_PIX_FMT_TILE422) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d input format:%c%c%c%c not support rotate\n", + cfg->dev_id, cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24); + ret = -EINVAL; + goto end; + } + break; + default: + break; + } + + /** unite constraints **/ + if (*unite) { + if (cfg->input.format == V4L2_PIX_FMT_FBC0 || + cfg->input.format == V4L2_PIX_FMT_FBC2 || + cfg->input.format == V4L2_PIX_FMT_FBC4 || + cfg->input.format == V4L2_PIX_FMT_TILE420 || + cfg->input.format == V4L2_PIX_FMT_TILE422) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support input this format:%c%c%c%c\n", + cfg->dev_id, cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24); + ret = -EINVAL; + goto end; + } + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!cfg->output[i].enable) + continue; + if (cfg->output[i].format != V4L2_PIX_FMT_NV12 && + cfg->output[i].format != V4L2_PIX_FMT_NV16 && + cfg->output[i].format != V4L2_PIX_FMT_NV21 && + cfg->output[i].format != V4L2_PIX_FMT_NV61) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support output this format:%c%c%c%c\n", + cfg->dev_id, cfg->output[i].format, cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].scl_width > cfg->input.width) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite horizontal no support scale up\n", + cfg->dev_id); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].aspt.enable) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support aspt\n", + cfg->dev_id); + ret = -EINVAL; + goto end; + } + } + } + +end: + return ret; +} + +static int rkvpss_prepare_run(struct file *file, struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + int ret = 0; + bool unite; + + ret = rkvpss_check_params(file, cfg, &unite); + if (ret < 0) + goto end; + + if (!unite) { + ret = rkvpss_ofl_run(file, cfg, false, false); + if (ret < 0) + goto end; + } else { + calc_unite_scl_params(file, cfg); + ret = rkvpss_ofl_run(file, cfg, true, true); + if (ret < 0) { + v4l2_err(&ofl->v4l2_dev, "unite left error\n"); + goto end; + } + ret = rkvpss_ofl_run(file, cfg, true, false); + if (ret < 0) { + v4l2_err(&ofl->v4l2_dev, "unite right error\n"); + goto end; + } + } + +end: + return ret; +} + static long rkvpss_ofl_ioctl(struct file *file, void *fh, bool valid_prio, unsigned int cmd, void *arg) { long ret = 0; + bool unite; if (!arg) return -EINVAL; @@ -1292,7 +1990,7 @@ static long rkvpss_ofl_ioctl(struct file *file, void *fh, ret = rkvpss_module_get(file, arg); break; case RKVPSS_CMD_FRAME_HANDLE: - ret = rkvpss_ofl_run(file, arg); + ret = rkvpss_prepare_run(file, arg); break; case RKVPSS_CMD_BUF_ADD: ret = rkvpss_ofl_buf_add(file, arg); @@ -1300,6 +1998,9 @@ static long rkvpss_ofl_ioctl(struct file *file, void *fh, case RKVPSS_CMD_BUF_DEL: rkvpss_ofl_buf_del(file, arg); break; + case RKVPSS_CMD_CHECKPARAMS: + ret = rkvpss_check_params(file, arg, &unite); + break; default: ret = -EFAULT; } diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.h b/drivers/media/platform/rockchip/vpss/vpss_offline.h index bbb3b801ec0b..72c9c0446eda 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.h +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.h @@ -4,6 +4,8 @@ #ifndef _RKVPSS_OFFLINE_H #define _RKVPSS_OFFLINE_H #define DEV_NUM_MAX 10 +#define UNITE_ENLARGE 16 +#define UNITE_LEFT_ENLARGE 16 #include "hw.h" @@ -45,6 +47,20 @@ struct rkvpss_dev_rate { u32 delay; }; +struct rkvpss_unite_scl_params { + u32 y_w_fac; + u32 c_w_fac; + u32 y_h_fac; + u32 c_h_fac; + u32 y_w_phase; + u32 c_w_phase; + u32 quad_crop_w; + u32 scl_in_crop_w_y; + u32 scl_in_crop_w_c; + u32 right_scl_need_size_y; + u32 right_scl_need_size_c; +}; + struct rkvpss_offline_dev { struct rkvpss_hw_dev *hw; struct v4l2_device v4l2_dev; @@ -56,6 +72,8 @@ struct rkvpss_offline_dev { struct list_head cfginfo_list; struct mutex ofl_lock; struct rkvpss_dev_rate dev_rate[DEV_NUM_MAX]; + struct rkvpss_unite_scl_params unite_params[RKVPSS_OUTPUT_MAX]; + u32 unite_right_enlarge; bool mode_sel_en; }; diff --git a/include/uapi/linux/rk-vpss-config.h b/include/uapi/linux/rk-vpss-config.h index 4973433ff2e1..ada6abeb9c64 100644 --- a/include/uapi/linux/rk-vpss-config.h +++ b/include/uapi/linux/rk-vpss-config.h @@ -65,6 +65,9 @@ #define RKVPSS_CMD_MODULE_GET \ _IOR('V', BASE_VIDIOC_PRIVATE + 54, struct rkvpss_module_sel) +#define RKVPSS_CMD_CHECKPARAMS \ + _IOW('V', BASE_VIDIOC_PRIVATE + 55, struct rkvpss_frame_cfg) + /********************************************************************/ /* struct rkvpss_mirror_flip From ea6192a78167c2e261bad113ffc23326bdf76bec Mon Sep 17 00:00:00 2001 From: Yandong Lin Date: Thu, 13 Jun 2024 16:20:34 +0800 Subject: [PATCH 06/19] video: rockchip: mpp: rk3576: fix enc err when rec_fbc_dis=1 Writing reg 0x300 BIT(31) may cause the DMA module to falsely trigger writing data. It will case enc err. So we need to disable the core clock before writing reg 0x300, and re-enable the core clock after writing reg 0x300. Change-Id: Ib385b3aa120533cd42b70b548120d219ceaf4fb5 Signed-off-by: Yandong Lin --- drivers/video/rockchip/mpp/mpp_rkvenc2.c | 30 +++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/video/rockchip/mpp/mpp_rkvenc2.c b/drivers/video/rockchip/mpp/mpp_rkvenc2.c index 1c83aae1f4b5..08f9c1cce1c9 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvenc2.c +++ b/drivers/video/rockchip/mpp/mpp_rkvenc2.c @@ -189,6 +189,7 @@ union rkvenc2_dual_core_handshake_id { #define RKVENC2_BIT_VAL_H264 0 #define RKVENC2_BIT_VAL_H265 1 #define RKVENC2_BIT_SLEN_FIFO BIT(30) +#define RKVENC2_BIT_REC_FBC_DIS BIT(31) #define RKVENC2_REG_SLI_SPLIT (56) #define RKVENC510_REG_SLI_SPLIT (60) @@ -270,6 +271,7 @@ struct rkvenc_task { /* jpege bitstream */ struct mpp_dma_buffer *bs_buf; u32 offset_bs; + u32 rec_fbc_dis; }; #define RKVENC_MAX_RCB_NUM (4) @@ -1112,6 +1114,16 @@ static void *rkvenc_alloc_task(struct mpp_session *session, task->clk_mode = CLK_MODE_NORMAL; rkvenc2_check_split_task(mpp, task); + /* check whether the current task is rec_fbc_dis = 1 */ + if (task->hw_info->vepu_type == RKVENC_VEPU_510) { + if (task->reg[RKVENC_CLASS_PIC].valid) { + u32 *reg = task->reg[RKVENC_CLASS_PIC].data; + + task->rec_fbc_dis = reg[RKVENC510_REG_ENC_PIC] & RKVENC2_BIT_REC_FBC_DIS; + reg[RKVENC510_REG_ENC_PIC] &= ~(RKVENC2_BIT_REC_FBC_DIS); + } + } + mpp_debug_leave(); return mpp_task; @@ -1423,6 +1435,9 @@ static int rkvenc_run(struct mpp_dev *mpp, struct mpp_task *mpp_task) mpp_task_run_begin(mpp_task, timing_en, MPP_WORK_TIMEOUT_DELAY); if (hw->vepu_type == RKVENC_VEPU_510) { + u32 rec_fbc_dis = task->rec_fbc_dis; + u32 enc_pic = mpp_read(mpp, 0x300); + /* * Config dvbm special reg to expected that * vepu will hold when the encoding finish. @@ -1431,7 +1446,20 @@ static int rkvenc_run(struct mpp_dev *mpp, struct mpp_task *mpp_task) mpp_write(mpp, 0x308, BIT(18) | BIT(16)); /* Enable slice done interrupt and slice fifo info. */ mpp_write(mpp, 0x20, mpp_read(mpp, 0x20) | BIT(3)); - mpp_write(mpp, 0x300, mpp_read(mpp, 0x300) | BIT(30)); + /* + * Fix bug: + * Writing reg 0x300 BIT(31) may cause the DMA module to falsely + * trigger writing data. It will case enc err. + * So we need to disable the core clock before writing reg 0x300, + * and re-enable the core clock after writing reg 0x300. + */ + if (rec_fbc_dis) { + mpp_clk_safe_disable(enc->core_clk_info.clk); + mpp_write(mpp, 0x300, enc_pic | BIT(30) | BIT(31)); + mpp_clk_safe_enable(enc->core_clk_info.clk); + } else { + mpp_write(mpp, 0x300, enc_pic | BIT(30)); + } } /* Flush the register before the start the device */ From d4f217f5327136ed1709688dbe46d38cc7b1b4d5 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Tue, 11 Jun 2024 10:42:39 +0800 Subject: [PATCH 07/19] drm/rockchip: analogix_dp: fix the log to indicate edp data source The log may be like: vop2/vop3: [ 21.047010] rockchip-dp 27dc0000.edp: [drm:rockchip_dp_drm_encoder_enable] vop@27d00000 vp1 output to edp vopb/vopl: [ 5.900240] rockchip-dp 27dc0000.edp: [drm:rockchip_dp_drm_encoder_enable] vop@27900000 vopl output to edp Change-Id: I940cb881481e8e86ad804b7b4a7f3453123f0f9f Signed-off-by: Damon Ding --- drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 8f082dc06058..323f4960b64c 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -383,6 +383,9 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder, struct rockchip_dp_device *dp = encoder_to_dp(encoder); struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; + struct of_endpoint endpoint; + struct device_node *remote_port_parent; + char name[32]; int ret; crtc = rockchip_dp_drm_get_new_crtc(encoder, state); @@ -398,11 +401,21 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder, if (ret != 0) DRM_DEV_ERROR(dp->dev, "Could not write to GRF reg mem_clk_auto_gating: %d\n", ret); - ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder); + ret = drm_of_encoder_active_endpoint(dp->dev->of_node, encoder, &endpoint); if (ret < 0) return; - DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG"); + remote_port_parent = of_graph_get_remote_port_parent(endpoint.local_node); + if (remote_port_parent) { + if (of_get_child_by_name(remote_port_parent, "ports")) + sprintf(name, "%s vp%d", remote_port_parent->full_name, endpoint.id); + else + sprintf(name, "%s %s", + remote_port_parent->full_name, endpoint.id ? "vopl" : "vopb"); + of_node_put(remote_port_parent); + + DRM_DEV_DEBUG(dp->dev, "%s output to edp\n", name); + } ret = rockchip_grf_field_write(dp->grf, &dp->data->lcdc_sel, ret); if (ret != 0) From a16ad1ba4fb6214007ef2634c33aae58b7f67c88 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Thu, 13 Jun 2024 16:44:01 +0800 Subject: [PATCH 08/19] media: i2c: lontium driver: remove judge timing valid Signed-off-by: Jianwei Fan Change-Id: Iec8dc38580f5f008caeebf66bcc137aa51dd30a0 --- drivers/media/i2c/lt6911uxc.c | 6 ------ drivers/media/i2c/lt7911d.c | 6 ------ drivers/media/i2c/lt7911uxc.c | 6 ------ drivers/media/i2c/lt8668sx.c | 6 ------ 4 files changed, 24 deletions(-) diff --git a/drivers/media/i2c/lt6911uxc.c b/drivers/media/i2c/lt6911uxc.c index 6584f395172a..b981753329b8 100644 --- a/drivers/media/i2c/lt6911uxc.c +++ b/drivers/media/i2c/lt6911uxc.c @@ -739,12 +739,6 @@ static int lt6911uxc_s_dv_timings(struct v4l2_subdev *sd, return 0; } - if (!v4l2_valid_dv_timings(timings, - <6911uxc_timings_cap, NULL, NULL)) { - v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); - return -ERANGE; - } - lt6911uxc->timings = *timings; enable_stream(sd, false); diff --git a/drivers/media/i2c/lt7911d.c b/drivers/media/i2c/lt7911d.c index 95cf4c2008b8..041dd636d315 100644 --- a/drivers/media/i2c/lt7911d.c +++ b/drivers/media/i2c/lt7911d.c @@ -654,12 +654,6 @@ static int lt7911d_s_dv_timings(struct v4l2_subdev *sd, return 0; } - if (!v4l2_valid_dv_timings(timings, - <7911d_timings_cap, NULL, NULL)) { - v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); - return -ERANGE; - } - lt7911d->timings = *timings; enable_stream(sd, false); diff --git a/drivers/media/i2c/lt7911uxc.c b/drivers/media/i2c/lt7911uxc.c index 57bd05013e6e..30badec9048b 100644 --- a/drivers/media/i2c/lt7911uxc.c +++ b/drivers/media/i2c/lt7911uxc.c @@ -925,12 +925,6 @@ static int lt7911uxc_s_dv_timings(struct v4l2_subdev *sd, return 0; } - if (!v4l2_valid_dv_timings(timings, - <7911uxc_timings_cap, NULL, NULL)) { - v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); - return -ERANGE; - } - lt7911uxc->timings = *timings; enable_stream(sd, false); diff --git a/drivers/media/i2c/lt8668sx.c b/drivers/media/i2c/lt8668sx.c index 13bd338c8100..8f2ef0809d3b 100644 --- a/drivers/media/i2c/lt8668sx.c +++ b/drivers/media/i2c/lt8668sx.c @@ -892,12 +892,6 @@ static int lt8668sx_s_dv_timings(struct v4l2_subdev *sd, return 0; } - if (!v4l2_valid_dv_timings(timings, - <8668sx_timings_cap, NULL, NULL)) { - v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); - return -ERANGE; - } - lt8668sx->timings = *timings; lt8668sx->last_framerate = lt8668sx->cur_framerate; From 60e92c4bf274a823c5ce4aa55de65289eec54603 Mon Sep 17 00:00:00 2001 From: Frank Wang Date: Fri, 14 Jun 2024 14:11:34 +0800 Subject: [PATCH 09/19] phy: rockchip: usbdp: amend ssc modulation deviation Move SSC modulation deviation into private config of clock. - 24M: 0x00D4[5:0] = 0x30 - 26M: 0x00D4[5:0] = 0x33 Signed-off-by: Frank Wang Change-Id: I6bd1e4352f85a6e12630cc4034fac49a9749627c --- drivers/phy/rockchip/phy-rockchip-usbdp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c index 3d448cb3d2d0..39589e14c008 100644 --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c @@ -336,7 +336,8 @@ static const struct reg_sequence udphy_24m_refclk_cfg[] = { {0x0a64, 0xa8}, {0x1a3c, 0xd0}, {0x1a44, 0xd0}, {0x1a48, 0x01}, {0x1a4c, 0x0d}, {0x1a54, 0xe0}, - {0x1a5c, 0xe0}, {0x1a64, 0xa8} + {0x1a5c, 0xe0}, {0x1a64, 0xa8}, + {0x00D4, 0x30} }; static const struct reg_sequence udphy_26m_refclk_cfg[] = { @@ -363,7 +364,7 @@ static const struct reg_sequence udphy_26m_refclk_cfg[] = { {0x0c30, 0x0E}, {0x0C48, 0x06}, {0x1C30, 0x0E}, {0x1C48, 0x06}, {0x028C, 0x18}, {0x0AF0, 0x00}, - {0x1AF0, 0x00} + {0x1AF0, 0x00}, {0x00D4, 0x33} }; static const struct reg_sequence udphy_init_sequence[] = { @@ -398,8 +399,7 @@ static const struct reg_sequence udphy_init_sequence[] = { {0x0070, 0x7D}, {0x0074, 0x68}, {0x0AF4, 0x1A}, {0x1AF4, 0x1A}, {0x0440, 0x3F}, {0x10D4, 0x08}, - {0x20D4, 0x08}, {0x00D4, 0x30}, - {0x0024, 0x6e}, + {0x20D4, 0x08}, {0x0024, 0x6e} }; static inline int grfreg_write(struct regmap *base, From 3353ec981a37de4b45369417ce396c975849f6b7 Mon Sep 17 00:00:00 2001 From: Rimon Xu Date: Fri, 14 Jun 2024 17:55:26 +0800 Subject: [PATCH 10/19] media: rockchip: vpss: fix offline device id out of range [ 27.785304] Unable to handle kernel paging request at virtual address 002d737370766bee [ 28.258589] Mem abort info: [ 28.258841] ESR = 0x0000000096000004 [ 28.259169] EC = 0x25: DABT (current EL), IL = 32 bits [ 28.259637] SET = 0, FnV = 0 [ 28.259911] EA = 0, S1PTW = 0 [ 28.260184] FSC = 0x04: level 0 translation fault [ 28.260610] Data abort info: [ 28.260861] ISV = 0, ISS = 0x00000004 [ 28.261200] CM = 0, WnR = 0 [ 28.261462] [002d737370766bee] address between user and kernel address ranges [ 28.262085] Internal error: Oops: 0000000096000004 [#1] SMP [ 28.262576] Modules linked in: [ 28.262852] CPU: 0 PID: 1064 Comm: vo_render_0 Not tainted 6.1.75 #37 [ 28.263420] Hardware name: Rockchip RK3576 EVB2 V10 Linux Board (DT) [ 28.263975] pstate: 800000c5 (Nzcv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 28.264587] pc : clk_core_disable+0x1c/0x1ec [ 28.264974] lr : clk_disable+0x34/0x50 [ 28.265303] sp : ffffffc00aedb8f0 [ 28.265599] x29: ffffffc00aedb8f0 x28: 0000000000000009 x27: 0000007f8322c290 [ 28.266224] x26: 0000000000000000 x25: 0000000000000000 x24: ffffff80c3bd4040 [ 28.266848] x23: ffffff80c038e068 x22: ffffffc009951ca0 x21: 0000000000000005 [ 28.267473] x20: 762d737370766b72 x19: 762d737370766b72 x18: 0000000000000030 [ 28.268097] x17: 2076656420323435 x16: 323239206d756e5f x15: 6b6c632039666534 [ 28.268721] x14: 6465353630303030 x13: 6633366362613364 x12: 3030303030303030 [ 28.269344] x11: 0000000000144580 x10: 0000000000144550 x9 : ffffffc0086ed044 [ 28.269969] x8 : ffffffc00a2141f8 x7 : ffffffc00a2c41f8 x6 : 0000000000000001 [ 28.270593] x5 : ffffff80fb529b60 x4 : ffffffc00a60aa30 x3 : 0000000000000000 [ 28.271216] x2 : 0000000000000001 x1 : ffffff80c0fa0000 x0 : 762d737370766b72 [ 28.271840] Call trace: [ 28.272060] clk_core_disable+0x1c/0x1ec [ 28.272411] clk_disable+0x34/0x50 [ 28.272718] rkvpss_runtime_suspend+0xac/0xd8 [ 28.273104] pm_generic_runtime_suspend+0x30/0x44 [ 28.273521] genpd_runtime_suspend+0xb8/0x2f4 [ 28.273906] __rpm_callback+0x4c/0x200 [ 28.274236] rpm_callback+0x6c/0x80 [ 28.274544] rpm_suspend+0xd4/0x78c [ 28.274852] rpm_idle+0x1a4/0x3b0 [ 28.275149] __pm_runtime_idle+0x64/0x1a0 [ 28.275501] ofl_release+0x78/0x94 [ 28.275810] v4l2_release+0xbc/0xf0 [ 28.276117] __fput+0xc0/0x250 [ 28.276393] ____fput+0x14/0x1c [ 28.276668] task_work_run+0xb8/0x170 [ 28.276997] do_exit+0x2d4/0x924 [ 28.277283] do_group_exit+0x38/0xa0 [ 28.277600] get_signal+0x998/0x9c0 [ 28.277908] do_notify_resume+0x180/0xde0 [ 28.278261] el0_svc+0x6c/0x80 [ 28.278535] el0t_64_sync_handler+0xb0/0xb4 [ 28.278907] el0t_64_sync+0x158/0x15c [ 28.279235] [ 28.279235] PC: 0xffffffc0086ece40: Signed-off-by: Rimon Xu Change-Id: I39d709ed8526b91c67d4181d51bad670edf9c88a --- drivers/media/platform/rockchip/vpss/vpss_offline.c | 8 ++++++++ drivers/media/platform/rockchip/vpss/vpss_offline.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.c b/drivers/media/platform/rockchip/vpss/vpss_offline.c index b71b90979200..0f77c29744f6 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.c +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.c @@ -1700,6 +1700,14 @@ static int rkvpss_check_params(struct file *file, struct rkvpss_frame_cfg *cfg, struct rkvpss_offline_dev *ofl = video_drvdata(file); int i, ret = 0, tile_num = 0; + /* check dev id out of range */ + if (cfg->dev_id >= DEV_NUM_MAX) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d is out of range. range[0, %d]\n", + cfg->dev_id, DEV_NUM_MAX); + ret = -EINVAL; + goto end; + } + /* set unite mode */ if (cfg->input.width > RKVPSS_MAX_WIDTH) *unite = true; diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.h b/drivers/media/platform/rockchip/vpss/vpss_offline.h index 72c9c0446eda..8632e3f18b18 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.h +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.h @@ -3,7 +3,7 @@ #ifndef _RKVPSS_OFFLINE_H #define _RKVPSS_OFFLINE_H -#define DEV_NUM_MAX 10 +#define DEV_NUM_MAX 256 #define UNITE_ENLARGE 16 #define UNITE_LEFT_ENLARGE 16 From 6d93380186c8299fd27f29aa03ca92a2f43666f3 Mon Sep 17 00:00:00 2001 From: Frank Wang Date: Mon, 19 Feb 2024 10:58:33 +0800 Subject: [PATCH 11/19] phy: rockchip: inno-usb2: Add refclock freq setting This adds 24/26 MHz reference clock frequency setting for rk3576 soc with SNPS USB2 PHY IP. Signed-off-by: Frank Wang Signed-off-by: William Wu Change-Id: I712ab4cb630db9a0cccb4174ce59ef6dbafe2df0 --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index c1c1a79e54c5..a2cd50df8859 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -217,6 +217,9 @@ struct rockchip_usb2phy_port_cfg { * @ls_filter_con: set linestate filter time. * @port_cfgs: usb-phy port configurations. * @ls_filter_con: set linestate filter time. + * @refclk_fsel: reference clock frequency select, + * true - select 24 MHz + * false - select 26 MHz * @chg_det: charger detection registers. */ struct rockchip_usb2phy_cfg { @@ -229,6 +232,7 @@ struct rockchip_usb2phy_cfg { struct usb2phy_reg clkout_ctl; struct usb2phy_reg clkout_ctl_phy; struct usb2phy_reg ls_filter_con; + struct usb2phy_reg refclk_fsel; const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS]; const struct rockchip_chg_det_reg chg_det; }; @@ -580,6 +584,31 @@ err_ret: return ret; } +static int rockchip_usb2phy_refclk_set(struct rockchip_usb2phy *rphy) +{ + struct regmap *base = get_reg_base(rphy); + struct clk *refclk = of_clk_get_by_name(rphy->dev->of_node, "phyclk"); + unsigned long rate; + + /* get phy reference clock */ + rate = clk_get_rate(refclk); + dev_info(rphy->dev, "refclk freq %ld\n", rate); + + switch (rate) { + case 24000000: + property_enable(base, &rphy->phy_cfg->refclk_fsel, true); + break; + case 26000000: + property_enable(base, &rphy->phy_cfg->refclk_fsel, false); + break; + default: + dev_err(rphy->dev, "unsupported refclk freq %ld\n", rate); + return -EINVAL; + } + + return 0; +} + static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) { int ret; @@ -2382,6 +2411,13 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) else rphy->num_clks = ret; + /* Set phy Reference clock frequency */ + if (rphy->phy_cfg->refclk_fsel.enable) { + ret = rockchip_usb2phy_refclk_set(rphy); + if (ret) + return ret; + } + ret = clk_bulk_prepare_enable(rphy->num_clks, rphy->clks); if (ret) return ret; @@ -4009,6 +4045,7 @@ static const struct rockchip_usb2phy_cfg rk3576_phy_cfgs[] = { .phy_tuning = rk3576_usb2phy_tuning, .clkout_ctl = { 0x0008, 0, 0, 1, 0 }, .ls_filter_con = { 0x0020, 19, 0, 0x30100, 0x00020 }, + .refclk_fsel = { 0x0004, 2, 0, 0x6, 0x2 }, .port_cfgs = { [USB2PHY_PORT_OTG] = { .phy_sus = { 0x0000, 8, 0, 0, 0x1d1 }, @@ -4063,6 +4100,7 @@ static const struct rockchip_usb2phy_cfg rk3576_phy_cfgs[] = { .phy_tuning = rk3576_usb2phy_tuning, .clkout_ctl = { 0x2008, 0, 0, 1, 0 }, .ls_filter_con = { 0x2020, 19, 0, 0x30100, 0x00020 }, + .refclk_fsel = { 0x2004, 2, 0, 0x6, 0x2 }, .port_cfgs = { [USB2PHY_PORT_OTG] = { .phy_sus = { 0x2000, 8, 0, 0, 0x1d1 }, From 6ac94745aebe518394e037b9972633e3a7228a4c Mon Sep 17 00:00:00 2001 From: Zhibin Huang Date: Fri, 24 May 2024 13:49:57 +0800 Subject: [PATCH 12/19] misc: rk628: hdmitx: fix rk628d display anomaly when clock frequency is below 40MHz Type: Fix Redmine ID: N/A Associated modifications: N/A Test: N/A Signed-off-by: Zhibin Huang Change-Id: I10124125500668d7d4654a9a19641761ef945d2b --- drivers/misc/rk628/rk628_hdmitx.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/misc/rk628/rk628_hdmitx.c b/drivers/misc/rk628/rk628_hdmitx.c index b5d7ab46a631..d7cea243ae57 100644 --- a/drivers/misc/rk628/rk628_hdmitx.c +++ b/drivers/misc/rk628/rk628_hdmitx.c @@ -677,8 +677,19 @@ static void rk628_hdmi_bridge_mode_set(struct drm_bridge *bridge, struct rk628_display_mode *src = rk628_display_get_src_mode(rk628); struct rk628_display_mode *dst = rk628_display_get_dst_mode(rk628); - /* Store the display mode for plugin/DPMS poweron events */ + /* + * Store the display mode for plugin/DPMS poweron events. rk628d hdmitx + * below (including) 40MHz clock frequency (800x600@60Hz), hsync and + * vsync can only output negative polarity + */ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); + if (rk628->version == RK628D_VERSION && mode->clock <= 40000) { + hdmi->previous_mode.flags &= ~(DRM_MODE_FLAG_PHSYNC | + DRM_MODE_FLAG_PVSYNC); + hdmi->previous_mode.flags |= (DRM_MODE_FLAG_NHSYNC | + DRM_MODE_FLAG_NVSYNC); + } + dst->clock = mode->clock; dst->hdisplay = mode->hdisplay; dst->hsync_start = mode->hsync_start; From ed3f7cfa9bda5c95d9b97a80e196744018a69dfd Mon Sep 17 00:00:00 2001 From: Zhibin Huang Date: Wed, 22 May 2024 12:01:04 +0800 Subject: [PATCH 13/19] misc: rk628: cru: add clk_summary for debugging Type: Function Redmine ID: N/A Associated modifications: N/A Test: # cat /d/rk628/8-0050/clk_summary clock rate --------------------------------- cpll 1188000000 clk_rx_read 594000000 clk_sclk_vop 594000000 clk_bt1120dec 148500000 clk_imodet 49500000 gpll 983039999 clk_hdmirx_aud 98303999 Signed-off-by: Zhibin Huang Change-Id: I6fec2ebafc0995a6007ba79dc9cfa59d3236513d --- drivers/misc/rk628/rk628.c | 1 + drivers/misc/rk628/rk628_cru.c | 291 ++++++++++++++++++++++++--------- drivers/misc/rk628/rk628_cru.h | 2 + 3 files changed, 219 insertions(+), 75 deletions(-) diff --git a/drivers/misc/rk628/rk628.c b/drivers/misc/rk628/rk628.c index 09cf8248b929..15f0ab40ed41 100644 --- a/drivers/misc/rk628/rk628.c +++ b/drivers/misc/rk628/rk628.c @@ -1321,6 +1321,7 @@ static void rk628_debugfs_create(struct rk628 *rk628) rk628_debugfs_register_create(rk628); + rk628_cru_create_debugfs_file(rk628); rk628_rgb_decoder_create_debugfs_file(rk628); rk628_post_process_create_debugfs_file(rk628); rk628_mipi_dsi_create_debugfs_file(rk628); diff --git a/drivers/misc/rk628/rk628_cru.c b/drivers/misc/rk628/rk628_cru.c index 0a15e4264df3..cf1a97177f1c 100644 --- a/drivers/misc/rk628/rk628_cru.c +++ b/drivers/misc/rk628/rk628_cru.c @@ -5,6 +5,8 @@ * Author: Wyon Bi */ +#include + #include "asm-generic/errno-base.h" #include "rk628.h" #include "rk628_cru.h" @@ -273,19 +275,85 @@ static unsigned long rk628_cru_clk_set_rate_pll(struct rk628 *rk628, return (unsigned long)foutpostdiv; } +static int rk628_cru_clk_get_parent_rate(struct rk628 *rk628, unsigned int id, + unsigned int *parent_id, + unsigned long *parent_rate) +{ + u32 val; + int parent = -1; + + switch (id) { + case CGU_CLK_RX_READ: + rk628_i2c_read(rk628, CRU_CLKSEL_CON02, &val); + val &= CLK_RX_READ_SEL_MASK; + val >>= CLK_RX_READ_SEL_SHIFT; + parent = val == CLK_RX_READ_SEL_GPLL ? CGU_CLK_GPLL : CGU_CLK_CPLL; + break; + case CGU_SCLK_VOP: + rk628_i2c_read(rk628, CRU_CLKSEL_CON02, &val); + val &= CLK_UART_SRC_SEL_MASK; + val >>= SCLK_VOP_SEL_SHIFT; + parent = val == SCLK_VOP_SEL_GPLL ? CGU_CLK_GPLL : CGU_CLK_CPLL; + break; + case CGU_CLK_UART_SRC: + rk628_i2c_read(rk628, CRU_CLKSEL_CON21, &val); + val &= SCLK_VOP_SEL_MASK; + parent = val == CLK_UART_SRC_SEL_GPLL ? CGU_CLK_GPLL : CGU_CLK_CPLL; + break; + case CGU_BT1120DEC: + rk628_i2c_read(rk628, CRU_CLKSEL_CON02, &val); + val &= CLK_BT1120DEC_SEL_MASK; + parent = val == CLK_BT1120DEC_SEL_GPLL ? CGU_CLK_GPLL : CGU_CLK_CPLL; + break; + case CGU_CLK_HDMIRX_AUD: + rk628_i2c_read(rk628, CRU_CLKSEL_CON05, &val); + if (rk628->version >= RK628F_VERSION) + val = (val & CLK_HDMIRX_AUD_SEL_MASK_V2) >> 14; + else + val = (val & CLK_HDMIRX_AUD_SEL_MASK_V1) >> 15; + switch (val) { + case 0: + parent = CGU_CLK_CPLL; + break; + case 1: + parent = CGU_CLK_GPLL; + break; + case 2: + parent = CGU_CLK_APLL; + } + break; + case CGU_CLK_IMODET: + rk628_i2c_read(rk628, CRU_CLKSEL_CON05, &val); + val &= CLK_IMODET_SEL_MASK; + val >>= CLK_IMODET_SEL_SHIFT; + parent = val == SCLK_VOP_SEL_GPLL ? CGU_CLK_GPLL : CGU_CLK_CPLL; + break; + default: + return -EINVAL; + } + + if (parent < 0) + return -EINVAL; + + if (parent_id) + *parent_id = parent; + + if (parent_rate) + *parent_rate = rk628_cru_clk_get_rate(rk628, parent); + + return 0; +} + static unsigned long rk628_cru_clk_set_rate_sclk_vop(struct rk628 *rk628, unsigned long rate) { unsigned long m, n, parent_rate; - u32 val; + int ret; - rk628_i2c_read(rk628, CRU_CLKSEL_CON02, &val); - val &= SCLK_VOP_SEL_MASK; - val >>= SCLK_VOP_SEL_SHIFT; - if (val == SCLK_VOP_SEL_GPLL) - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL); - else - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL); + ret = rk628_cru_clk_get_parent_rate(rk628, CGU_SCLK_VOP, + NULL, &parent_rate); + if (ret) + return 0; rational_best_approximation(rate, parent_rate, GENMASK(15, 0), GENMASK(15, 0), @@ -298,15 +366,13 @@ static unsigned long rk628_cru_clk_set_rate_sclk_vop(struct rk628 *rk628, static unsigned long rk628_cru_clk_get_rate_sclk_vop(struct rk628 *rk628) { unsigned long rate, parent_rate, m, n; - u32 mux, div; + u32 div; + int ret; - rk628_i2c_read(rk628, CRU_CLKSEL_CON02, &mux); - mux &= CLK_UART_SRC_SEL_MASK; - mux >>= SCLK_VOP_SEL_SHIFT; - if (mux == SCLK_VOP_SEL_GPLL) - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL); - else - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL); + ret = rk628_cru_clk_get_parent_rate(rk628, CGU_SCLK_VOP, + NULL, &parent_rate); + if (ret) + return 0; rk628_i2c_read(rk628, CRU_CLKSEL_CON13, &div); m = div >> 16 & 0xffff; @@ -319,15 +385,13 @@ static unsigned long rk628_cru_clk_get_rate_sclk_vop(struct rk628 *rk628) static unsigned long rk628_cru_clk_get_rate_clk_imodet(struct rk628 *rk628) { unsigned long rate, parent_rate, n; - u32 mux, div; + u32 div; + int ret; - rk628_i2c_read(rk628, CRU_CLKSEL_CON05, &mux); - mux &= CLK_IMODET_SEL_MASK; - mux >>= CLK_IMODET_SEL_SHIFT; - if (mux == SCLK_VOP_SEL_GPLL) - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL); - else - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL); + ret = rk628_cru_clk_get_parent_rate(rk628, CGU_CLK_IMODET, + NULL, &parent_rate); + if (ret) + return 0; rk628_i2c_read(rk628, CRU_CLKSEL_CON05, &div); n = div & 0x1f; @@ -340,15 +404,12 @@ static unsigned long rk628_cru_clk_set_rate_rx_read(struct rk628 *rk628, unsigned long rate) { unsigned long m, n, parent_rate; - u32 val; + int ret; - rk628_i2c_read(rk628, CRU_CLKSEL_CON02, &val); - val &= CLK_RX_READ_SEL_MASK; - val >>= CLK_RX_READ_SEL_SHIFT; - if (val == CLK_RX_READ_SEL_GPLL) - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL); - else - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL); + ret = rk628_cru_clk_get_parent_rate(rk628, CGU_CLK_RX_READ, + NULL, &parent_rate); + if (ret) + return 0; rational_best_approximation(rate, parent_rate, GENMASK(15, 0), GENMASK(15, 0), @@ -358,17 +419,35 @@ static unsigned long rk628_cru_clk_set_rate_rx_read(struct rk628 *rk628, return rate; } +static unsigned long rk628_cru_clk_get_rate_rx_read(struct rk628 *rk628) +{ + unsigned long rate, m, n, parent_rate; + u32 div; + int ret; + + ret = rk628_cru_clk_get_parent_rate(rk628, CGU_CLK_RX_READ, + NULL, &parent_rate); + if (ret) + return 0; + + rk628_i2c_read(rk628, CRU_CLKSEL_CON14, &div); + m = div >> 16 & 0xffff; + n = div & 0xffff; + rate = parent_rate * m / n; + + return rate; +} + static unsigned long rk628_cru_clk_get_rate_uart_src(struct rk628 *rk628) { unsigned long rate, parent_rate; - u32 mux, div; + u32 div; + int ret; - rk628_i2c_read(rk628, CRU_CLKSEL_CON21, &mux); - mux &= SCLK_VOP_SEL_MASK; - if (mux == CLK_UART_SRC_SEL_GPLL) - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL); - else - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL); + ret = rk628_cru_clk_get_parent_rate(rk628, CGU_CLK_UART_SRC, + NULL, &parent_rate); + if (ret) + return 0; rk628_i2c_read(rk628, CRU_CLKSEL_CON21, &div); div &= CLK_UART_SRC_DIV_MASK; @@ -429,51 +508,34 @@ static unsigned long rk628_cru_clk_set_rate_sclk_hdmirx_aud(struct rk628 *rk628, static unsigned long rk628_cru_clk_get_rate_sclk_hdmirx_aud(struct rk628 *rk628) { - unsigned long rate; - u64 parent_rate; - u8 div; - u32 val; + unsigned long rate, parent_rate; + u32 div; + int ret; + + ret = rk628_cru_clk_get_parent_rate(rk628, CGU_CLK_HDMIRX_AUD, + NULL, &parent_rate); + if (ret) + return 0; + + rk628_i2c_read(rk628, CRU_CLKSEL_CON05, &div); + div = ((div & CLK_HDMIRX_AUD_DIV_MASK) >> 6) + 1; + rate = parent_rate / div; - rk628_i2c_read(rk628, CRU_CLKSEL_CON05, &val); - div = ((val & CLK_HDMIRX_AUD_DIV_MASK) >> 6) + 1; - if (rk628->version >= RK628F_VERSION) - val = (val & CLK_HDMIRX_AUD_SEL_MASK_V2) >> 14; - else - val = (val & CLK_HDMIRX_AUD_SEL_MASK_V1) >> 15; - if (!val) - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL); - else if (val == 2) - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_APLL); - else - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL); - do_div(parent_rate, div); - rate = parent_rate; return rate; } -static unsigned long -rk628_cru_clk_get_rate_bt1120_dec_parent(struct rk628 *rk628) -{ - unsigned long parent_rate; - u32 mux; - - rk628_i2c_read(rk628, CRU_CLKSEL_CON02, &mux); - mux &= CLK_BT1120DEC_SEL_MASK; - if (mux == CLK_BT1120DEC_SEL_GPLL) - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL); - else - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL); - - return parent_rate; -} - static unsigned long rk628_cru_clk_set_rate_bt1120_dec(struct rk628 *rk628, unsigned long rate) { unsigned long parent_rate; u32 div; + int ret; + + ret = rk628_cru_clk_get_parent_rate(rk628, CGU_BT1120DEC, + NULL, &parent_rate); + if (ret) + return 0; - parent_rate = rk628_cru_clk_get_rate_bt1120_dec_parent(rk628); div = DIV_ROUND_UP(parent_rate, rate); rk628_i2c_write(rk628, CRU_CLKSEL_CON02, CLK_BT1120DEC_DIV(div-1)); @@ -484,8 +546,12 @@ static unsigned long rk628_cru_clk_get_rate_bt1120_dec(struct rk628 *rk628) { unsigned long parent_rate; u32 div; + int ret; - parent_rate = rk628_cru_clk_get_rate_bt1120_dec_parent(rk628); + ret = rk628_cru_clk_get_parent_rate(rk628, CGU_BT1120DEC, + NULL, &parent_rate); + if (ret) + return 0; rk628_i2c_read(rk628, CRU_CLKSEL_CON02, &div); div = (div & 0x1f) + 1; @@ -540,6 +606,9 @@ unsigned long rk628_cru_clk_get_rate(struct rk628 *rk628, unsigned int id) case CGU_CLK_GPLL: rate = rk628_cru_clk_get_rate_pll(rk628, id); break; + case CGU_CLK_RX_READ: + rate = rk628_cru_clk_get_rate_rx_read(rk628); + break; case CGU_SCLK_VOP: rate = rk628_cru_clk_get_rate_sclk_vop(rk628); break; @@ -559,6 +628,78 @@ unsigned long rk628_cru_clk_get_rate(struct rk628 *rk628, unsigned int id) return rate; } +static void rk628_cru_show_pll_tree(struct seq_file *s, unsigned int parent_id, + const char *parent_name) +{ + struct rk628 *rk628 = s->private; + unsigned long rate; + unsigned int parent, i; + unsigned int id_list[] = { + CGU_CLK_RX_READ, + CGU_SCLK_VOP, + CGU_BT1120DEC, + CGU_CLK_HDMIRX_AUD, + CGU_CLK_IMODET + }; + char const *id_name[] = { + "clk_rx_read", + "clk_sclk_vop", + "clk_bt1120dec", + "clk_hdmirx_aud", + "clk_imodet" + }; + + if (rk628->version < RK628F_VERSION && parent_id == CGU_CLK_APLL) + return; + + rate = rk628_cru_clk_get_rate(rk628, parent_id); + seq_printf(s, "%-22s %10lu\n", parent_name, rate); + + for (i = 0; i < ARRAY_SIZE(id_list); ++i) { + rk628_cru_clk_get_parent_rate(rk628, id_list[i], &parent, NULL); + if (parent != parent_id) + continue; + rate = rk628_cru_clk_get_rate(rk628, id_list[i]); + seq_printf(s, " %-18s %10lu\n", id_name[i], rate); + } +} + +static int rk628_cru_show_clk_tree(struct seq_file *s, void *data) +{ + unsigned int pll_list[] = {CGU_CLK_CPLL, CGU_CLK_GPLL, CGU_CLK_APLL}; + char const *pll_name[] = {"cpll", "gpll", "apll"}; + unsigned int i; + + seq_printf(s, "%-22s %10s\n", " clock", "rate "); + seq_puts(s, "---------------------------------\n"); + + for (i = 0; i < ARRAY_SIZE(pll_list); ++i) + rk628_cru_show_pll_tree(s, pll_list[i], pll_name[i]); + + return 0; +} + +static int rk628_clk_summary_open(struct inode *inode, struct file *file) +{ + struct rk628 *rk628 = inode->i_private; + + return single_open(file, rk628_cru_show_clk_tree, rk628); +} + +static const struct file_operations rk628_clk_summary_fops = { + .owner = THIS_MODULE, + .open = rk628_clk_summary_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void rk628_cru_create_debugfs_file(struct rk628 *rk628) +{ + debugfs_create_file("clk_summary", 0400, rk628->debug_dir, rk628, + &rk628_clk_summary_fops); +} + void rk628_cru_init(struct rk628 *rk628) { u32 val; diff --git a/drivers/misc/rk628/rk628_cru.h b/drivers/misc/rk628/rk628_cru.h index e13b728eb915..820d1264f970 100644 --- a/drivers/misc/rk628/rk628_cru.h +++ b/drivers/misc/rk628/rk628_cru.h @@ -172,10 +172,12 @@ #define CGU_BT1120DEC 37 #define CGU_SCLK_UART 38 #define CGU_CLK_APLL 39 +#define CGU_CLK_UART_SRC 40 unsigned long rk628_cru_clk_get_rate(struct rk628 *rk628, unsigned int id); int rk628_cru_clk_set_rate(struct rk628 *rk628, unsigned int id, unsigned long rate); void rk628_cru_init(struct rk628 *rk628); +void rk628_cru_create_debugfs_file(struct rk628 *rk628); #endif From f0ba3396b3b696e159a639757269026ab8d4a98a Mon Sep 17 00:00:00 2001 From: Zhibin Huang Date: Fri, 24 May 2024 14:48:35 +0800 Subject: [PATCH 14/19] misc: rk628: cru: clock adjustment logic optimization Type: Function Redmine ID: N/A Associated modifications: N/A Test: N/A Signed-off-by: Zhibin Huang Change-Id: Ieaed642c4bf45b29493af1d358bece83dbcda6c9 --- drivers/misc/rk628/rk628_cru.c | 49 +++++++++++++++++++++++++ drivers/misc/rk628/rk628_cru.h | 1 + drivers/misc/rk628/rk628_hdmirx.c | 9 +---- drivers/misc/rk628/rk628_post_process.c | 15 +------- drivers/misc/rk628/rk628_rgb.c | 14 ------- 5 files changed, 52 insertions(+), 36 deletions(-) diff --git a/drivers/misc/rk628/rk628_cru.c b/drivers/misc/rk628/rk628_cru.c index cf1a97177f1c..ed2364904876 100644 --- a/drivers/misc/rk628/rk628_cru.c +++ b/drivers/misc/rk628/rk628_cru.c @@ -729,3 +729,52 @@ void rk628_cru_init(struct rk628 *rk628) mdelay(1); rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff000b); } + +void rk628_cru_clk_adjust(struct rk628 *rk628) +{ + struct rk628_display_mode *src = &rk628->src_mode; + const struct rk628_display_mode *dst = &rk628->dst_mode; + u64 dst_rate, src_rate; + unsigned long dec_clk_rate; + u32 val; + + /* + * Try to keep cpll frequency close to 1188m (Tested rk628f hdmirx + * scenarios) + */ + if (rk628_input_is_hdmi(rk628) && rk628->version != RK628D_VERSION) { + val = 1188000000UL / (src->clock * 1000); + val *= src->clock * 1000; + rk628_cru_clk_set_rate(rk628, CGU_CLK_CPLL, val); + msleep(50); + dev_info(rk628->dev, "adjust cpll to %uHz", val); + } + + /* + * BT1120 dec clk is a 5-bit integer division, which is inaccurate in + * most resolutions. So if the frequency division is not accurate, apply + * for a fault tolerance of up 2% in frequency setting, so that the + * obtained frequency is slightly higher than the actual required clk, + * so that the deviation between the actual clk and the required clk + * frequency is not significant. + */ + if (rk628_input_is_bt1120(rk628)) { + rk628_cru_clk_set_rate(rk628, CGU_BT1120DEC, src->clock * 1000); + dec_clk_rate = rk628_cru_clk_get_rate(rk628, CGU_BT1120DEC); + if (dec_clk_rate < src->clock * 1000) + rk628_cru_clk_set_rate(rk628, CGU_BT1120DEC, src->clock * 1020); + } + + src_rate = src->clock * 1000; + dst_rate = src_rate * dst->vtotal * dst->htotal; + do_div(dst_rate, (src->vtotal * src->htotal)); + do_div(dst_rate, 1000); + dev_info(rk628->dev, "src %dx%d clock:%d\n", + src->hdisplay, src->vdisplay, src->clock); + + dev_info(rk628->dev, "dst %dx%d clock:%llu\n", + dst->hdisplay, dst->vdisplay, dst_rate); + + rk628_cru_clk_set_rate(rk628, CGU_CLK_RX_READ, src->clock * 1000); + rk628_cru_clk_set_rate(rk628, CGU_SCLK_VOP, dst_rate * 1000); +} diff --git a/drivers/misc/rk628/rk628_cru.h b/drivers/misc/rk628/rk628_cru.h index 820d1264f970..c492d928d112 100644 --- a/drivers/misc/rk628/rk628_cru.h +++ b/drivers/misc/rk628/rk628_cru.h @@ -179,5 +179,6 @@ int rk628_cru_clk_set_rate(struct rk628 *rk628, unsigned int id, unsigned long rate); void rk628_cru_init(struct rk628 *rk628); void rk628_cru_create_debugfs_file(struct rk628 *rk628); +void rk628_cru_clk_adjust(struct rk628 *rk628); #endif diff --git a/drivers/misc/rk628/rk628_hdmirx.c b/drivers/misc/rk628/rk628_hdmirx.c index 3829366a65d5..36d34913a096 100644 --- a/drivers/misc/rk628/rk628_hdmirx.c +++ b/drivers/misc/rk628/rk628_hdmirx.c @@ -1227,14 +1227,7 @@ int rk628_hdmirx_enable(struct rk628 *rk628) return HDMIRX_PLUGIN | HDMIRX_NOSIGNAL; dev_info(rk628->dev, "hdmirx success\n"); - if (rk628->version != RK628D_VERSION) { - /* Try to keep pll frequency close to 1188m */ - val = DIV_ROUND_CLOSEST_ULL(1188000000, (src_mode->clock * 1000)); - val *= src_mode->clock * 1000; - /* set pll rate according hdmirx tmds clk */ - rk628_cru_clk_set_rate(rk628, CGU_CLK_CPLL, val); - msleep(50); - } + rk628_hdmirx_video_unmute(rk628, 1); return HDMIRX_PLUGIN; diff --git a/drivers/misc/rk628/rk628_post_process.c b/drivers/misc/rk628/rk628_post_process.c index ae0c5aa1fcf6..32c27bbc5474 100644 --- a/drivers/misc/rk628/rk628_post_process.c +++ b/drivers/misc/rk628/rk628_post_process.c @@ -1502,20 +1502,6 @@ void rk628_post_process_init(struct rk628 *rk628) { struct rk628_display_mode *src = &rk628->src_mode; const struct rk628_display_mode *dst = &rk628->dst_mode; - u64 dst_rate, src_rate; - - src_rate = src->clock * 1000; - dst_rate = src_rate * dst->vtotal * dst->htotal; - do_div(dst_rate, (src->vtotal * src->htotal)); - do_div(dst_rate, 1000); - dev_info(rk628->dev, "src %dx%d clock:%d\n", - src->hdisplay, src->vdisplay, src->clock); - - dev_info(rk628->dev, "dst %dx%d clock:%llu\n", - dst->hdisplay, dst->vdisplay, dst_rate); - - rk628_cru_clk_set_rate(rk628, CGU_CLK_RX_READ, src->clock * 1000); - rk628_cru_clk_set_rate(rk628, CGU_SCLK_VOP, dst_rate * 1000); if (rk628_output_is_hdmi(rk628)) { rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_VSYNC_POL_MASK, @@ -1602,6 +1588,7 @@ static void rk628_post_process_csc(struct rk628 *rk628) void rk628_post_process_enable(struct rk628 *rk628) { + rk628_cru_clk_adjust(rk628); /* * bt1120 needs to configure the timing register, but hdmitx will modify * the timing as needed, so the bt1120 enable process is moved here. diff --git a/drivers/misc/rk628/rk628_rgb.c b/drivers/misc/rk628/rk628_rgb.c index 7dcf7fa0d522..45df2ce09368 100644 --- a/drivers/misc/rk628/rk628_rgb.c +++ b/drivers/misc/rk628/rk628_rgb.c @@ -269,7 +269,6 @@ static void rk628_bt1120_decoder_timing_cfg(struct rk628 *rk628) static void rk628_bt1120_decoder_enable(struct rk628 *rk628) { struct rk628_display_mode *mode = rk628_display_get_src_mode(rk628); - unsigned long dec_clk_rate; rk628_set_input_bus_format(rk628, BUS_FMT_YUV422); @@ -289,19 +288,6 @@ static void rk628_bt1120_decoder_enable(struct rk628 *rk628) rk628_i2c_write(rk628, CRU_SOFTRST_CON00, 0x10001000); rk628_i2c_write(rk628, CRU_SOFTRST_CON00, 0x10000000); - /* - * BT1120 dec clk is a 4-bit integer division, which is inaccurate in - * most resolutions. So if the frequency division is not accurate, apply - * for a fault tolerance of up 2% in frequency setting, so that the - * obtained frequency is slightly higher than the actual required clk, - * so that the deviation between the actual clk and the required clk - * frequency is not significant. - */ - rk628_cru_clk_set_rate(rk628, CGU_BT1120DEC, mode->clock * 1000); - dec_clk_rate = rk628_cru_clk_get_rate(rk628, CGU_BT1120DEC); - if (dec_clk_rate < mode->clock * 1000) - rk628_cru_clk_set_rate(rk628, CGU_BT1120DEC, mode->clock * 1020); - if (rk628->rgb.bt1120_dual_edge) { rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON0, DEC_DUALEDGE_EN, DEC_DUALEDGE_EN); From 1b4cb08dbe017b12b90dcfdea7d8b15ff0f9d01b Mon Sep 17 00:00:00 2001 From: Zhibin Huang Date: Thu, 30 May 2024 09:54:55 +0800 Subject: [PATCH 15/19] misc: rk628: bt1120rx: rk628f adjust cpll frequency to support more frequencies Type: Function Redmine ID: N/A Associated modifications: N/A Test: N/A Signed-off-by: Zhibin Huang Change-Id: Ifdd947d3396399faaea106a0ba7d1193be494eeb --- drivers/misc/rk628/rk628_cru.c | 5 ++++- drivers/misc/rk628/rk628_cru.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/misc/rk628/rk628_cru.c b/drivers/misc/rk628/rk628_cru.c index ed2364904876..a16244f6936c 100644 --- a/drivers/misc/rk628/rk628_cru.c +++ b/drivers/misc/rk628/rk628_cru.c @@ -742,8 +742,11 @@ void rk628_cru_clk_adjust(struct rk628 *rk628) * Try to keep cpll frequency close to 1188m (Tested rk628f hdmirx * scenarios) */ - if (rk628_input_is_hdmi(rk628) && rk628->version != RK628D_VERSION) { + if ((rk628_input_is_hdmi(rk628) || rk628_input_is_bt1120(rk628)) && + rk628->version != RK628D_VERSION) { val = 1188000000UL / (src->clock * 1000); + if (rk628_input_is_bt1120(rk628) && val > (CLK_BT1120DEC_DIV_MAX + 1)) + val = CLK_BT1120DEC_DIV_MAX + 1; val *= src->clock * 1000; rk628_cru_clk_set_rate(rk628, CGU_CLK_CPLL, val); msleep(50); diff --git a/drivers/misc/rk628/rk628_cru.h b/drivers/misc/rk628/rk628_cru.h index c492d928d112..d435c35027b6 100644 --- a/drivers/misc/rk628/rk628_cru.h +++ b/drivers/misc/rk628/rk628_cru.h @@ -80,6 +80,7 @@ #define CLK_BT1120DEC_SEL_GPLL 1 #define CLK_BT1120DEC_SEL_CPLL 0 #define CLK_BT1120DEC_DIV(x) HIWORD_UPDATE(x, 4, 0) +#define CLK_BT1120DEC_DIV_MAX GENMASK(4, 0) #define CRU_CLKSEL_CON03 CRU_REG(0x008c) #define CRU_CLKSEL_CON04 CRU_REG(0x0090) #define CLK_HDMIRX_AUD_DIV_MASK GENMASK(13, 6) From 6ac091d511c5ff082632cb291a6394fde411a893 Mon Sep 17 00:00:00 2001 From: Zhibin Huang Date: Thu, 30 May 2024 10:12:14 +0800 Subject: [PATCH 16/19] misc: rk628: bt1120rx: rk628d adjust cpll frequency to support more frequencies Type: Function Redmine ID: N/A Associated modifications: N/A Test: N/A Signed-off-by: Zhibin Huang Change-Id: I570608197321634ec9172d31718bb01769755324 --- drivers/misc/rk628/rk628_cru.c | 67 ++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/drivers/misc/rk628/rk628_cru.c b/drivers/misc/rk628/rk628_cru.c index a16244f6936c..6afdae90e5f7 100644 --- a/drivers/misc/rk628/rk628_cru.c +++ b/drivers/misc/rk628/rk628_cru.c @@ -705,29 +705,74 @@ void rk628_cru_init(struct rk628 *rk628) u32 val; u8 mcu_mode; + /* + * In rk628d application, if you need to dynamically tune the cpll + * frequency, you need to mount pclk under gpll, otherwise it will + * affect the i2c use. The bt1120rx only supports 5bit integer crossover + * frequency, in order to crossover frequency accurately, you need to + * adjust the cpll frequency dynamically, so in the scenario of rk628d + * bt1120rx, mount the pclk under gpll. + */ rk628_i2c_read(rk628, GRF_SYSTEM_STATUS0, &val); mcu_mode = (val & I2C_ONLY_FLAG) ? 0 : 1; if (mcu_mode || rk628->version >= RK628F_VERSION) { rk628_i2c_write(rk628, CRU_MODE_CON00, HIWORD_UPDATE(1, 4, 4)); + /* + * rk628d pclk use cpll by default, and frequency is 99MHz + * rk628f pclk use gpll by default, and frequency is 98.304MHz + */ + if (rk628_input_is_bt1120(rk628)) { + /* set pclk use gpll, and set pclk 98.304Hz */ + rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff0089); + } return; } + + /* clock switch and first set gpll almost 99MHz */ rk628_i2c_write(rk628, CRU_GPLL_CON0, 0xffff701d); mdelay(1); + /* set clk_gpll_mux from gpll */ rk628_i2c_write(rk628, CRU_MODE_CON00, 0xffff0004); mdelay(1); rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff0080); + /* set pclk use gpll, now div is 4 */ rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff0083); + /* set cpll almost 400MHz */ rk628_i2c_write(rk628, CRU_CPLL_CON0, 0xffff3063); mdelay(1); + /* set clk_cpll_mux from clk_cpll */ rk628_i2c_write(rk628, CRU_MODE_CON00, 0xffff0005); - rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff0003); - rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff000b); - rk628_i2c_write(rk628, CRU_GPLL_CON0, 0xffff1028); mdelay(1); - rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff008b); - rk628_i2c_write(rk628, CRU_CPLL_CON0, 0xffff1063); - mdelay(1); - rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff000b); + if (rk628_input_is_bt1120(rk628)) { + /* set pclk use cpll, now div is 4 */ + rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff0003); + /* set pclk use cpll, now div is 10 */ + rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff0009); + /* set gpll 983.04Hz */ + rk628_i2c_write(rk628, CRU_GPLL_CON0, 0xffff1028); + mdelay(1); + /* set pclk use gpll, now div is 10 */ + rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff0089); + /* set cpll 1188MHz */ + rk628_i2c_write(rk628, CRU_CPLL_CON0, 0xffff1063); + /* final: cpll 1188MHz, gpll 983.04Hz, pclk (use gpll) 98.304Hz */ + } else { + /* set pclk use cpll, now div is 4 */ + rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff0003); + /* set pclk use cpll, now div is 12 */ + rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff000b); + /* set gpll 983.04Hz */ + rk628_i2c_write(rk628, CRU_GPLL_CON0, 0xffff1028); + mdelay(1); + /* set pclk use gpll, now div is 12 */ + rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff008b); + /* set cpll 1188MHz */ + rk628_i2c_write(rk628, CRU_CPLL_CON0, 0xffff1063); + mdelay(1); + /* set pclk use cpll, now div is 12 */ + rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff000b); + /* final: cpll 1188MHz, gpll 983.04Hz, pclk (use cpll) 99Hz */ + } } void rk628_cru_clk_adjust(struct rk628 *rk628) @@ -739,11 +784,11 @@ void rk628_cru_clk_adjust(struct rk628 *rk628) u32 val; /* - * Try to keep cpll frequency close to 1188m (Tested rk628f hdmirx - * scenarios) + * Try to keep cpll frequency close to 1188m (Tested bt1120rx and rk628f + * hdmirx scenarios) */ - if ((rk628_input_is_hdmi(rk628) || rk628_input_is_bt1120(rk628)) && - rk628->version != RK628D_VERSION) { + if ((rk628_input_is_hdmi(rk628) && rk628->version != RK628D_VERSION) || + rk628_input_is_bt1120(rk628)) { val = 1188000000UL / (src->clock * 1000); if (rk628_input_is_bt1120(rk628) && val > (CLK_BT1120DEC_DIV_MAX + 1)) val = CLK_BT1120DEC_DIV_MAX + 1; From 2293a3c858c61b6d12090af8adc0567853f62135 Mon Sep 17 00:00:00 2001 From: Zhibin Huang Date: Fri, 24 May 2024 14:44:49 +0800 Subject: [PATCH 17/19] misc: rk628: hdmitx: supplement valid modes Type: Function Redmine ID: N/A Associated modifications: N/A Test: N/A Signed-off-by: Zhibin Huang Change-Id: Ic0b429e2a170296164b875cd3d6650971b53e0a8 --- drivers/misc/rk628/rk628_hdmitx.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/misc/rk628/rk628_hdmitx.c b/drivers/misc/rk628/rk628_hdmitx.c index d7cea243ae57..a5444501455d 100644 --- a/drivers/misc/rk628/rk628_hdmitx.c +++ b/drivers/misc/rk628/rk628_hdmitx.c @@ -630,6 +630,33 @@ static enum drm_mode_status rk628_hdmi_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { + struct rk628_hdmi *hdmi = connector_to_hdmi(connector); + struct rk628 *rk628 = hdmi->rk628; + + /* rk628 hdmitx supports up to 148.5MHz frequency */ + if (mode->clock > 148500) + return MODE_CLOCK_HIGH; + + /* + * rk628d hdmitx below (including) 40MHz clock frequency (800x600@60Hz), + * hsync and vsync can only output negative polarity + */ + if (rk628->version == RK628D_VERSION && mode->clock <= 40000 && + (mode->flags & DRM_MODE_FLAG_PHSYNC || + mode->flags & DRM_MODE_FLAG_PVSYNC)) + return MODE_BAD; + + if (rk628_input_is_bt1120(rk628) || rk628_input_is_rgb(rk628)) { + /* rk628 bt1120rx cannot be displayed at frequencies below 40MHz. */ + if (rk628_input_is_bt1120(rk628) && mode->clock < 40000) + return MODE_CLOCK_LOW; + + return MODE_OK; + } + + /* + * Unverified pathway retains only 1080p and 720p resolutions + */ if ((mode->hdisplay == 1920 && mode->vdisplay == 1080) || (mode->hdisplay == 1280 && mode->vdisplay == 720)) return MODE_OK; From 1c5fe02ca8d339389c8a23abb71a9097216edd4a Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Wed, 12 Jun 2024 18:13:25 +0800 Subject: [PATCH 18/19] usb: typec: tcpci_husb311: Disable IRQ before unregistering TCPCI port For reboot stress tests, have the below crash. [ 15.003004] Unable to handle kernel paging request at virtual address ffffffc009c975a0 [ 14.897469] pc : queued_spin_lock_slowpath+0x274/0x420 [ 14.898086] lr : kthread_queue_work+0xb8/0x114 [ 14.899280] sp : ffffffc00cb93c90 [ 14.899280] x29: ffffffc00cb93c90 x28: 0000000000000000 x27: 0000000000000000 [ 14.899284] x26: ffffffc0080bc414 x25: ffffffc0080bc4f0 ... [ 15.010633] CPU: 5 PID: 240 Comm: irq/82-husb311 Tainted: G O 6.1.75 #120 ... [ 15.029917] Call trace: [ 15.030137] queued_spin_lock_slowpath+0x274/0x420 [ 15.030564] kthread_queue_work+0xb8/0x114 [ 15.030926] tcpm_cc_change+0x60/0xa4 [ 15.031256] tcpci_irq+0x150/0x240 [ 15.031554] husb311_irq+0x50/0x290 [ 15.031863] irq_thread_fn+0x30/0xb0 [ 15.032182] irq_thread+0x16c/0x2c4 [ 15.032490] kthread+0xdc/0xe0 [ 15.032765] ret_from_fork+0x10/0x20 ... This commit addresses the issue by disabling the IRQ before unregistering the TCPCI port in both the remove and shutdown handlers. This ensures that no new interrupts are handled after the port is unregistered, preventing potential race conditions or use-after-free issues. Fixes: 5e320d031053 ("usb: typec: husb311: set vbus off when shutdown") Change-Id: Ie2cb85a15f6c63b019e4a000ba65205bc508e483 Signed-off-by: Caesar Wang --- drivers/usb/typec/tcpm/tcpci_husb311.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpci_husb311.c b/drivers/usb/typec/tcpm/tcpci_husb311.c index 74f45f581ca6..175d5f3e2a1d 100644 --- a/drivers/usb/typec/tcpm/tcpci_husb311.c +++ b/drivers/usb/typec/tcpm/tcpci_husb311.c @@ -307,6 +307,7 @@ static void husb311_remove(struct i2c_client *client) struct husb311_chip *chip = i2c_get_clientdata(client); device_init_wakeup(chip->dev, false); + disable_irq(client->irq); cancel_delayed_work_sync(&chip->pm_work); tcpci_unregister_port(chip->tcpci); } @@ -317,6 +318,7 @@ static void husb311_shutdown(struct i2c_client *client) husb311_set_vbus(chip->tcpci, &chip->data, false, false); + disable_irq(client->irq); cancel_delayed_work_sync(&chip->pm_work); tcpci_unregister_port(chip->tcpci); } From ce6e1b95ba555d78ea1f4ca5653a1c8a074c1745 Mon Sep 17 00:00:00 2001 From: Wu Liangqing Date: Fri, 14 Jun 2024 19:38:22 +0800 Subject: [PATCH 19/19] arm64: dts: rockchip: rk3576-android: enabled wdt Change-Id: Iee39c9cc175ba803102700501eec33d77ca7d928 Signed-off-by: Wu Liangqing --- arch/arm64/boot/dts/rockchip/rk3576-android.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3576-android.dtsi b/arch/arm64/boot/dts/rockchip/rk3576-android.dtsi index e233eb1cec0d..3389e9e1102a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-android.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576-android.dtsi @@ -81,3 +81,7 @@ &vop { support-multi-area; }; + +&wdt { + status= "okay"; +};