diff --git a/arch/arm64/boot/dts/rockchip/rk3576-iotest-v10-edp2dp.dts b/arch/arm64/boot/dts/rockchip/rk3576-iotest-v10-edp2dp.dts index abc9c3e1e95e..e5c116260bbc 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-iotest-v10-edp2dp.dts +++ b/arch/arm64/boot/dts/rockchip/rk3576-iotest-v10-edp2dp.dts @@ -37,6 +37,10 @@ status = "okay"; }; +&edp_sound { + status = "okay"; +}; + &hdmi { status = "disabled"; }; @@ -66,6 +70,10 @@ status = "disabled"; }; +&sai6 { + status = "okay"; +}; + &vp0 { assigned-clocks = <&cru DCLK_VP0_SRC>; assigned-clock-parents = <&cru PLL_VPLL>; diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi index 357c6e2518a9..c0612b58be26 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi @@ -3993,6 +3993,7 @@ <&cru SRST_T_EMMC>; reset-names = "core", "bus", "axi", "block", "timer"; max-frequency = <200000000>; + supports-cqe; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi index 6d42de7248fa..2cc2481592ec 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi @@ -5711,6 +5711,7 @@ <&cru SRST_T_EMMC>; reset-names = "core", "bus", "axi", "block", "timer"; max-frequency = <200000000>; + supports-cqe; status = "disabled"; }; diff --git a/drivers/gpu/drm/rockchip/dw-dp.c b/drivers/gpu/drm/rockchip/dw-dp.c index 7ece490e33fa..4b9cf4fdd22f 100644 --- a/drivers/gpu/drm/rockchip/dw-dp.c +++ b/drivers/gpu/drm/rockchip/dw-dp.c @@ -418,12 +418,14 @@ struct dw_dp_mst_enc { struct dw_dp_video video; struct dw_dp_audio *audio; struct device_node *port_node; + struct drm_bridge *next_bridge; DECLARE_BITMAP(sdp_reg_bank, SDP_REG_BANK_SIZE); struct dw_dp_mst_conn *mst_conn; struct dw_dp *dp; int stream_id; + int fix_port_num; bool active; }; @@ -488,6 +490,7 @@ struct dw_dp { bool is_loader_protect; bool support_mst; bool is_mst; + bool is_fix_port; int mst_port_num; int active_mst_links; struct drm_dp_mst_topology_mgr mst_mgr; @@ -1306,6 +1309,7 @@ static int dw_dp_mst_info_dump(struct seq_file *s, void *data) struct dw_dp *dp = node->info_ent->data; struct dw_dp_mst_conn *mst_conn; struct drm_property_blob *path_blob; + int i; if (dp->mst_mgr.cbs) { drm_dp_mst_dump_topology(s, &dp->mst_mgr); @@ -1321,6 +1325,17 @@ static int dw_dp_mst_info_dump(struct seq_file *s, void *data) (char *)path_blob->data); } seq_puts(s, "\n"); + if (dp->is_fix_port) { + seq_puts(s, "\n*** Fix port info ***\n"); + seq_puts(s, "stream id | port num\n"); + + for (i = 0; i < dp->mst_port_num; i++) { + if (!dp->mst_enc[i].dp) + continue; + seq_printf(s, "%-16d %d\n", dp->mst_enc[i].stream_id, + dp->mst_enc[i].fix_port_num); + } + } } return 0; @@ -3250,14 +3265,39 @@ static const struct drm_connector_funcs dw_dp_mst_connector_funcs = { .early_unregister = dw_dp_mst_connector_early_unregister, }; +static struct drm_bridge *dw_dp_mst_connector_get_bridge(struct dw_dp_mst_conn *mst_conn) +{ + struct dw_dp *dp = mst_conn->dp; + int i; + + if (!dp->is_fix_port) + return NULL; + + for (i = 0; i < dp->mst_port_num; i++) + if (dp->mst_enc[i].fix_port_num == mst_conn->port->port_num) + return dp->mst_enc[i].next_bridge; + + return NULL; +} + static int dw_dp_mst_connector_get_modes(struct drm_connector *connector) { struct dw_dp_mst_conn *mst_conn = container_of(connector, struct dw_dp_mst_conn, connector); struct dw_dp *dp = mst_conn->dp; + struct drm_bridge *bridge; struct edid *edid; int num_modes = 0; + if (dp->is_fix_port) { + bridge = dw_dp_mst_connector_get_bridge(mst_conn); + if (bridge) { + num_modes = drm_bridge_get_modes(bridge, connector); + if (num_modes) + return num_modes; + } + } + edid = drm_dp_mst_get_edid(connector, &dp->mst_mgr, mst_conn->port); if (edid) { drm_connector_update_edid_property(connector, edid); @@ -3924,6 +3964,8 @@ dw_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_p for (i = 0; i < dp->mst_port_num; i++) { if (!of_device_is_available(dp->mst_enc[i].port_node)) continue; + if (dp->is_fix_port && dp->mst_enc[i].fix_port_num != port->port_num) + continue; ret = drm_connector_attach_encoder(&mst_conn->connector, &dp->mst_enc[i].encoder); if (ret) goto err; @@ -3979,6 +4021,57 @@ dw_dp_create_fake_mst_encoders(struct dw_dp *dp) return true; } +static int dw_dp_mst_get_fix_port(struct dw_dp *dp) +{ + char *prop_name = "rockchip,mst-fixed-ports"; + int elem_len, ret, i; + int elem_data[DPTX_MAX_STREAMS]; + + if (!device_property_present(dp->dev, prop_name)) + return 0; + + elem_len = device_property_count_u32(dp->dev, prop_name); + if (dp->mst_port_num != elem_len) + return -EINVAL; + + ret = device_property_read_u32_array(dp->dev, prop_name, elem_data, elem_len); + if (ret) + return -EINVAL; + + dp->is_fix_port = true; + + for (i = 0; i < dp->mst_port_num; i++) + dp->mst_enc[i].fix_port_num = elem_data[i]; + + return 0; +} + +static int dw_dp_mst_find_ext_bridges(struct dw_dp *dp) +{ + struct dw_dp_mst_enc *mst_enc; + int i, ret; + + for (i = 0; i < dp->mst_port_num; i++) { + mst_enc = &dp->mst_enc[i]; + if (!of_device_is_available(dp->mst_enc[i].port_node)) + continue; + ret = drm_of_find_panel_or_bridge(mst_enc->port_node, 2, -1, NULL, + &mst_enc->next_bridge); + if (ret < 0 && ret != -ENODEV) + return ret; + + if (mst_enc->next_bridge) { + ret = drm_bridge_attach(&mst_enc->encoder, mst_enc->next_bridge, NULL, 0); + if (ret) { + DRM_DEV_ERROR(dp->dev, "failed to attach next bridge: %d\n", ret); + return ret; + } + } + } + + return 0; +} + static int dw_dp_mst_encoder_init(struct dw_dp *dp, int conn_base_id) { int ret; @@ -3989,6 +4082,13 @@ static int dw_dp_mst_encoder_init(struct dw_dp *dp, int conn_base_id) INIT_LIST_HEAD(&dp->mst_conn_list); dp->mst_mgr.cbs = &mst_cbs; dw_dp_create_fake_mst_encoders(dp); + ret = dw_dp_mst_get_fix_port(dp); + if (ret) + return ret; + + ret = dw_dp_mst_find_ext_bridges(dp); + if (ret) + return ret; ret = drm_dp_mst_topology_mgr_init(&dp->mst_mgr, dp->encoder.dev, &dp->aux, 16, dp->mst_port_num, conn_base_id); if (ret) @@ -4025,7 +4125,9 @@ static int dw_dp_connector_init(struct dw_dp *dp) drm_connector_attach_encoder(connector, bridge->encoder); - dw_dp_mst_encoder_init(dp, connector->base.id); + ret = dw_dp_mst_encoder_init(dp, connector->base.id); + if (ret) + return ret; prop = drm_property_create_enum(connector->dev, 0, RK_IF_PROP_COLOR_DEPTH, color_depth_enum_list, ARRAY_SIZE(color_depth_enum_list)); diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c index 434a6cf03969..1a329f90fca1 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c @@ -282,6 +282,9 @@ struct dw_mipi_dsi2 { bool dual_connector_split; bool left_display; u32 split_area; + + bool support_psr; + bool enabled; }; static inline struct dw_mipi_dsi2 *host_to_dsi2(struct mipi_dsi_host *host) @@ -318,6 +321,14 @@ static void grf_field_write(struct dw_mipi_dsi2 *dsi2, enum grf_reg_fields index regmap_write(dsi2->grf, reg, (val << lsb) | (GENMASK(msb, lsb) << 16)); } +static int dw_mipi_dsi2_is_cmd_mode(struct dw_mipi_dsi2 *dsi2) +{ + if (!(dsi2->mode_flags & MIPI_DSI_MODE_VIDEO)) + return true; + + return false; +} + static int cri_fifos_wait_avail(struct dw_mipi_dsi2 *dsi2) { u32 sts, mask; @@ -432,53 +443,95 @@ static void dw_mipi_dsi2_set_cmd_mode(struct dw_mipi_dsi2 *dsi2) mode, mode & COMMAND_MODE, 1000, MODE_STATUS_TIMEOUT_US); if (ret < 0) - dev_err(dsi2->dev, "failed to enter data stream mode\n"); + dev_err(dsi2->dev, "failed to enter command mode\n"); } static void dw_mipi_dsi2_disable(struct dw_mipi_dsi2 *dsi2) { + if (!dsi2->enabled) + goto out; + regmap_write(dsi2->regmap, DSI2_IPI_PIX_PKT_CFG, 0); dw_mipi_dsi2_set_cmd_mode(dsi2); +out: if (dsi2->slave) dw_mipi_dsi2_disable(dsi2->slave); } static void dw_mipi_dsi2_post_disable(struct dw_mipi_dsi2 *dsi2) { + if (!dsi2->enabled) + goto out; + dw_mipi_dsi2_irq_enable(dsi2, 0); regmap_write(dsi2->regmap, DSI2_PWR_UP, RESET); mipi_dcphy_power_off(dsi2); pm_runtime_put(dsi2->dev); +out: if (dsi2->slave) dw_mipi_dsi2_post_disable(dsi2->slave); } +static struct drm_crtc *dw_mipi_dsi2_get_new_crtc(struct dw_mipi_dsi2 *dsi2, + struct drm_atomic_state *state) +{ + struct drm_encoder *encoder = &dsi2->encoder; + struct drm_connector *connector; + struct drm_connector_state *conn_state; + + connector = drm_atomic_get_new_connector_for_encoder(state, encoder); + if (!connector) + return NULL; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (!conn_state) + return NULL; + + return conn_state->crtc; +} + static void dw_mipi_dsi2_encoder_atomic_disable(struct drm_encoder *encoder, - struct drm_atomic_state *state) + struct drm_atomic_state *old_state) { struct dw_mipi_dsi2 *dsi2 = encoder_to_dsi2(encoder); - struct drm_crtc *crtc = encoder->crtc; - struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); + struct rockchip_crtc_state *s = to_rockchip_crtc_state(encoder->crtc->state); + struct drm_crtc *new_crtc; + struct drm_crtc_state *new_crtc_state = NULL; - if (dsi2->panel) - drm_panel_disable(dsi2->panel); + new_crtc = dw_mipi_dsi2_get_new_crtc(dsi2, old_state); + if (new_crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(old_state, new_crtc); - if (!(dsi2->mode_flags & MIPI_DSI_MODE_VIDEO)) + if (new_crtc_state && new_crtc_state->self_refresh_active) + dev_dbg(dsi2->dev, "%s:%d: psr entry\n", __func__, __LINE__); + + if (!new_crtc_state || !new_crtc_state->self_refresh_active) { + if (dsi2->panel) + drm_panel_disable(dsi2->panel); + } + + if (dw_mipi_dsi2_is_cmd_mode(dsi2)) rockchip_drm_crtc_standby(encoder->crtc, 1); dw_mipi_dsi2_disable(dsi2); - if (!(dsi2->mode_flags & MIPI_DSI_MODE_VIDEO)) + if (dw_mipi_dsi2_is_cmd_mode(dsi2)) rockchip_drm_crtc_standby(encoder->crtc, 0); - if (dsi2->panel) - drm_panel_unprepare(dsi2->panel); + if (!new_crtc_state || !new_crtc_state->self_refresh_active) { + if (dsi2->panel) + drm_panel_unprepare(dsi2->panel); + } dw_mipi_dsi2_post_disable(dsi2); - if (!crtc->state->active_changed) + dsi2->enabled = false; + if (dsi2->slave) + dsi2->slave->enabled = false; + + if (!encoder->crtc->state->active_changed) return; if (dsi2->slave) @@ -749,7 +802,7 @@ static void dw_mipi_dsi2_ipi_set(struct dw_mipi_dsi2 *dsi2) * if the controller is intended to operate in data stream mode, * no more steps are required. */ - if (!(dsi2->mode_flags & MIPI_DSI_MODE_VIDEO)) + if (dw_mipi_dsi2_is_cmd_mode(dsi2)) return; vact = mode->crtc_vdisplay; @@ -811,6 +864,9 @@ dw_mipi_dsi2_work_mode(struct dw_mipi_dsi2 *dsi2, u32 mode) static void dw_mipi_dsi2_pre_enable(struct dw_mipi_dsi2 *dsi2) { + if (dsi2->enabled) + goto out; + pm_runtime_get_sync(dsi2->dev); dw_mipi_dsi2_host_softrst(dsi2); @@ -827,6 +883,7 @@ static void dw_mipi_dsi2_pre_enable(struct dw_mipi_dsi2 *dsi2) regmap_write(dsi2->regmap, DSI2_PWR_UP, POWER_UP); dw_mipi_dsi2_set_cmd_mode(dsi2); +out: if (dsi2->slave) dw_mipi_dsi2_pre_enable(dsi2->slave); } @@ -853,6 +910,9 @@ static void dw_mipi_dsi2_enable(struct dw_mipi_dsi2 *dsi2) u32 mode; int ret; + if (dsi2->enabled) + goto out; + dw_mipi_dsi2_clk_management(dsi2); dw_mipi_dsi2_ipi_set(dsi2); @@ -870,6 +930,7 @@ static void dw_mipi_dsi2_enable(struct dw_mipi_dsi2 *dsi2) else dw_mipi_dsi2_set_data_stream_mode(dsi2); +out: if (dsi2->slave) dw_mipi_dsi2_enable(dsi2->slave); } @@ -943,8 +1004,19 @@ static void dw_mipi_dsi2_encoder_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) { struct dw_mipi_dsi2 *dsi2 = encoder_to_dsi2(encoder); + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state = NULL; int ret; + crtc = dw_mipi_dsi2_get_new_crtc(dsi2, state); + if (crtc) + old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + + if (old_crtc_state && old_crtc_state->self_refresh_active) { + rockchip_drm_crtc_standby(encoder->crtc, 1); + dev_dbg(dsi2->dev, "%s:%d: psr exit\n", __func__, __LINE__); + } + ret = dw_mipi_dsi2_encoder_mode_set(dsi2, state); if (ret) { dev_err(dsi2->dev, "failed to set dsi2 mode\n"); @@ -961,13 +1033,24 @@ static void dw_mipi_dsi2_encoder_atomic_enable(struct drm_encoder *encoder, dw_mipi_dsi2_pre_enable(dsi2); - if (dsi2->panel) - drm_panel_prepare(dsi2->panel); + if (!(old_crtc_state && old_crtc_state->self_refresh_active)) { + if (dsi2->panel) + drm_panel_prepare(dsi2->panel); + } dw_mipi_dsi2_enable(dsi2); - if (dsi2->panel) - drm_panel_enable(dsi2->panel); + if (old_crtc_state && old_crtc_state->self_refresh_active) + rockchip_drm_crtc_standby(encoder->crtc, 0); + + dsi2->enabled = true; + if (dsi2->slave) + dsi2->slave->enabled = true; + + if (!(old_crtc_state && old_crtc_state->self_refresh_active)) { + if (dsi2->panel) + drm_panel_enable(dsi2->panel); + } DRM_DEV_INFO(dsi2->dev, "final DSI-Link bandwidth: %u x %d %s\n", dsi2->lane_hs_rate, @@ -1014,7 +1097,7 @@ dw_mipi_dsi2_encoder_atomic_check(struct drm_encoder *encoder, s->color_encoding = DRM_COLOR_YCBCR_BT709; s->color_range = DRM_COLOR_YCBCR_FULL_RANGE; - if (!(dsi2->mode_flags & MIPI_DSI_MODE_VIDEO)) { + if (dw_mipi_dsi2_is_cmd_mode(dsi2)) { s->output_flags |= ROCKCHIP_OUTPUT_MIPI_DS_MODE; s->soft_te = dsi2->te_gpio ? true : false; s->hold_mode = dsi2->disable_hold_mode ? false : true; @@ -1057,18 +1140,26 @@ dw_mipi_dsi2_encoder_atomic_check(struct drm_encoder *encoder, static void dw_mipi_dsi2_loader_protect(struct dw_mipi_dsi2 *dsi2, bool on) { + if (dsi2->dcphy) + if (!dsi2->c_option) + phy_set_mode(dsi2->dcphy, PHY_MODE_MIPI_DPHY); + if (on) { pm_runtime_get_sync(dsi2->dev); phy_init(dsi2->dcphy); dsi2->phy_enabled = true; if (dsi2->dcphy) dsi2->dcphy->power_count++; + + dsi2->enabled = true; } else { pm_runtime_put(dsi2->dev); phy_exit(dsi2->dcphy); dsi2->phy_enabled = false; if (dsi2->dcphy) dsi2->dcphy->power_count--; + + dsi2->enabled = false; } if (dsi2->slave) @@ -1152,9 +1243,36 @@ dw_mipi_dsi2_connector_mode_valid(struct drm_connector *connector, return MODE_OK; } +static int dw_mipi_dsi2_connector_atomic_check(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + struct dw_mipi_dsi2 *dsi2 = con_to_dsi2(connector); + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return -ENODEV; + + conn_state->self_refresh_aware = dsi2->support_psr && dw_mipi_dsi2_is_cmd_mode(dsi2); + + if (!conn_state->crtc) + return 0; + + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (!crtc_state) + return 0; + + if (crtc_state->self_refresh_active && !dw_mipi_dsi2_is_cmd_mode(dsi2)) + return -EINVAL; + + return 0; +} + static struct drm_connector_helper_funcs dw_mipi_dsi2_connector_helper_funcs = { .get_modes = dw_mipi_dsi2_connector_get_modes, .mode_valid = dw_mipi_dsi2_connector_mode_valid, + .atomic_check = dw_mipi_dsi2_connector_atomic_check, }; static enum drm_connector_status @@ -1257,6 +1375,7 @@ static int dw_mipi_dsi2_dual_channel_probe(struct dw_mipi_dsi2 *dsi2) dsi2->slave->channel = dsi2->channel; dsi2->slave->format = dsi2->format; dsi2->slave->mode_flags = dsi2->mode_flags; + dsi2->slave->support_psr = dsi2->support_psr; } return 0; @@ -1668,6 +1787,7 @@ static ssize_t dw_mipi_dsi2_transfer(struct dw_mipi_dsi2 *dsi2, u32 val; u32 mode; + pm_runtime_get_sync(dsi2->dev); dw_mipi_dsi2_clk_management(dsi2); regmap_update_bits(dsi2->regmap, DSI2_DSI_VID_TX_CFG, LPDT_DISPLAY_CMD_EN, msg->flags & MIPI_DSI_MSG_USE_LPM ? LPDT_DISPLAY_CMD_EN : 0); @@ -1676,12 +1796,12 @@ static ssize_t dw_mipi_dsi2_transfer(struct dw_mipi_dsi2 *dsi2, ret = mipi_dsi_create_packet(&packet, msg); if (ret) { DRM_DEV_ERROR(dsi2->dev, "failed to create packet: %d\n", ret); - return ret; + goto err; } ret = cri_fifos_wait_avail(dsi2); if (ret) - return ret; + goto err; /* Send payload */ while (DIV_ROUND_UP(packet.payload_length, 4)) { @@ -1708,18 +1828,24 @@ static ssize_t dw_mipi_dsi2_transfer(struct dw_mipi_dsi2 *dsi2, ret = cri_fifos_wait_avail(dsi2); if (ret) - return ret; + goto err; if (msg->rx_len) { ret = dw_mipi_dsi2_read_from_fifo(dsi2, msg); if (ret < 0) - return ret; + goto err; } + pm_runtime_put(dsi2->dev); + if (dsi2->slave) dw_mipi_dsi2_transfer(dsi2->slave, msg); return msg->tx_len; + +err: + pm_runtime_put(dsi2->dev); + return ret; } static ssize_t dw_mipi_dsi2_host_transfer(struct mipi_dsi_host *host, @@ -1774,6 +1900,12 @@ static int dw_mipi_dsi2_probe(struct platform_device *pdev) if (device_property_read_u32(dev, "split-area", &dsi2->split_area)) dsi2->split_area = 0; + dsi2->support_psr = device_property_read_bool(dev, "support-psr"); + if (dsi2->support_psr && dsi2->auto_calc_mode) { + dsi2->auto_calc_mode = false; + dev_info(dev, "disable auto-calculation-mode in PSR mode\n"); + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev, res); if (IS_ERR(regs)) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 9b6194babdf6..c513ad2c6f6f 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -200,7 +200,8 @@ static int rockchip_drm_aclk_adjust(struct drm_device *dev, funcs = priv->crtc_funcs[drm_crtc_index(crtc)]; if (funcs && funcs->set_aclk) { if (vop_bw_info->plane_num_4k || crtc_num > 1 || - crtc->state->adjusted_mode.crtc_hdisplay > 4096) { + crtc->state->adjusted_mode.crtc_hdisplay > 2560 || + crtc->state->adjusted_mode.crtc_vdisplay > 2560) { funcs->set_aclk(crtc, ROCKCHIP_VOP_ACLK_ADVANCED_MODE); priv->aclk_adjust_frame_num = 2; } else { diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index ac14f540e8c3..f522bbac867c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -28,11 +28,12 @@ #define VOP2_MINOR(version) (((version) >> 16) & 0xff) #define VOP2_BUILD(version) ((version) & 0xffff) +/* The new SOC VOP version is bigger than the old */ +#define VOP_VERSION_RK3568 VOP2_VERSION(0x40, 0x15, 0x8023) +#define VOP_VERSION_RK3588 VOP2_VERSION(0x40, 0x17, 0x6786) #define VOP_VERSION_RK3528 VOP2_VERSION(0x50, 0x17, 0x1263) #define VOP_VERSION_RK3562 VOP2_VERSION(0x50, 0x17, 0x4350) -#define VOP_VERSION_RK3568 VOP2_VERSION(0x40, 0x15, 0x8023) #define VOP_VERSION_RK3576 VOP2_VERSION(0x50, 0x19, 0x9765) -#define VOP_VERSION_RK3588 VOP2_VERSION(0x40, 0x17, 0x6786) /* register one connector */ #define ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE BIT(0) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 5e59371afd04..cfc7d20f0abd 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -4849,6 +4849,17 @@ static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, goto out; } + /* + * Usperspace not commit new frame for long time will triggle driver enter + * psr mode, If userspace directly close display at next time and without + * any new frame commit, driver will not exit psr, at this case we need to + * recover aclk here. + */ + if (vop2->aclk_rate_reset) { + clk_set_rate(vop2->aclk, vop2->aclk_current_freq); + vop2->aclk_rate_reset = false; + } + vop2_lock(vop2); DRM_DEV_INFO(vop2->dev, "Crtc atomic disable vp%d\n", vp->id); VOP_MODULE_SET(vop2, vp, almost_full_or_en, 0); @@ -5914,9 +5925,18 @@ static void vop2_win_atomic_update(struct vop2_win *win, struct drm_rect *src, s } } - if (is_linear_10bit_yuv(fb->format->format) && actual_w & 0x3) { - DRM_WARN("vp%d %s actual_w[%d] should align as 4 pixel when is linear 10 bit yuv format\n", vp->id, win->name, actual_w); - actual_w = ALIGN_DOWN(actual_w, 4); + /* + * At RK356X/RK3588/RK3562/RK3528 linear 10bit yuv format actual_w should align as 4 pixel, + * from RK3576 linear 10bit yuv format actual_w should align as 2 pixel. + */ + if (is_linear_10bit_yuv(fb->format->format)) { + if (vop2->version < VOP_VERSION_RK3576 && actual_w & 0x3) { + DRM_WARN("vp%d %s actual_w[%d] should align as 4 pixel when is linear 10 bit yuv format\n", vp->id, win->name, actual_w); + actual_w = ALIGN_DOWN(actual_w, 4); + } else if (vop2->version >= VOP_VERSION_RK3576 && actual_w & 0x1) { + DRM_WARN("vp%d %s actual_w[%d] should align as 2 pixel when is linear 10 bit yuv format\n", vp->id, win->name, actual_w); + actual_w = ALIGN_DOWN(actual_w, 2); + } } act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff); @@ -7700,7 +7720,7 @@ static size_t vop2_crtc_bandwidth(struct drm_crtc *crtc, act_w = drm_rect_width(&pstate->src) >> 16; act_h = drm_rect_height(&pstate->src) >> 16; - if (pstate->fb->format->is_yuv && (act_w >= 3840 || act_h >= 3840)) + if (pstate->fb->format->is_yuv && (act_w > 2560 || act_h > 2560)) vop_bw_info->plane_num_4k++; bpp = rockchip_drm_get_bpp(pstate->fb->format); diff --git a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.c b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.c index 53785ee1e89d..8da9fdd775d5 100644 --- a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.c +++ b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.c @@ -136,6 +136,20 @@ static int maxim4c_check_local_chipid(maxim4c_t *maxim4c) return -ENODEV; } +static void maxim4c_hot_plug_event_report(maxim4c_t *maxim4c, int data) +{ + struct v4l2_subdev *sd = &maxim4c->subdev; + struct device *dev = &maxim4c->client->dev; + struct v4l2_event evt_hot_plug = { + .type = V4L2_EVENT_HOT_PLUG, + .u.data[0] = data, + }; + + dev_dbg(dev, "%s data %d\n", __func__, data); + + v4l2_event_queue(sd->devnode, &evt_hot_plug); +} + static irqreturn_t maxim4c_hot_plug_detect_irq_handler(int irq, void *dev_id) { maxim4c_t *maxim4c = dev_id; @@ -223,6 +237,7 @@ static void maxim4c_hot_plug_state_check_work(struct work_struct *work) dev_dbg(dev, "lock state: current = 0x%02x, last = 0x%02x\n", curr_lock_state, last_lock_state); + maxim4c_hot_plug_event_report(maxim4c, curr_lock_state); maxim4c->link_lock_state = curr_lock_state; } diff --git a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.h b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.h index df355dff0755..9ea41c7ba3d5 100644 --- a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.h +++ b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_drv.h @@ -9,12 +9,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -32,6 +34,10 @@ /* power supply numbers */ #define MAXIM4C_NUM_SUPPLIES 2 +/* Private v4l2 event */ +#define V4L2_EVENT_HOT_PLUG \ + (V4L2_EVENT_PRIVATE_START + 0x10) + enum { MAXIM4C_HOT_PLUG_OUT = 0, MAXIM4C_HOT_PLUG_IN, diff --git a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_v4l2.c b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_v4l2.c index d517f5b71adb..c434290722b6 100644 --- a/drivers/media/i2c/maxim/local/maxim4c/maxim4c_v4l2.c +++ b/drivers/media/i2c/maxim/local/maxim4c/maxim4c_v4l2.c @@ -779,10 +779,7 @@ static int maxim4c_get_fmt(struct v4l2_subdev *sd, fmt->format.height = mode->height; fmt->format.code = mode->bus_fmt; fmt->format.field = V4L2_FIELD_NONE; - if (fmt->pad < PAD_MAX && fmt->pad >= PAD0) - fmt->reserved[0] = mode->vc[fmt->pad]; - else - fmt->reserved[0] = mode->vc[PAD0]; + fmt->reserved[0] = mode->vc[fmt->pad]; } mutex_unlock(&maxim4c->mutex); @@ -925,6 +922,17 @@ static int maxim4c_g_mbus_config(struct v4l2_subdev *sd, } #endif /* LINUX_VERSION_CODE */ +static int maxim4c_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_HOT_PLUG: + return v4l2_event_subscribe(fh, sub, 0, NULL); + default: + return -EINVAL; + } +} + #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static const struct v4l2_subdev_internal_ops maxim4c_internal_ops = { .open = maxim4c_open, @@ -933,6 +941,8 @@ static const struct v4l2_subdev_internal_ops maxim4c_internal_ops = { static const struct v4l2_subdev_core_ops maxim4c_core_ops = { .s_power = maxim4c_s_power, + .subscribe_event = maxim4c_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, .ioctl = maxim4c_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl32 = maxim4c_compat_ioctl32, @@ -1062,7 +1072,7 @@ int maxim4c_v4l2_subdev_init(maxim4c_t *maxim4c) #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &maxim4c_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index ba2b7c4fc1d1..d56fa23b87fe 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -225,6 +225,7 @@ config MMC_SDHCI_OF_DWCMSHC depends on OF depends on COMMON_CLK select MMC_HSQ + select MMC_CQHCI help This selects Synopsys DesignWare Cores Mobile Storage Controller support. diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c index 41e94cd14109..c14d7251d0bb 100644 --- a/drivers/mmc/host/cqhci-core.c +++ b/drivers/mmc/host/cqhci-core.c @@ -474,8 +474,8 @@ static int cqhci_dma_map(struct mmc_host *host, struct mmc_request *mrq) return sg_count; } -static void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, - bool dma64) +void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, + bool dma64) { __le32 *attr = (__le32 __force *)desc; @@ -495,6 +495,7 @@ static void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, dataddr[0] = cpu_to_le32(addr); } } +EXPORT_SYMBOL(cqhci_set_tran_desc); static int cqhci_prep_tran_desc(struct mmc_request *mrq, struct cqhci_host *cq_host, int tag) @@ -522,7 +523,11 @@ static int cqhci_prep_tran_desc(struct mmc_request *mrq, if ((i+1) == sg_count) end = true; - cqhci_set_tran_desc(desc, addr, len, end, dma64); + if (cq_host->ops->set_tran_desc) + cq_host->ops->set_tran_desc(cq_host, &desc, addr, len, end, dma64); + else + cqhci_set_tran_desc(desc, addr, len, end, dma64); + desc += cq_host->trans_desc_len; } diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h index ba9387ed90eb..1efd3802d66b 100644 --- a/drivers/mmc/host/cqhci.h +++ b/drivers/mmc/host/cqhci.h @@ -290,6 +290,9 @@ struct cqhci_host_ops { int (*program_key)(struct cqhci_host *cq_host, const union cqhci_crypto_cfg_entry *cfg, int slot); #endif + void (*set_tran_desc)(struct cqhci_host *cq_host, u8 **desc, + dma_addr_t addr, int len, bool end, bool dma64); + }; static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg) @@ -315,6 +318,7 @@ irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error, int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc, bool dma64); struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev); int cqhci_deactivate(struct mmc_host *mmc); +void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, bool dma64); static inline int cqhci_suspend(struct mmc_host *mmc) { return cqhci_deactivate(mmc); diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index df63b9c9ba0c..24db6a819fa2 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -22,6 +22,8 @@ #include "sdhci-pltfm.h" #include "mmc_hsq.h" +#include "cqhci.h" +#include "sdhci-cqhci.h" #define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16) @@ -38,12 +40,17 @@ #define DWCMSHC_ENHANCED_STROBE BIT(8) #define DWCMSHC_EMMC_ATCTRL 0x40 +/* DWC IP vendor area 2 pointer */ +#define DWCMSHC_P_VENDOR_AREA2 0xea + /* Rockchip specific Registers */ #define DWCMSHC_EMMC_DLL_CTRL 0x800 #define DWCMSHC_EMMC_DLL_RXCLK 0x804 #define DWCMSHC_EMMC_DLL_TXCLK 0x808 #define DWCMSHC_EMMC_DLL_STRBIN 0x80c #define DECMSHC_EMMC_DLL_CMDOUT 0x810 +#define DECMSHC_EMMC_MISC_CON 0x81C +#define MISC_INTCLK_EN BIT(1) #define DWCMSHC_EMMC_DLL_STATUS0 0x840 #define DWCMSHC_EMMC_DLL_STATUS1 0x844 #define DWCMSHC_EMMC_DLL_START BIT(0) @@ -83,6 +90,10 @@ #define BOUNDARY_OK(addr, len) \ ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1))) +#define DWCMSHC_SDHCI_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \ + SDHCI_TRNS_BLK_CNT_EN | \ + SDHCI_TRNS_DMA) + enum dwcmshc_rk_type { DWCMSHC_RK3568, DWCMSHC_RK3588, @@ -117,7 +128,9 @@ struct rk35xx_priv { struct dwcmshc_priv { struct clk *bus_clk; - int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA reg */ + int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA1 reg */ + int vendor_specific_area2; /* P_VENDOR_SPECIFIC_AREA2 reg */ + void *priv; /* pointer to SoC private stuff */ }; @@ -230,6 +243,145 @@ static void dwcmshc_hs400_enhanced_strobe(struct mmc_host *mmc, sdhci_writel(host, vendor, reg); } +static int dwcmshc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + int err = sdhci_execute_tuning(mmc, opcode); + struct sdhci_host *host = mmc_priv(mmc); + + if (err) + return err; + + /* + * Tuning can leave the IP in an active state (Buffer Read Enable bit + * set) which prevents the entry to low power states (i.e. S0i3). Data + * reset will clear it. + */ + sdhci_reset(host, SDHCI_RESET_DATA); + + return 0; +} + +static u32 dwcmshc_cqe_irq_handler(struct sdhci_host *host, u32 intmask) +{ + int cmd_error = 0; + int data_error = 0; + + if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error)) + return intmask; + + cqhci_irq(host->mmc, intmask, cmd_error, data_error); + + return 0; +} + +static void dwcmshc_sdhci_cqe_enable(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + u8 ctrl; + + sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); + + sdhci_cqe_enable(mmc); + + /* + * The "DesignWare Cores Mobile Storage Host Controller + * DWC_mshc / DWC_mshc_lite Databook" says: + * when Host Version 4 Enable" is 1 in Host Control 2 register, + * SDHCI_CTRL_ADMA32 bit means ADMA2 is selected. + * Selection of 32-bit/64-bit System Addressing: + * either 32-bit or 64-bit system addressing is selected by + * 64-bit Addressing bit in Host Control 2 register. + * + * On the other hand the "DesignWare Cores Mobile Storage Host + * Controller DWC_mshc / DWC_mshc_lite User Guide" says, that we have to + * set DMA_SEL to ADMA2 _only_ mode in the Host Control 2 register. + */ + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + ctrl &= ~SDHCI_CTRL_DMA_MASK; + ctrl |= SDHCI_CTRL_ADMA32; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +} + +static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 reg; + + reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG); + reg |= CQHCI_ENABLE; + sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_CFG); + + reg = sdhci_readl(host, SDHCI_PRESENT_STATE); + while (reg & SDHCI_DATA_AVAILABLE) { + sdhci_readl(host, SDHCI_BUFFER); + reg = sdhci_readl(host, SDHCI_PRESENT_STATE); + } + + sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); + + sdhci_cqe_enable(mmc); + + sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); +} + +static void rk35xx_sdhci_cqe_disabled(struct mmc_host *mmc, bool recovery) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + unsigned long flags; + u32 ctrl; + + mmc->cqe_ops->cqe_wait_for_idle(mmc); + spin_lock_irqsave(&host->lock, flags); + + /* + * During CQE command transfers, command complete bit gets latched. + * So s/w should clear command complete interrupt status when CQE is + * either halted or disabled. Otherwise unexpected SDCHI legacy + * interrupt gets triggered when CQE is halted/disabled. + */ + ctrl = sdhci_readl(host, SDHCI_INT_ENABLE); + ctrl |= SDHCI_INT_RESPONSE; + sdhci_writel(host, ctrl, SDHCI_INT_ENABLE); + sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); + + spin_unlock_irqrestore(&host->lock, flags); + + sdhci_cqe_disable(mmc, recovery); + + ctrl = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG); + ctrl &= ~CQHCI_ENABLE; + sdhci_writel(host, ctrl, dwc_priv->vendor_specific_area2 + CQHCI_CFG); +} + +static void dwcmshc_set_tran_desc(struct cqhci_host *cq_host, u8 **desc, + dma_addr_t addr, int len, bool end, bool dma64) +{ + int tmplen, offset; + + if (likely(!len || BOUNDARY_OK(addr, len))) { + cqhci_set_tran_desc(*desc, addr, len, end, dma64); + return; + } + + offset = addr & (SZ_128M - 1); + tmplen = SZ_128M - offset; + cqhci_set_tran_desc(*desc, addr, tmplen, false, dma64); + + addr += tmplen; + len -= tmplen; + *desc += cq_host->trans_desc_len; + cqhci_set_tran_desc(*desc, addr, len, end, dma64); +} + +static void dwcmshc_cqhci_dumpregs(struct mmc_host *mmc) +{ + sdhci_dumpregs(mmc_priv(mmc)); +} + static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -375,6 +527,7 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); struct rk35xx_priv *priv = dwc_priv->priv; + u32 extra = sdhci_readl(host, DECMSHC_EMMC_MISC_CON); if (mask & SDHCI_RESET_ALL && priv->reset) { reset_control_assert(priv->reset); @@ -383,12 +536,17 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) } sdhci_reset(host, mask); + + /* Enable INTERNAL CLOCK */ + sdhci_writel(host, MISC_INTCLK_EN | extra, DECMSHC_EMMC_MISC_CON); } static void sdhci_dwcmshc_request_done(struct sdhci_host *host, struct mmc_request *mrq) { - if (mmc_hsq_finalize_request(host->mmc, mrq)) - return; + if (!(host->mmc->caps2 & MMC_CAP2_CQE)) { + if (mmc_hsq_finalize_request(host->mmc, mrq)) + return; + } mmc_request_done(host->mmc, mrq); } @@ -400,6 +558,7 @@ static const struct sdhci_ops sdhci_dwcmshc_ops = { .get_max_clock = dwcmshc_get_max_clock, .reset = sdhci_reset, .adma_write_desc = dwcmshc_adma_write_desc, + .irq = dwcmshc_cqe_irq_handler, }; static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = { @@ -409,6 +568,7 @@ static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = { .get_max_clock = sdhci_pltfm_clk_get_max_clock, .reset = rk35xx_sdhci_reset, .adma_write_desc = dwcmshc_adma_write_desc, + .irq = dwcmshc_cqe_irq_handler, .request_done = sdhci_dwcmshc_request_done, }; @@ -434,6 +594,84 @@ static const struct sdhci_pltfm_data sdhci_dwcmshc_rk35xx_pdata = { SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, }; +static const struct cqhci_host_ops dwcmshc_cqhci_ops = { + .enable = dwcmshc_sdhci_cqe_enable, + .disable = sdhci_cqe_disable, + .dumpregs = dwcmshc_cqhci_dumpregs, + .set_tran_desc = dwcmshc_set_tran_desc, +}; + +static const struct cqhci_host_ops rk35xx_cqhci_ops = { + .enable = rk35xx_sdhci_cqe_enable, + .disable = rk35xx_sdhci_cqe_disabled, + .dumpregs = dwcmshc_cqhci_dumpregs, + .set_tran_desc = dwcmshc_set_tran_desc, +}; + +static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev) +{ + struct cqhci_host *cq_host; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + struct rk35xx_priv *rk_priv = priv->priv; + bool dma64 = false; + u16 clk; + int err; + + host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD; + cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL); + if (!cq_host) { + dev_err(mmc_dev(host->mmc), "Unable to setup CQE: not enough memory\n"); + goto dsbl_cqe_caps; + } + + /* + * For dwcmshc host controller we have to enable internal clock + * before access to some registers from Vendor Specific Area 2. + */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + if (!(clk & SDHCI_CLOCK_INT_EN)) { + dev_err(mmc_dev(host->mmc), "Unable to setup CQE: internal clock enable error\n"); + goto free_cq_host; + } + + cq_host->mmio = host->ioaddr + priv->vendor_specific_area2; + if (rk_priv) + cq_host->ops = &rk35xx_cqhci_ops; + else + cq_host->ops = &dwcmshc_cqhci_ops; + + /* Enable using of 128-bit task descriptors */ + dma64 = host->flags & SDHCI_USE_64_BIT_DMA; + if (dma64) { + dev_dbg(mmc_dev(host->mmc), "128-bit task descriptors\n"); + cq_host->caps |= CQHCI_TASK_DESC_SZ_128; + } + err = cqhci_init(cq_host, host->mmc, dma64); + if (err) { + dev_err(mmc_dev(host->mmc), "Unable to setup CQE: error %d\n", err); + goto int_clock_disable; + } + + dev_dbg(mmc_dev(host->mmc), "CQE init done\n"); + + return; + +int_clock_disable: + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + +free_cq_host: + devm_kfree(&pdev->dev, cq_host); + +dsbl_cqe_caps: + host->mmc->caps2 &= ~(MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD); +} + static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) { int err; @@ -594,7 +832,7 @@ static int dwcmshc_probe(struct platform_device *pdev) const struct dwcmshc_driver_data *drv_data; struct mmc_hsq *hsq; int err; - u32 extra; + u32 extra, caps; drv_data = device_get_match_data(&pdev->dev); if (!drv_data) { @@ -646,16 +884,7 @@ static int dwcmshc_probe(struct platform_device *pdev) host->mmc_host_ops.request = dwcmshc_request; host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe; - - hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL); - if (!hsq) { - err = -ENOMEM; - goto err_clk; - } - - err = mmc_hsq_init(hsq, host->mmc); - if (err) - goto err_clk; + host->mmc_host_ops.execute_tuning = dwcmshc_execute_tuning; if (drv_data->flags & RK_PLATFROM) { rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk35xx_priv), GFP_KERNEL); @@ -679,12 +908,34 @@ static int dwcmshc_probe(struct platform_device *pdev) goto err_clk; } + caps = sdhci_readl(host, SDHCI_CAPABILITIES); + if (caps & SDHCI_CAN_64BIT_V4) + sdhci_enable_v4_mode(host); + host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; err = sdhci_setup_host(host); if (err) goto err_clk; + /* Setup Command Queue Engine if enabled */ + if (device_property_read_bool(&pdev->dev, "supports-cqe")) { + priv->vendor_specific_area2 = + sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2); + + dwcmshc_cqhci_init(host, pdev); + } else { + hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL); + if (!hsq) { + err = -ENOMEM; + goto err_setup_host; + } + + err = mmc_hsq_init(hsq, host->mmc); + if (err) + goto err_setup_host; + } + if (rk_priv) dwcmshc_rk35xx_postinit(host, priv); @@ -750,7 +1001,13 @@ static int dwcmshc_suspend(struct device *dev) struct rk35xx_priv *rk_priv = priv->priv; int ret; - mmc_hsq_suspend(host->mmc); + if (host->mmc->caps2 & MMC_CAP2_CQE) { + ret = cqhci_suspend(host->mmc); + if (ret) + return ret; + } else { + mmc_hsq_suspend(host->mmc); + } ret = sdhci_suspend_host(host); if (ret) @@ -782,21 +1039,40 @@ static int dwcmshc_resume(struct device *dev) if (!IS_ERR(priv->bus_clk)) { ret = clk_prepare_enable(priv->bus_clk); if (ret) - return ret; + goto disable_clk; } if (rk_priv) { ret = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, rk_priv->rockchip_clks); if (ret) - return ret; + goto disable_bus_clk; } ret = sdhci_resume_host(host); if (ret) - return ret; + goto disable_rockchip_clks; - return mmc_hsq_resume(host->mmc); + if (host->mmc->caps2 & MMC_CAP2_CQE) { + ret = cqhci_resume(host->mmc); + if (ret) + goto disable_rockchip_clks; + } else { + return mmc_hsq_resume(host->mmc); + } + + return 0; + +disable_rockchip_clks: + if (rk_priv) + clk_bulk_disable_unprepare(RK35xx_MAX_CLKS, + rk_priv->rockchip_clks); +disable_bus_clk: + if (!IS_ERR(priv->bus_clk)) + clk_disable_unprepare(priv->bus_clk); +disable_clk: + clk_disable_unprepare(pltfm_host->clk); + return ret; } static int dwcmshc_runtime_suspend(struct device *dev) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c index eaaf6955805c..8edfa71ad08c 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c @@ -67,6 +67,11 @@ #define DPHY_MC_GNR_CON1 0x0304 #define T_PHY_READY(x) UPDATE(x, 15, 0) #define DPHY_MC_ANA_CON0 0x0308 +#define EDGE_CON(x) UPDATE(x, 14, 12) +#define EDGE_CON_DIR(x) UPDATE(x, 9, 9) +#define EDGE_CON_EN BIT(8) +#define RES_UP(x) UPDATE(x, 7, 4) +#define RES_DN(x) UPDATE(x, 3, 0) #define DPHY_MC_ANA_CON1 0x030c #define DPHY_MC_ANA_CON2 0x0310 #define HS_VREG_AMP_ICON(x) UPDATE(x, 1, 0) @@ -1594,15 +1599,25 @@ samsung_mipi_dphy_clk_lane_timing_init(struct samsung_mipi_dcphy *samsung) { const struct samsung_mipi_dphy_timing *timing; unsigned int lane_hs_rate = div64_ul(samsung->pll.rate, USEC_PER_SEC); - u32 val = 0; + u32 val, res_up, res_down; timing = samsung_mipi_dphy_get_timing(samsung); regmap_write(samsung->regmap, DPHY_MC_GNR_CON0, 0xf000); - regmap_write(samsung->regmap, DPHY_MC_ANA_CON0, 0x7133); + + /* + * The Drive-Strength / Voltage-Amplitude is adjusted by adjusting the + * Driver-Up Resistor and Driver-Down Resistor. + */ + res_up = samsung->pdata->dphy_hs_drv_res_cfg->clk_hs_drv_up_ohm; + res_down = samsung->pdata->dphy_hs_drv_res_cfg->clk_hs_drv_down_ohm; + val = EDGE_CON(7) | EDGE_CON_DIR(0) | EDGE_CON_EN | + RES_UP(res_up) | RES_DN(res_down); + regmap_write(samsung->regmap, DPHY_MC_ANA_CON0, val); if (lane_hs_rate >= 4500) regmap_write(samsung->regmap, DPHY_MC_ANA_CON1, 0x0001); + val = 0; /* * Divide-by-2 Clock from Serial Clock. Use this when data rate is under * 1500Mbps, otherwise divide-by-16 Clock from Serial Clock @@ -1639,14 +1654,22 @@ samsung_mipi_dphy_data_lane_timing_init(struct samsung_mipi_dcphy *samsung) { const struct samsung_mipi_dphy_timing *timing; unsigned int lane_hs_rate = div64_ul(samsung->pll.rate, USEC_PER_SEC); - u32 val = 0; + u32 val, res_up, res_down; timing = samsung_mipi_dphy_get_timing(samsung); - regmap_write(samsung->regmap, COMBO_MD0_ANA_CON0, 0x7133); - regmap_write(samsung->regmap, COMBO_MD1_ANA_CON0, 0x7133); - regmap_write(samsung->regmap, COMBO_MD2_ANA_CON0, 0x7133); - regmap_write(samsung->regmap, DPHY_MD3_ANA_CON0, 0x7133); + /* + * The Drive-Strength / Voltage-Amplitude is adjusted by adjusting the + * Driver-Up Resistor and Driver-Down Resistor. + */ + res_up = samsung->pdata->dphy_hs_drv_res_cfg->data_hs_drv_up_ohm; + res_down = samsung->pdata->dphy_hs_drv_res_cfg->data_hs_drv_down_ohm; + val = EDGE_CON(7) | EDGE_CON_DIR(0) | EDGE_CON_EN | + RES_UP(res_up) | RES_DN(res_down); + regmap_write(samsung->regmap, COMBO_MD0_ANA_CON0, val); + regmap_write(samsung->regmap, COMBO_MD1_ANA_CON0, val); + regmap_write(samsung->regmap, COMBO_MD2_ANA_CON0, val); + regmap_write(samsung->regmap, DPHY_MD3_ANA_CON0, val); if (lane_hs_rate >= 4500) { regmap_write(samsung->regmap, COMBO_MD0_ANA_CON1, 0x0001); @@ -1655,6 +1678,7 @@ samsung_mipi_dphy_data_lane_timing_init(struct samsung_mipi_dcphy *samsung) regmap_write(samsung->regmap, DPHY_MD3_ANA_CON1, 0x0001); } + val = 0; /* * Divide-by-2 Clock from Serial Clock. Use this when data rate is under * 1500Mbps, otherwise divide-by-16 Clock from Serial Clock @@ -2473,12 +2497,28 @@ static const struct dev_pm_ops samsung_mipi_dcphy_pm_ops = { samsung_mipi_dcphy_runtime_resume, NULL) }; +static const struct hs_drv_res_cfg rk3576_dphy_hs_drv_res_cfg = { + .clk_hs_drv_up_ohm = _52_OHM, + .clk_hs_drv_down_ohm = _52_OHM, + .data_hs_drv_up_ohm = _39_OHM, + .data_hs_drv_down_ohm = _39_OHM, +}; + +static const struct hs_drv_res_cfg rk3588_dphy_hs_drv_res_cfg = { + .clk_hs_drv_up_ohm = _34_OHM, + .clk_hs_drv_down_ohm = _34_OHM, + .data_hs_drv_up_ohm = _43_OHM, + .data_hs_drv_down_ohm = _43_OHM, +}; + static const struct samsung_mipi_dcphy_plat_data rk3576_samsung_mipi_dcphy_plat_data = { + .dphy_hs_drv_res_cfg = &rk3576_dphy_hs_drv_res_cfg, .dphy_tx_max_kbps_per_lane = 2500000L, .cphy_tx_max_ksps_per_lane = 1700000L, }; static const struct samsung_mipi_dcphy_plat_data rk3588_samsung_mipi_dcphy_plat_data = { + .dphy_hs_drv_res_cfg = &rk3588_dphy_hs_drv_res_cfg, .dphy_tx_max_kbps_per_lane = 4500000L, .cphy_tx_max_ksps_per_lane = 2000000L, }; diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.h b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.h index 4155a1a65b0c..535b0f424dbe 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.h +++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.h @@ -10,7 +10,34 @@ #define MAX_NUM_CSI2_DPHY (0x2) +enum hs_drv_res_ohm { + _30_OHM = 0x8, + _31_2_OHM, + _32_5_OHM, + _34_OHM, + _35_5_OHM, + _37_OHM, + _39_OHM, + _41_OHM, + _43_OHM = 0x0, + _46_OHM, + _49_OHM, + _52_OHM, + _56_OHM, + _60_OHM, + _66_OHM, + _73_OHM, +}; + +struct hs_drv_res_cfg { + enum hs_drv_res_ohm clk_hs_drv_up_ohm; + enum hs_drv_res_ohm clk_hs_drv_down_ohm; + enum hs_drv_res_ohm data_hs_drv_up_ohm; + enum hs_drv_res_ohm data_hs_drv_down_ohm; +}; + struct samsung_mipi_dcphy_plat_data { + const struct hs_drv_res_cfg *dphy_hs_drv_res_cfg; u32 dphy_tx_max_kbps_per_lane; u32 cphy_tx_max_ksps_per_lane; }; diff --git a/sound/soc/codecs/rk817_codec.c b/sound/soc/codecs/rk817_codec.c index 400be6d5c3d4..f82c0d940257 100644 --- a/sound/soc/codecs/rk817_codec.c +++ b/sound/soc/codecs/rk817_codec.c @@ -931,7 +931,7 @@ static int rk817_resume_path_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); - DBG("%s : resume_path %ld\n", __func__, rk817->resume_path); + DBG("%s : resume_path %d\n", __func__, rk817->resume_path); ucontrol->value.integer.value[0] = rk817->resume_path;