// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include #include #include "iris_core.h" #include "iris_instance.h" #include "iris_vpu_common.h" #include "iris_vpu_register_defines.h" #define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL (AON_BASE_OFFS + 0x2C) #define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS (AON_BASE_OFFS + 0x30) #define CTRL_INIT (CPU_CS_BASE_OFFS + 0x48) #define CTRL_STATUS (CPU_CS_BASE_OFFS + 0x4C) #define CTRL_INIT_IDLE_MSG_BMSK 0x40000000 #define CTRL_ERROR_STATUS__M 0xfe #define CTRL_STATUS_PC_READY 0x100 #define QTBL_INFO (CPU_CS_BASE_OFFS + 0x50) #define QTBL_ENABLE BIT(0) #define QTBL_ADDR (CPU_CS_BASE_OFFS + 0x54) #define CPU_CS_SCIACMDARG3 (CPU_CS_BASE_OFFS + 0x58) #define SFR_ADDR (CPU_CS_BASE_OFFS + 0x5C) #define UC_REGION_ADDR (CPU_CS_BASE_OFFS + 0x64) #define UC_REGION_SIZE (CPU_CS_BASE_OFFS + 0x68) static void iris_vpu_interrupt_init(struct iris_core *core) { u32 mask_val; mask_val = readl(core->reg_base + WRAPPER_INTR_MASK); mask_val &= ~(WRAPPER_INTR_MASK_A2HWD_BMSK | WRAPPER_INTR_MASK_A2HCPU_BMSK); writel(mask_val, core->reg_base + WRAPPER_INTR_MASK); } static void iris_vpu_setup_ucregion_memory_map(struct iris_core *core) { u32 queue_size, value; const struct vpu_ops *vpu_ops = core->iris_platform_data->vpu_ops; /* Iris hardware requires 4K queue alignment */ queue_size = ALIGN(sizeof(struct iris_hfi_queue_table_header) + (IFACEQ_QUEUE_SIZE * IFACEQ_NUMQ), SZ_4K); value = (u32)core->iface_q_table_daddr; writel(value, core->reg_base + UC_REGION_ADDR); /* Iris hardware requires 1M queue alignment */ value = ALIGN(SFR_SIZE + queue_size, SZ_1M); writel(value, core->reg_base + UC_REGION_SIZE); value = (u32)core->iface_q_table_daddr; writel(value, core->reg_base + QTBL_ADDR); writel(QTBL_ENABLE, core->reg_base + QTBL_INFO); if (core->sfr_daddr) { value = (u32)core->sfr_daddr + core->iris_platform_data->core_arch; writel(value, core->reg_base + SFR_ADDR); } if (vpu_ops->program_bootup_registers) vpu_ops->program_bootup_registers(core); } int iris_vpu_boot_firmware(struct iris_core *core) { u32 ctrl_init = BIT(0), ctrl_status = 0, count = 0, max_tries = 1000; iris_vpu_setup_ucregion_memory_map(core); writel(ctrl_init, core->reg_base + CTRL_INIT); writel(0x1, core->reg_base + CPU_CS_SCIACMDARG3); while (!ctrl_status && count < max_tries) { ctrl_status = readl(core->reg_base + CTRL_STATUS); if ((ctrl_status & CTRL_ERROR_STATUS__M) == 0x4) { dev_err(core->dev, "invalid setting for uc_region\n"); break; } usleep_range(50, 100); count++; } if (count >= max_tries) { dev_err(core->dev, "error booting up iris firmware\n"); return -ETIME; } writel(HOST2XTENSA_INTR_ENABLE, core->reg_base + CPU_CS_H2XSOFTINTEN); writel(0x0, core->reg_base + CPU_CS_X2RPMH); return 0; } void iris_vpu_raise_interrupt(struct iris_core *core) { writel(1 << CPU_IC_SOFTINT_H2A_SHFT, core->reg_base + CPU_IC_SOFTINT); } void iris_vpu_clear_interrupt(struct iris_core *core) { u32 intr_status, mask; intr_status = readl(core->reg_base + WRAPPER_INTR_STATUS); mask = (WRAPPER_INTR_STATUS_A2H_BMSK | WRAPPER_INTR_STATUS_A2HWD_BMSK | CTRL_INIT_IDLE_MSG_BMSK); if (intr_status & mask) core->intr_status |= intr_status; writel(CLEAR_XTENSA2HOST_INTR, core->reg_base + CPU_CS_A2HSOFTINTCLR); } int iris_vpu_watchdog(struct iris_core *core, u32 intr_status) { if (intr_status & WRAPPER_INTR_STATUS_A2HWD_BMSK) { dev_err(core->dev, "received watchdog interrupt\n"); return -ETIME; } return 0; } int iris_vpu_prepare_pc(struct iris_core *core) { u32 wfi_status, idle_status, pc_ready; u32 ctrl_status, val = 0; int ret; ctrl_status = readl(core->reg_base + CTRL_STATUS); pc_ready = ctrl_status & CTRL_STATUS_PC_READY; idle_status = ctrl_status & BIT(30); if (pc_ready) return 0; wfi_status = readl(core->reg_base + WRAPPER_TZ_CPU_STATUS); wfi_status &= BIT(0); if (!wfi_status || !idle_status) goto skip_power_off; ret = core->hfi_ops->sys_pc_prep(core); if (ret) goto skip_power_off; ret = readl_poll_timeout(core->reg_base + CTRL_STATUS, val, val & CTRL_STATUS_PC_READY, 250, 2500); if (ret) goto skip_power_off; ret = readl_poll_timeout(core->reg_base + WRAPPER_TZ_CPU_STATUS, val, val & BIT(0), 250, 2500); if (ret) goto skip_power_off; return 0; skip_power_off: ctrl_status = readl(core->reg_base + CTRL_STATUS); wfi_status = readl(core->reg_base + WRAPPER_TZ_CPU_STATUS); wfi_status &= BIT(0); dev_err(core->dev, "skip power collapse, wfi=%#x, idle=%#x, pcr=%#x, ctrl=%#x)\n", wfi_status, idle_status, pc_ready, ctrl_status); return -EAGAIN; } int iris_vpu_power_off_controller(struct iris_core *core) { u32 val = 0; int ret; writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH); if (!core->iris_platform_data->no_aon) { writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS, val, val & BIT(0), 200, 2000); if (ret) goto disable_power; } writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL); ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS, val, val & BIT(0), 200, 2000); if (ret) goto disable_power; writel(0x0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL); ret = readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS, val, val == 0, 200, 2000); if (ret) goto disable_power; writel(CTL_AXI_CLK_HALT | CTL_CLK_HALT, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG); writel(RESET_HIGH, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET); writel(0x0, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET); writel(0x0, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG); disable_power: iris_disable_unprepare_clock(core, IRIS_AHB_CLK); iris_disable_unprepare_clock(core, IRIS_CTRL_CLK); iris_disable_unprepare_clock(core, IRIS_AXI_CLK); iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); return 0; } void iris_vpu_power_off_hw(struct iris_core *core) { dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], false); iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK); iris_disable_unprepare_clock(core, IRIS_HW_CLK); } void iris_vpu_power_off(struct iris_core *core) { iris_opp_set_rate(core->dev, 0); core->iris_platform_data->vpu_ops->power_off_hw(core); core->iris_platform_data->vpu_ops->power_off_controller(core); iris_unset_icc_bw(core); if (!iris_vpu_watchdog(core, core->intr_status)) disable_irq_nosync(core->irq); } int iris_vpu_power_on_controller(struct iris_core *core) { u32 rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size; int ret; ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); if (ret) return ret; ret = reset_control_bulk_reset(rst_tbl_size, core->resets); if (ret) goto err_disable_power; ret = iris_prepare_enable_clock(core, IRIS_AXI_CLK); if (ret) goto err_disable_power; ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK); if (ret) goto err_disable_axi_clock; ret = iris_prepare_enable_clock(core, IRIS_AHB_CLK); if (ret && ret != -ENOENT) goto err_disable_ctrl_clock; return 0; err_disable_ctrl_clock: iris_disable_unprepare_clock(core, IRIS_CTRL_CLK); err_disable_axi_clock: iris_disable_unprepare_clock(core, IRIS_AXI_CLK); err_disable_power: iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); return ret; } int iris_vpu_power_on_hw(struct iris_core *core) { int ret; ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); if (ret) return ret; ret = iris_prepare_enable_clock(core, IRIS_HW_CLK); if (ret) goto err_disable_power; ret = iris_prepare_enable_clock(core, IRIS_HW_AHB_CLK); if (ret && ret != -ENOENT) goto err_disable_hw_clock; ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], true); if (ret) goto err_disable_hw_ahb_clock; return 0; err_disable_hw_ahb_clock: iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK); err_disable_hw_clock: iris_disable_unprepare_clock(core, IRIS_HW_CLK); err_disable_power: iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); return ret; } int iris_vpu35_vpu4x_power_off_controller(struct iris_core *core) { u32 clk_rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size; bool handshake_done, handshake_busy; u32 count = 0, val = 0; int ret; writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH); writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL); ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS, val, val & BIT(0), 200, 2000); if (ret) goto disable_power; writel(0, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL); /* Retry up to 1000 times as recommended by hardware documentation */ do { /* set MNoC to low power */ writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL); usleep_range(10, 20); val = readl(core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS); handshake_done = val & NOC_LPI_STATUS_DONE; handshake_busy = val & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE); if (handshake_done || !handshake_busy) break; writel(0, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL); usleep_range(10, 20); } while (++count < 1000); if (!handshake_done && handshake_busy) dev_err(core->dev, "LPI handshake timeout\n"); ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS, val, val & BIT(0), 200, 2000); if (ret) goto disable_power; writel(0, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL); writel(0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL); readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS, val, val == 0, 200, 2000); disable_power: iris_disable_unprepare_clock(core, IRIS_CTRL_CLK); iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK); iris_disable_unprepare_clock(core, IRIS_AXI1_CLK); iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); reset_control_bulk_reset(clk_rst_tbl_size, core->resets); return 0; } int iris_vpu35_vpu4x_power_on_controller(struct iris_core *core) { int ret; ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); if (ret) return ret; ret = iris_prepare_enable_clock(core, IRIS_AXI1_CLK); if (ret) goto err_disable_power; ret = iris_prepare_enable_clock(core, IRIS_CTRL_FREERUN_CLK); if (ret) goto err_disable_axi1_clk; ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK); if (ret) goto err_disable_ctrl_free_clk; return 0; err_disable_ctrl_free_clk: iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK); err_disable_axi1_clk: iris_disable_unprepare_clock(core, IRIS_AXI1_CLK); err_disable_power: iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); return ret; } void iris_vpu35_vpu4x_program_bootup_registers(struct iris_core *core) { writel(0x1, core->reg_base + WRAPPER_IRIS_VCODEC_VPU_WRAPPER_SPARE_0); } u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_size) { struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps; struct v4l2_format *inp_f = inst->fmt_src; u32 height, width, mbs_per_second, mbpf; u64 fw_cycles, fw_vpp_cycles; u64 vsp_cycles, vpp_cycles; u32 fps = DEFAULT_FPS; width = max(inp_f->fmt.pix_mp.width, inst->crop.width); height = max(inp_f->fmt.pix_mp.height, inst->crop.height); mbpf = NUM_MBS_PER_FRAME(height, width); mbs_per_second = mbpf * fps; fw_cycles = fps * caps->mb_cycles_fw; fw_vpp_cycles = fps * caps->mb_cycles_fw_vpp; vpp_cycles = mult_frac(mbs_per_second, caps->mb_cycles_vpp, (u32)inst->fw_caps[PIPE].value); /* 21 / 20 is minimum overhead factor */ vpp_cycles += max(div_u64(vpp_cycles, 20), fw_vpp_cycles); /* 1.059 is multi-pipe overhead */ if (inst->fw_caps[PIPE].value > 1) vpp_cycles += div_u64(vpp_cycles * 59, 1000); vsp_cycles = fps * data_size * 8; vsp_cycles = div_u64(vsp_cycles, 2); /* VSP FW overhead 1.05 */ vsp_cycles = div_u64(vsp_cycles * 21, 20); if (inst->fw_caps[STAGE].value == STAGE_1) vsp_cycles = vsp_cycles * 3; return max3(vpp_cycles, vsp_cycles, fw_cycles); } int iris_vpu_power_on(struct iris_core *core) { u32 freq; int ret; ret = iris_set_icc_bw(core, INT_MAX); if (ret) goto err; ret = core->iris_platform_data->vpu_ops->power_on_controller(core); if (ret) goto err_unvote_icc; ret = core->iris_platform_data->vpu_ops->power_on_hw(core); if (ret) goto err_power_off_ctrl; freq = core->power.clk_freq ? core->power.clk_freq : (u32)ULONG_MAX; iris_opp_set_rate(core->dev, freq); core->iris_platform_data->set_preset_registers(core); iris_vpu_interrupt_init(core); core->intr_status = 0; enable_irq(core->irq); return 0; err_power_off_ctrl: core->iris_platform_data->vpu_ops->power_off_controller(core); err_unvote_icc: iris_unset_icc_bw(core); err: dev_err(core->dev, "power on failed\n"); return ret; }