diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-pas.yaml index 66b455d0a8e32..6969ceadaff6d 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-pas.yaml @@ -42,6 +42,14 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle description: Reference to the AOSS side-channel message RAM. + qcom,broken-reset: + type: boolean + description: + Indicates that the remote processor reset line is not owned by the + operating system. When present, the PAS driver must not try to boot or + shut down the remote processor through SCM and may only attach to it if + it is already running. + smd-edge: false firmware-name: diff --git a/drivers/gpu/drm/display/drm_hdmi_audio_helper.c b/drivers/gpu/drm/display/drm_hdmi_audio_helper.c index 7d78b02c14462..6ca1c7ad0632f 100644 --- a/drivers/gpu/drm/display/drm_hdmi_audio_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_audio_helper.c @@ -130,6 +130,7 @@ EXPORT_SYMBOL(drm_connector_hdmi_audio_plugged_notify); static const struct hdmi_codec_ops drm_connector_hdmi_audio_ops = { .audio_startup = drm_connector_hdmi_audio_startup, + .hw_params = drm_connector_hdmi_audio_prepare, .prepare = drm_connector_hdmi_audio_prepare, .audio_shutdown = drm_connector_hdmi_audio_shutdown, .mute_stream = drm_connector_hdmi_audio_mute_stream, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h index 2f8688224f343..49290bb867f16 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h @@ -9,6 +9,7 @@ static const struct dpu_caps sc7280_dpu_caps = { .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, + .max_dsc_encoder_width = DEFAULT_DSC_ENCODER_MAX_WIDTH, .max_mixer_blendstages = 0x7, .has_dim_layer = true, .has_idle_pc = true, @@ -122,24 +123,28 @@ static const struct dpu_pingpong_cfg sc7280_pp[] = { { .name = "pingpong_0", .id = PINGPONG_0, .base = 0x69000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = 0, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 8), }, { .name = "pingpong_1", .id = PINGPONG_1, .base = 0x6a000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = 0, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 9), }, { .name = "pingpong_2", .id = PINGPONG_2, .base = 0x6b000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_1, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 10), }, { .name = "pingpong_3", .id = PINGPONG_3, .base = 0x6c000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_1, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 11), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h index 7f24ab6342459..cd4823aaa4481 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h @@ -9,6 +9,7 @@ static const struct dpu_caps sc8280xp_dpu_caps = { .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, + .max_dsc_encoder_width = DEFAULT_DSC_ENCODER_MAX_WIDTH, .max_mixer_blendstages = 11, .has_src_split = true, .has_dim_layer = true, @@ -204,36 +205,42 @@ static const struct dpu_pingpong_cfg sc8280xp_pp[] = { { .name = "pingpong_0", .id = PINGPONG_0, .base = 0x69000, .len = 0, + .max_linewidth = DPU_8_x_MAX_PINGPONG_0_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_0, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 8), }, { .name = "pingpong_1", .id = PINGPONG_1, .base = 0x6a000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_0, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 9), }, { .name = "pingpong_2", .id = PINGPONG_2, .base = 0x6b000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_1, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 10), }, { .name = "pingpong_3", .id = PINGPONG_3, .base = 0x6c000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_1, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 11), }, { .name = "pingpong_4", .id = PINGPONG_4, .base = 0x6d000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_2, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 30), }, { .name = "pingpong_5", .id = PINGPONG_5, .base = 0x6e000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_2, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h index 52ff4baa668a4..71a21db7fb12f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h @@ -8,6 +8,7 @@ static const struct dpu_caps x1e80100_dpu_caps = { .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, + .max_dsc_encoder_width = DEFAULT_DSC_ENCODER_MAX_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, @@ -201,47 +202,55 @@ static const struct dpu_pingpong_cfg x1e80100_pp[] = { { .name = "pingpong_0", .id = PINGPONG_0, .base = 0x69000, .len = 0, + .max_linewidth = DPU_8_x_MAX_PINGPONG_0_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_0, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 8), }, { .name = "pingpong_1", .id = PINGPONG_1, .base = 0x6a000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_0, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 9), }, { .name = "pingpong_2", .id = PINGPONG_2, .base = 0x6b000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_1, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 10), }, { .name = "pingpong_3", .id = PINGPONG_3, .base = 0x6c000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_1, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 11), }, { .name = "pingpong_4", .id = PINGPONG_4, .base = 0x6d000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_2, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 30), }, { .name = "pingpong_5", .id = PINGPONG_5, .base = 0x6e000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_2, .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 31), }, { .name = "pingpong_cwb_0", .id = PINGPONG_CWB_0, .base = 0x66000, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_3, }, { .name = "pingpong_cwb_1", .id = PINGPONG_CWB_1, .base = 0x66400, .len = 0, + .max_linewidth = DPU_6_x_MAX_PINGPONG_WIDTH, .sblk = &sc7280_pp_sblk, .merge_3d = MERGE_3D_3, }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 0f4921b1a8922..c42cce82487a7 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -762,6 +762,31 @@ void dpu_crtc_complete_commit(struct drm_crtc *crtc) _dpu_crtc_complete_flip(crtc); } +static int msm_display_get_max_pingpong_width(struct dpu_kms *dpu_kms) +{ + const struct dpu_pingpong_cfg *pingpong; + u32 max_pingpong_width = dpu_kms->catalog->pingpong[0].max_linewidth; + + /* + * Find the smallest overall PINGPONG max_linewidth in the catalog since + * max_linewidth can differ between PINGPONGs even within the same + * chipset. + * + * Note: While, for DPU 8.x+, PINGPONG_0 can technically support up to + * 8k resolutions, this requires reworking the RM to try to reserve + * PINGPONG_0 for modes greater than 5k. + * + * Once this additional logic is implemented, we can drop this helper + * and use the reserved PINGPONG's max_linewidth + */ + for (int i = 1; i < dpu_kms->catalog->pingpong_count; i++) { + pingpong = &dpu_kms->catalog->pingpong[i]; + max_pingpong_width = min(max_pingpong_width, pingpong->max_linewidth); + } + + return max_pingpong_width; +} + static int _dpu_crtc_check_and_setup_lm_bounds(struct drm_crtc *crtc, struct drm_crtc_state *state) { @@ -769,13 +794,14 @@ static int _dpu_crtc_check_and_setup_lm_bounds(struct drm_crtc *crtc, struct drm_display_mode *adj_mode = &state->adjusted_mode; u32 crtc_split_width = adj_mode->hdisplay / cstate->num_mixers; struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); + int max_pingpong_width = msm_display_get_max_pingpong_width(dpu_kms); int i; /* if we cannot merge 2 LMs (no 3d mux) better to fail earlier * before even checking the width after the split */ if (!dpu_kms->catalog->caps->has_3d_merge && - adj_mode->hdisplay > dpu_kms->catalog->caps->max_mixer_width) + adj_mode->hdisplay > max_pingpong_width) return -E2BIG; for (i = 0; i < cstate->num_mixers; i++) { @@ -787,7 +813,7 @@ static int _dpu_crtc_check_and_setup_lm_bounds(struct drm_crtc *crtc, trace_dpu_crtc_setup_lm_bounds(DRMID(crtc), i, r); - if (drm_rect_width(r) > dpu_kms->catalog->caps->max_mixer_width) + if (drm_rect_width(r) > max_pingpong_width) return -E2BIG; } @@ -1367,7 +1393,6 @@ static int dpu_crtc_reassign_planes(struct drm_crtc *crtc, struct drm_crtc_state } #define MAX_CHANNELS_PER_CRTC PIPES_PER_PLANE -#define MAX_HDISPLAY_SPLIT 1080 static struct msm_display_topology dpu_crtc_get_topology( struct drm_crtc *crtc, @@ -1377,12 +1402,18 @@ static struct msm_display_topology dpu_crtc_get_topology( struct drm_display_mode *mode = &crtc_state->adjusted_mode; struct msm_display_topology topology = {0}; struct drm_encoder *drm_enc; + const struct dpu_caps *caps = dpu_kms->catalog->caps; + u32 max_hdisplay_split; drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) dpu_encoder_update_topology(drm_enc, &topology, crtc_state->state, &crtc_state->adjusted_mode); topology.cwb_enabled = drm_crtc_in_clone_mode(crtc_state); + max_hdisplay_split = msm_display_get_max_pingpong_width(dpu_kms); + + if (topology.num_dsc > 0 && caps->max_dsc_encoder_width > 0) + max_hdisplay_split = min(max_hdisplay_split, caps->max_dsc_encoder_width); /* * Datapath topology selection @@ -1403,7 +1434,7 @@ static struct msm_display_topology dpu_crtc_get_topology( * count both the WB and real-time phys encoders. * * For non-DSC CWB usecases, have the num_lm be decided by the - * (mode->hdisplay > MAX_HDISPLAY_SPLIT) check. + * (mode->hdisplay > max_hdisplay_split) check. */ if (topology.num_intf == 2 && !topology.cwb_enabled) @@ -1411,7 +1442,7 @@ static struct msm_display_topology dpu_crtc_get_topology( else if (topology.num_dsc == 2) topology.num_lm = 2; else if (dpu_kms->catalog->caps->has_3d_merge) - topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1; + topology.num_lm = (mode->hdisplay > max_hdisplay_split) ? 2 : 1; else topology.num_lm = 1; @@ -1591,12 +1622,13 @@ static enum drm_mode_status dpu_crtc_mode_valid(struct drm_crtc *crtc, { struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); u64 adjusted_mode_clk; + int max_pingpong_width = msm_display_get_max_pingpong_width(dpu_kms); /* if there is no 3d_mux block we cannot merge LMs so we cannot * split the large layer into 2 LMs, filter out such modes */ if (!dpu_kms->catalog->caps->has_3d_merge && - mode->hdisplay > dpu_kms->catalog->caps->max_mixer_width) + mode->hdisplay > max_pingpong_width) return MODE_BAD_HVALUE; adjusted_mode_clk = dpu_core_perf_adjusted_mode_clk(mode->clock, @@ -1616,7 +1648,7 @@ static enum drm_mode_status dpu_crtc_mode_valid(struct drm_crtc *crtc, * max crtc width is equal to the max mixer width * 2 and max height is 4K */ return drm_mode_validate_size(mode, - 2 * dpu_kms->catalog->caps->max_mixer_width, + 2 * max_pingpong_width, 4096); } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h index 70d5ed4732f2e..24c5fed803514 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h @@ -24,6 +24,12 @@ #define DPU_MAX_IMG_WIDTH 0x3fff #define DPU_MAX_IMG_HEIGHT 0x3fff +#define DPU_1_x_MAX_PINGPONG_WIDTH 4096 +#define DPU_6_x_MAX_PINGPONG_WIDTH 5120 +#define DPU_8_x_MAX_PINGPONG_0_WIDTH 8960 + +#define DEFAULT_DSC_ENCODER_MAX_WIDTH 2560 + #define CRTC_DUAL_MIXERS 2 #define MAX_XIN_COUNT 16 @@ -267,6 +273,7 @@ struct dpu_rotation_cfg { /** * struct dpu_caps - define DPU capabilities * @max_mixer_width max layer mixer line width support. + * @max_dsc_encoder_width max dsc encoder line width support * @max_mixer_blendstages max layer mixer blend stages or * supported z order * @has_src_split source split feature status @@ -280,6 +287,7 @@ struct dpu_rotation_cfg { */ struct dpu_caps { u32 max_mixer_width; + u32 max_dsc_encoder_width; u32 max_mixer_blendstages; bool has_src_split; bool has_dim_layer; @@ -463,6 +471,7 @@ struct dpu_dspp_cfg { * struct dpu_pingpong_cfg - information of PING-PONG blocks * @id enum identifying this block * @base register offset of this block + * @max_linewidth max linewidth for PINGPONG * @intr_done: index for PINGPONG done interrupt * @intr_rdptr: index for PINGPONG readpointer done interrupt * @sblk sub-blocks information @@ -470,6 +479,7 @@ struct dpu_dspp_cfg { struct dpu_pingpong_cfg { DPU_HW_BLK_INFO; u32 merge_3d; + u32 max_linewidth; unsigned int intr_done; unsigned int intr_rdptr; const struct dpu_pingpong_sub_blks *sblk; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c index 7545c0293efbd..209b1e27a84b1 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c @@ -14,14 +14,7 @@ static int dpu_wb_conn_get_modes(struct drm_connector *connector) struct msm_drm_private *priv = dev->dev_private; struct dpu_kms *dpu_kms = to_dpu_kms(priv->kms); - /* - * We should ideally be limiting the modes only to the maxlinewidth but - * on some chipsets this will allow even 4k modes to be added which will - * fail the per SSPP bandwidth checks. So, till we have dual-SSPP support - * and source split support added lets limit the modes based on max_mixer_width - * as 4K modes can then be supported. - */ - return drm_add_modes_noedid(connector, dpu_kms->catalog->caps->max_mixer_width, + return drm_add_modes_noedid(connector, dpu_kms->catalog->wb->maxlinewidth, dev->mode_config.max_height); } diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index ae37c7a2682ba..5b619de251061 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -88,6 +88,7 @@ struct qcom_pas { int crash_reason_smem; unsigned int smem_host_id; bool decrypt_shutdown; + bool broken_reset; const char *info_name; const struct firmware *firmware; @@ -225,6 +226,11 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) struct qcom_pas *pas = rproc->priv; int ret; + if (pas->broken_reset) { + dev_err(pas->dev, "reset is owned by another entity\n"); + return -EPERM; + } + /* Store firmware handle to be used in qcom_pas_start() */ pas->firmware = fw; @@ -407,17 +413,23 @@ static int qcom_pas_stop(struct rproc *rproc) if (ret == -ETIMEDOUT) dev_err(pas->dev, "timed out on wait\n"); - ret = qcom_scm_pas_shutdown(pas->pas_id); - if (ret && pas->decrypt_shutdown) - ret = qcom_pas_shutdown_poll_decrypt(pas); + if (pas->broken_reset) { + ret = 0; + } else { + ret = qcom_scm_pas_shutdown(pas->pas_id); + if (ret && pas->decrypt_shutdown) + ret = qcom_pas_shutdown_poll_decrypt(pas); - if (ret) - dev_err(pas->dev, "failed to shutdown: %d\n", ret); + if (ret) + dev_err(pas->dev, "failed to shutdown: %d\n", ret); + } if (pas->dtb_pas_id) { - ret = qcom_scm_pas_shutdown(pas->dtb_pas_id); - if (ret) - dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret); + if (!pas->broken_reset) { + ret = qcom_scm_pas_shutdown(pas->dtb_pas_id); + if (ret) + dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret); + } qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size); } @@ -517,14 +529,74 @@ static unsigned long qcom_pas_panic(struct rproc *rproc) return qcom_q6v5_panic(&pas->q6v5); } +static int qcom_pas_read_smp2p_state(struct qcom_pas *pas, bool *running, bool *crashed) +{ + bool handover = false; + bool ready = false; + bool fatal = false; + bool stop = false; + int ret; + + ret = irq_get_irqchip_state(pas->q6v5.handover_irq, + IRQCHIP_STATE_LINE_LEVEL, &handover); + if (ret) + return ret; + + ret = irq_get_irqchip_state(pas->q6v5.ready_irq, + IRQCHIP_STATE_LINE_LEVEL, &ready); + if (ret) + return ret; + + ret = irq_get_irqchip_state(pas->q6v5.fatal_irq, + IRQCHIP_STATE_LINE_LEVEL, &fatal); + if (ret) + return ret; + + ret = irq_get_irqchip_state(pas->q6v5.stop_irq, + IRQCHIP_STATE_LINE_LEVEL, &stop); + if (ret) + return ret; + + *crashed = fatal; + *running = ready && handover && !stop && !fatal; + + return 0; +} + static int qcom_pas_attach(struct rproc *rproc) { int ret; struct qcom_pas *pas = rproc->priv; + bool running; bool ready_state; bool crash_state; pas->q6v5.running = true; + + if (pas->broken_reset) { + ret = qcom_pas_read_smp2p_state(pas, &running, &crash_state); + if (ret) + goto disable_running; + + if (crash_state) { + dev_err(pas->dev, "Subsystem has crashed before driver probe\n"); + rproc_report_crash(rproc, RPROC_FATAL_ERROR); + ret = -EINVAL; + goto disable_running; + } + + if (!running) { + dev_err(pas->dev, "Failed to detect already running subsystem\n"); + pas->rproc->state = RPROC_OFFLINE; + ret = -EINVAL; + goto disable_running; + } + + pas->q6v5.handover_issued = true; + + return 0; + } + ret = irq_get_irqchip_state(pas->q6v5.fatal_irq, IRQCHIP_STATE_LINE_LEVEL, &crash_state); @@ -593,6 +665,7 @@ static const struct rproc_ops qcom_pas_minidump_ops = { .parse_fw = qcom_pas_parse_firmware, .load = qcom_pas_load, .panic = qcom_pas_panic, + .attach = qcom_pas_attach, .coredump = qcom_pas_minidump, }; @@ -805,6 +878,9 @@ static int qcom_pas_probe(struct platform_device *pdev) struct rproc *rproc; const char *fw_name, *dtb_fw_name = NULL; const struct rproc_ops *ops = &qcom_pas_ops; + bool broken_reset; + bool crashed; + bool running; int ret; desc = of_device_get_match_data(&pdev->dev); @@ -831,6 +907,8 @@ static int qcom_pas_probe(struct platform_device *pdev) if (desc->minidump_id) ops = &qcom_pas_minidump_ops; + broken_reset = of_property_read_bool(pdev->dev.of_node, "qcom,broken-reset"); + rproc = devm_rproc_alloc(&pdev->dev, desc->sysmon_name, ops, fw_name, sizeof(*pas)); if (!rproc) { @@ -839,7 +917,7 @@ static int qcom_pas_probe(struct platform_device *pdev) } rproc->has_iommu = of_property_present(pdev->dev.of_node, "iommus"); - rproc->auto_boot = desc->auto_boot; + rproc->auto_boot = desc->auto_boot && !broken_reset; rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); pas = rproc->priv; @@ -852,6 +930,7 @@ static int qcom_pas_probe(struct platform_device *pdev) pas->info_name = desc->sysmon_name; pas->smem_host_id = desc->smem_host_id; pas->decrypt_shutdown = desc->decrypt_shutdown; + pas->broken_reset = broken_reset; pas->region_assign_idx = desc->region_assign_idx; pas->region_assign_count = min_t(int, MAX_ASSIGN_COUNT, desc->region_assign_count); pas->region_assign_vmid = desc->region_assign_vmid; @@ -921,7 +1000,19 @@ static int qcom_pas_probe(struct platform_device *pdev) pas->pas_ctx->use_tzmem = rproc->has_iommu; pas->dtb_pas_ctx->use_tzmem = rproc->has_iommu; - if (desc->early_boot) { + if (pas->broken_reset) { + ret = qcom_pas_read_smp2p_state(pas, &running, &crashed); + if (ret) { + dev_warn(&pdev->dev, "Failed to read SMP2P state; auto boot disabled\n"); + } else if (running) { + pas->rproc->state = RPROC_DETACHED; + pas->rproc->auto_boot = desc->auto_boot; + } else if (crashed) { + dev_warn(&pdev->dev, "Subsystem is crashed before attach\n"); + } else { + dev_warn(&pdev->dev, "Subsystem is not running; auto boot disabled\n"); + } + } else if (desc->early_boot) { ret = qcom_q6v5_ping_subsystem_init(&pas->q6v5, pdev); if (ret) dev_warn(&pdev->dev, "Falling back to firmware load\n"); diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 5be37eeea329f..1cd8774f245ca 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -159,6 +159,31 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct } } +static int q6apm_lpass_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!dai_data->is_port_started[dai->id]) { + ret = q6apm_graph_start(dai_data->graph[dai->id]); + if (ret < 0) + dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); + else + dai_data->is_port_started[dai->id] = true; + } + break; + default: + break; + } + + return ret; +} + static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -171,10 +196,6 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s q6apm_graph_stop(dai_data->graph[dai->id]); dai_data->is_port_started[dai->id] = false; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - q6apm_graph_close(dai_data->graph[dai->id]); - dai_data->graph[dai->id] = NULL; - } } /** @@ -203,14 +224,6 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s dev_err(dai->dev, "Failed to prepare Graph %d\n", rc); goto err; } - - rc = q6apm_graph_start(dai_data->graph[dai->id]); - if (rc < 0) { - dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); - goto err; - } - dai_data->is_port_started[dai->id] = true; - return 0; err: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -254,6 +267,7 @@ static const struct snd_soc_dai_ops q6dma_ops = { .shutdown = q6apm_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_dai_ops q6i2s_ops = { @@ -263,6 +277,7 @@ static const struct snd_soc_dai_ops q6i2s_ops = { .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, .set_fmt = q6i2s_set_fmt, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_dai_ops q6hdmi_ops = { @@ -271,6 +286,7 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { .shutdown = q6apm_lpass_dai_shutdown, .hw_params = q6hdmi_hw_params, .set_fmt = q6i2s_set_fmt, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_component_driver q6apm_lpass_dai_component = {