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"; +}; 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>; 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 = { 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) 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; 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); } } 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..0f77c29744f6 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,297 @@ 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; + + /* 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; + 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 +1998,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 +2006,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..8632e3f18b18 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.h +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.h @@ -3,7 +3,9 @@ #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 #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/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..6afdae90e5f7 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,32 +628,201 @@ 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; 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) +{ + 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 bt1120rx and rk628f + * hdmirx scenarios) + */ + 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; + 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 e13b728eb915..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) @@ -172,10 +173,13 @@ #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); +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_hdmitx.c b/drivers/misc/rk628/rk628_hdmitx.c index b5d7ab46a631..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; @@ -677,8 +704,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; 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); 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 }, 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, 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); } 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 */ 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 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 =