From 16ebd65ea7ecfbbf0adc95163911f4cfe43e83f2 Mon Sep 17 00:00:00 2001 From: Vaishnavi Madireddy Date: Mon, 28 Sep 2026 15:40:47 +0530 Subject: [PATCH] Support for Shikra added for Video Address all reviewer comments on the Shikra V4L2 support: utils/functestlib.sh - Add platform_runtime_identity() and platform_identity_matches() as the single source of truth for board identity shared by audio, video, and future callers. utils/lib_video.sh - Add Shikra platform detection and upstream-only stack support. - Add video_qcom_iris_active() combining lsmod and sysfs binding evidence to handle both modular and built-in qcom_iris drivers. - Fix video_stage_control_override() AWK compact-JSON scoping: use a placeholder-based split so sub() applies only to the portion of the line after the matched Id. - Add video_policy_lookup() with Level-4.0 cap for Shikra H.264/HEVC encode; restrict automatic rewrite to bundled fixtures by default. - Remove Hamoa from video_detect_platform() (separate PR). suites/Multimedia/Video/Video_V4L2_Runner/run.sh - Add Shikra post-stack module validation. - Add bundled-fixture guard before video_policy_lookup. - Document --platform shikra, VIDEO_POLICY_SCOPE, and VIDEO_POLICY_EXTRA in --help. suites/Multimedia/Video/Video_V4L2_Runner/README_Video.md - Add Shikra Platform Support section. suites/Multimedia/Video/Video_V4L2_Runner/Video_V4L2_Runner.yaml - Add VIDEO_STACK and VIDEO_POLICY_SCOPE params; wire --stack into run. Signed-off-by: Vaishnavi Madireddy --- .../Video/Video_V4L2_Runner/README_Video.md | 50 +- .../Video_V4L2_Runner/Video_V4L2_Runner.yaml | 22 +- .../Multimedia/Video/Video_V4L2_Runner/run.sh | 118 ++++- Runner/utils/audio_common.sh | 38 +- Runner/utils/functestlib.sh | 55 ++ Runner/utils/lib_video.sh | 492 +++++++++++++++++- 6 files changed, 715 insertions(+), 60 deletions(-) diff --git a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/README_Video.md b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/README_Video.md index a5d3d5fb9..ee37ff6de 100644 --- a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/README_Video.md +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/README_Video.md @@ -14,6 +14,55 @@ The suite includes a **reboot-free video stack switcher** (upstream ↔ downstre --- +## Shikra Platform Support + +**Shikra** is an upstream-only platform. The sole video driver is `qcom_iris`, which may be +modular (visible in `lsmod`) or compiled in as a built-in (bound under +`/sys/bus/platform/drivers/qcom_iris`). Both cases are detected automatically. + +| Property | Value | +|---|---| +| Supported stack | `upstream` only (`--stack upstream` or `--stack auto`) | +| Downstream stack | Not supported — rejected with a clear error | +| Video driver | `qcom_iris` (modular or built-in) | +| Max H.264/HEVC encode Level | **4.0** (automatically applied to bundled fixtures) | + +### Automatic Level cap for bundled encoder fixtures + +On Shikra, Level 4.0 is the maximum supported H.264 and HEVC encode level. +The runner automatically rewrites the `Level` control to `4.0` in the four +bundled encoder configs before execution: + +- `base_h264Encoder.json` +- `base_h265Encoder.json` +- `overlay_h264Encoder.json` +- `overlay_h265Encoder.json` + +User-supplied configs passed via `--config` are **not** modified by default. +Set `VIDEO_POLICY_SCOPE=all` to apply the rewrite to every config. + +### Extending the policy table + +Additional per-platform control overrides can be injected at runtime without +modifying any source file: + +```sh +# Apply a Profile override for Shikra H.264 encode in addition to the built-in Level cap +export VIDEO_POLICY_EXTRA="shikra|encode|h264|Profile|High" +./run-test.sh Video_V4L2_Runner --app /path/to/iris_v4l2_test --stack upstream +``` + +Format: `platform|mode|codec|control|value` (one row per line, pipe-separated). + +### Example: run all configs on Shikra + +```sh +# From the Runner/ directory on the device: +./run-test.sh Video_V4L2_Runner --app /path/to/iris_v4l2_test --stack upstream +``` + +--- + ## What’s New (since 2025‑10‑03) - **Network stabilization delay (post-connect)** @@ -350,4 +399,3 @@ export VIDEO_INTER_TEST_SLEEP=3 Ensure time is sane (TLS), network is reachable, and provide Wi‑Fi creds via env or `ssid_list.txt`. The downloader uses BusyBox‑compatible flags with retries and a final TLS‑lenient attempt if needed. When the network remains unreachable, the runner **SKIPs** decode cases. --- - diff --git a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/Video_V4L2_Runner.yaml b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/Video_V4L2_Runner.yaml index 3a9d7aaa7..37c2e16e0 100644 --- a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/Video_V4L2_Runner.yaml +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/Video_V4L2_Runner.yaml @@ -1,7 +1,12 @@ metadata: name: video-v4l2-runner format: "Lava-Test Test Definition 1.0" - description: "These scripts automate validation of video **encoding** and **decoding** on Qualcomm Linux platforms running a Yocto-based rootfs." + description: > + Automates V4L2 video encoding and decoding validation on Qualcomm Linux + platforms (LeMans, Monaco, Kodiak, Shikra) using iris_v4l2_test. + Shikra is upstream-only (qcom_iris driver); set VIDEO_STACK=upstream. + Level 4.0 is automatically applied to bundled Shikra encoder fixtures. + Set VIDEO_POLICY_SCOPE=all to extend the rewrite to user-supplied configs. os: - linux scope: @@ -10,10 +15,19 @@ metadata: params: # path to iris_v4l2_test binary APP_PATH: "/usr/bin/iris_v4l2_test" - # specific config can be selected. Available: h264Decoder.json, h264Encoder.json, h265Decoder.json, h265Encoder.json, vp9Decoder.json. Default is to use all of them + # specific config can be selected. Available: base_h264Decoder.json, + # base_h264Encoder.json, base_h265Decoder.json, base_h265Encoder.json, + # base_vp9Decoder.json, overlay_*.json. Default is to use all of them. CONFIG_PATH: "./" - # platform is autodetected by the test script + # platform is autodetected by the test script (lemans|monaco|kodiak|shikra) PLATFORM: "" + # stack selection: auto|upstream|downstream|base|overlay|both + # Shikra supports upstream only; use VIDEO_STACK=upstream or leave as auto. + VIDEO_STACK: "auto" + # policy scope: bundled (default) | all + # 'bundled' restricts Level-4.0 rewrite to the four bundled encoder fixtures. + # 'all' applies the rewrite to every config including user-supplied ones. + VIDEO_POLICY_SCOPE: "bundled" LOGLEVEL: 15 RETRY_ON_FAIL: 5 POST_TEST_SLEEP: 10 @@ -22,5 +36,5 @@ run: steps: - REPO_PATH=$PWD - cd Runner/suites/Multimedia/Video/Video_V4L2_Runner/ - - ./run.sh --app "${APP_PATH}" --config "${CONFIG_PATH}" --platform "${PLATFORM}" --loglevel "${LOGLEVEL}" --retry-on-fail "${RETRY_ON_FAIL}" --post-test-sleep "${POST_TEST_SLEEP}" || true + - VIDEO_POLICY_SCOPE="${VIDEO_POLICY_SCOPE}" ./run.sh --app "${APP_PATH}" --config "${CONFIG_PATH}" --platform "${PLATFORM}" --stack "${VIDEO_STACK}" --loglevel "${LOGLEVEL}" --retry-on-fail "${RETRY_ON_FAIL}" --post-test-sleep "${POST_TEST_SLEEP}" || true - $REPO_PATH/Runner/utils/send-to-lava.sh Video_V4L2_Runner.res || true diff --git a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh index 72fa5bdca..78147c5aa 100755 --- a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh @@ -104,7 +104,7 @@ Usage: $0 [--config path.json|/path/dir] [--dir DIR] [--pattern GLOB] [--repeat N] [--repeat-delay S] [--repeat-policy all|any] [--junit FILE] [--dry-run] [--verbose] [--stack auto|upstream|downstream|base|overlay|up|down|both] - [--platform lemans|monaco|kodiak] + [--platform lemans|monaco|kodiak|shikra] [--downstream-fw PATH] [--force] [--app /path/to/iris_v4l2_test] [--ssid SSID] [--password PASS] @@ -120,6 +120,23 @@ Usage: $0 [--config path.json|/path/dir] [--dir DIR] [--pattern GLOB] # --- Media bundle (opt-in, local tar) --- [--clips-tar /path/to/clips.tar.gz] # extract locally even if --dir/--config is used [--clips-dest DIR] # extraction destination (defaults to cfg/dir root or testcase dir) + +Platform notes: + shikra Upstream-only platform (qcom_iris driver, modular or built-in). + Downstream stack is not supported. --stack upstream (or auto) is + the only valid choice. Level 4.0 is the maximum supported H.264/HEVC + encode level and is automatically applied to the bundled encoder + fixtures (base_h264Encoder.json, base_h265Encoder.json, + overlay_h264Encoder.json, overlay_h265Encoder.json). + +Environment variables: + VIDEO_POLICY_SCOPE bundled (default) | all + Controls which configs receive automatic platform policy rewrites. + 'bundled' restricts rewrites to the bundled fixture configs above. + 'all' applies the policy to every config, including user-supplied ones. + VIDEO_POLICY_EXTRA Additional policy rows in pipe-separated format: + platform|mode|codec|control|value (one row per line). + Example: VIDEO_POLICY_EXTRA="shikra|encode|h264|Profile|High" EOF } @@ -933,6 +950,15 @@ case "$plat" in fi fi ;; + shikra) + if [ "$post_stack" = "upstream" ]; then + if video_qcom_iris_active; then + log_pass "Upstream validated: qcom_iris present (Shikra)" + else + log_warn "Upstream expected but qcom_iris not present (Shikra)" + fi + fi + ;; *) log_warn "Unknown platform; skipping strict module validation" ;; @@ -1157,6 +1183,88 @@ while IFS= read -r cfg; do continue fi + # ----------------------------------------------------------------------- + # Stage the effective config once per test case, before the repeat loop. + # Both normal runs and retries use the same staged path so they cannot + # diverge. Platforms with no matching policy row use the original config + # unchanged (no-op path through video_policy_lookup). + # + # Policy rewrites are restricted to the bundled fixture configs by default + # (base_*.json and overlay_*.json) so that user-supplied configs passed via + # --config are not silently modified. Set VIDEO_POLICY_SCOPE=all to apply + # the policy to every config regardless of filename. + # ----------------------------------------------------------------------- + effective_cfg="$cfg" + vpl_policy_file="$LOG_DIR/.policy_${id}.$$.txt" + # Resolve the config to an absolute path and compare against the actual + # bundled fixture paths in the suite directory. A config that merely has + # the same basename as a bundled fixture (e.g. /data/base_h264Encoder.json) + # is treated as user-supplied and is not rewritten by default. + vpl_cfg_resolved="$(cd "$(dirname "$cfg")" 2>/dev/null && pwd)/$(basename "$cfg")" + vpl_apply_policy=0 + case "$vpl_cfg_resolved" in + "$SCRIPT_DIR/base_h264Encoder.json"|\ + "$SCRIPT_DIR/base_h265Encoder.json"|\ + "$SCRIPT_DIR/overlay_h264Encoder.json"|\ + "$SCRIPT_DIR/overlay_h265Encoder.json") + vpl_apply_policy=1 + ;; + *) + if [ "${VIDEO_POLICY_SCOPE:-bundled}" = "all" ]; then + vpl_apply_policy=1 + fi + ;; + esac + if [ "$vpl_apply_policy" -eq 1 ]; then + video_policy_lookup "$plat" "$mode" "$codec" > "$vpl_policy_file" 2>/dev/null || true + fi + + if [ -s "$vpl_policy_file" ]; then + vpl_staged="$cfg" + vpl_stage_ok=1 + while IFS='|' read -r vpl_ctrl vpl_val; do + [ -z "$vpl_ctrl" ] && continue + vpl_result="$(video_stage_control_override \ + "$vpl_staged" "$vpl_ctrl" "$vpl_val" "$LOG_DIR")" + vpl_rc=$? + if [ "$vpl_rc" -eq 2 ]; then + # Policy requires this control but the config does not declare it. + # Running the original config would silently use the platform default + # (potentially unsupported) value — fail preparation instead. + log_warn "[$id] Required control '$vpl_ctrl' is absent from config '$vpl_staged'; aborting preparation" + vpl_stage_ok=0 + break + fi + if [ "$vpl_rc" -ne 0 ]; then + log_warn "[$id] Config staging failed for control '$vpl_ctrl'; aborting preparation" + vpl_stage_ok=0 + break + fi + if [ -n "$vpl_result" ] && [ -f "$vpl_result" ]; then + vpl_staged="$vpl_result" + fi + done < "$vpl_policy_file" + + if [ "$vpl_stage_ok" -eq 0 ]; then + rm -f "$vpl_policy_file" 2>/dev/null || true + log_fail "[$id] FAIL - config preparation failed" + printf '%s\n' "$id FAIL $pretty" >> "$LOG_DIR/summary.txt" + printf '%s\n' "$mode,$id,FAIL,$pretty,0,0,0" >> "$LOG_DIR/results.csv" + fail=$((fail + 1)) + suite_rc=1 + if [ "$STOP_ON_FAIL" -eq 1 ]; then + break + fi + continue + fi + + if [ "$vpl_staged" != "$cfg" ]; then + effective_cfg="$vpl_staged" + log_info "[$id] Using staged config: $effective_cfg" + fi + fi + rm -f "$vpl_policy_file" 2>/dev/null || true + pass_runs="0" fail_runs="0" rep="1" @@ -1169,7 +1277,7 @@ while IFS= read -r cfg; do fi video_step "$id" "Execute app" - log_info "[$id] CMD: $VIDEO_APP --config \"$cfg\" --loglevel $LOGLEVEL" + log_info "[$id] CMD: $VIDEO_APP --config \"$effective_cfg\" --loglevel $LOGLEVEL" case "$APP_LAUNCH_SLEEP" in ''|*[!0-9]* ) @@ -1184,7 +1292,7 @@ while IFS= read -r cfg; do ;; esac - if video_run_once "$cfg" "$logf" "$TIMEOUT" "$SUCCESS_RE" "$LOGLEVEL"; then + if video_run_once "$effective_cfg" "$logf" "$TIMEOUT" "$SUCCESS_RE" "$LOGLEVEL"; then pass_runs=$((pass_runs + 1)) else rc_val="$(awk -F'=' '/^END-RUN rc=/{print $2}' "$logf" 2>/dev/null | tail -n1 | tr -d ' ')" @@ -1238,7 +1346,7 @@ while IFS= read -r cfg; do fi # (2) Retry on final failure (extra attempts outside REPEAT loop, before recording results) - if [ "$final" = "FAIL" ] && [ "$RETRY_ON_FAIL" -gt 0 ] 2>/dev/null; then + if [ "$final" = "FAIL" ] && [ "$RETRY_ON_FAIL" -gt 0 ] 2>/dev/null; then r=1 log_info "[$id] RETRY_ON_FAIL: up to $RETRY_ON_FAIL additional attempt(s)" while [ "$r" -le "$RETRY_ON_FAIL" ]; do @@ -1247,7 +1355,7 @@ while IFS= read -r cfg; do fi log_info "[$id] retry attempt $r/$RETRY_ON_FAIL" - if video_run_once "$cfg" "$logf" "$TIMEOUT" "$SUCCESS_RE" "$LOGLEVEL"; then + if video_run_once "$effective_cfg" "$logf" "$TIMEOUT" "$SUCCESS_RE" "$LOGLEVEL"; then pass_runs=$((pass_runs + 1)) final="PASS" log_pass "[$id] RETRY succeeded — marking PASS" diff --git a/Runner/utils/audio_common.sh b/Runner/utils/audio_common.sh index 322371d8d..cf3dc1b4e 100755 --- a/Runner/utils/audio_common.sh +++ b/Runner/utils/audio_common.sh @@ -2779,39 +2779,13 @@ audio_remoteproc_is_modem() { } # Return success when runtime platform evidence identifies a Shikra target. -# The preflight runs before the suites call detect_platform, so inspect both -# any already-populated platform variables and the standard runtime DT paths. +# +# All repository callers source functestlib.sh before audio_common.sh, +# including child user-session paths. platform_identity_matches() from +# functestlib.sh is therefore always available and is the single contract. +# If standalone sourcing is required, source functestlib.sh explicitly first. audio_platform_is_shikra() { - apis_identity="${PLATFORM_MACHINE:-} ${PLATFORM_TARGET:-}" - apis_identity="$apis_identity ${PLATFORM_SOC_MACHINE:-}" - apis_identity="$apis_identity ${PLATFORM_DT_MODEL:-}" - apis_identity="$apis_identity ${PLATFORM_DT_COMPAT:-}" - - for apis_file in \ - /proc/device-tree/model \ - /proc/device-tree/compatible \ - /sys/firmware/devicetree/base/model \ - /sys/firmware/devicetree/base/compatible - do - if [ -r "$apis_file" ]; then - apis_value="$(tr '\000' ' ' <"$apis_file" 2>/dev/null || true)" - apis_identity="$apis_identity $apis_value" - fi - done - - apis_identity="$( - printf '%s\n' "$apis_identity" | - tr '[:upper:]' '[:lower:]' - )" - - case "$apis_identity" in - *shikra*) - return 0 - ;; - *) - return 1 - ;; - esac + platform_identity_matches "shikra" } # Reorder an inventory so a modem-hosted audio path is considered before DSP diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index 7ceaeae1e..765102da9 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -8580,6 +8580,61 @@ detect_platform() { return 0 } +# ----------------------------------------------------------------------------- +# platform_runtime_identity +# +# Prints a lowercased, space-joined identity string built from all available +# platform evidence (soc0 sysfs, device-tree model/compatible, and any vars +# already exported by detect_platform). Callers treat an empty result as +# "no match" rather than falling back to a name heuristic. +# This is the single source of truth for board identity shared by audio, +# video, and any other subsystem library. +# ----------------------------------------------------------------------------- +platform_runtime_identity() { + pri_identity="${PLATFORM_MACHINE:-} ${PLATFORM_TARGET:-}" + pri_identity="$pri_identity ${PLATFORM_SOC_MACHINE:-}" + pri_identity="$pri_identity ${PLATFORM_DT_MODEL:-}" + pri_identity="$pri_identity ${PLATFORM_DT_COMPAT:-}" + + for pri_dtf in \ + /proc/device-tree/model \ + /proc/device-tree/compatible \ + /sys/firmware/devicetree/base/model \ + /sys/firmware/devicetree/base/compatible + do + if [ -r "$pri_dtf" ]; then + pri_dtv="$(tr '\000' ' ' <"$pri_dtf" 2>/dev/null || true)" + pri_identity="$pri_identity $pri_dtv" + fi + done + + printf '%s\n' "$pri_identity" | tr '[:upper:]' '[:lower:]' +} + +# ----------------------------------------------------------------------------- +# platform_identity_matches +# +# Returns 0 when (case-insensitive) appears anywhere in the runtime +# platform identity string produced by platform_runtime_identity(). +# Returns 1 when there is no match or the identity string is empty. +# ----------------------------------------------------------------------------- +platform_identity_matches() { + pim_tok="$1" + [ -n "$pim_tok" ] || return 1 + + pim_identity="$(platform_runtime_identity)" + pim_tok_l="$(printf '%s' "$pim_tok" | tr '[:upper:]' '[:lower:]')" + + case "$pim_identity" in + *"$pim_tok_l"*) + return 0 + ;; + *) + return 1 + ;; + esac +} + # ---------- minimal root / FS helpers (Yocto-safe, no underscores) ---------- isroot() { uid="$(id -u 2>/dev/null || echo 1)"; [ "$uid" -eq 0 ]; } diff --git a/Runner/utils/lib_video.sh b/Runner/utils/lib_video.sh index c51b2a76b..b22a75610 100755 --- a/Runner/utils/lib_video.sh +++ b/Runner/utils/lib_video.sh @@ -75,6 +75,39 @@ video_has_module_loaded() { "$LSMOD" 2>/dev/null | awk '{print $1}' | grep -q "^$1$" } +# ----------------------------------------------------------------------------- +# video_qcom_iris_active +# +# Returns 0 when the qcom_iris upstream driver is active, whether loaded as a +# kernel module (CONFIG_VIDEO_QCOM_IRIS=m, appears in lsmod) or compiled in as +# a built-in driver (CONFIG_VIDEO_QCOM_IRIS=y, absent from lsmod but bound to +# platform devices). +# +# Built-in detection: the platform driver directory exists under +# /sys/bus/platform/drivers/ and contains at least one device symlink, +# indicating the driver is registered and bound to hardware. +# Both qcom_iris (underscore) and qcom-iris (hyphen) names are checked since +# the sysfs name uses the hyphenated form on some kernel versions. +# ----------------------------------------------------------------------------- +video_qcom_iris_active() { + # Modular: appears in lsmod (CONFIG_VIDEO_QCOM_IRIS=m) + if video_has_module_loaded qcom_iris; then + return 0 + fi + # Built-in: driver directory exists and is bound to at least one device + # (CONFIG_VIDEO_QCOM_IRIS=y). + for _vqia_drv in \ + /sys/bus/platform/drivers/qcom_iris \ + /sys/bus/platform/drivers/qcom-iris + do + if [ -d "$_vqia_drv" ] && \ + find "$_vqia_drv" -maxdepth 1 -type l 2>/dev/null | grep -q .; then + return 0 + fi + done + return 1 +} + video_devices_present() { set -- /dev/video* 2>/dev/null [ -e "$1" ] @@ -663,22 +696,50 @@ video_normalize_stack() { } # ----------------------------------------------------------------------------- -# Platform detect → lemans|monaco|kodiak|unknown +# video_get_dt_identity +# +# Delegates to platform_runtime_identity() from functestlib.sh when available +# (the shared single source of truth for board identity across audio and video). +# Falls back to an inline implementation with identical logic for environments +# where functestlib.sh is not sourced before lib_video.sh. +# +# Output: lowercased, space-joined identity string on stdout. May be empty +# if no device-tree/platform data is available (callers should treat that +# as "no match" rather than falling back to a caller-name heuristic). +# ----------------------------------------------------------------------------- +video_get_dt_identity() { + if command -v platform_runtime_identity >/dev/null 2>&1; then + platform_runtime_identity + return + fi + + # Inline fallback — identical logic to platform_runtime_identity(). + identity="${PLATFORM_MACHINE:-} ${PLATFORM_TARGET:-}" + identity="$identity ${PLATFORM_SOC_MACHINE:-}" + identity="$identity ${PLATFORM_DT_MODEL:-}" + identity="$identity ${PLATFORM_DT_COMPAT:-}" + + for dtf in \ + /proc/device-tree/model \ + /proc/device-tree/compatible \ + /sys/firmware/devicetree/base/model \ + /sys/firmware/devicetree/base/compatible + do + if [ -r "$dtf" ]; then + dtv="$(tr '\000' ' ' <"$dtf" 2>/dev/null || true)" + identity="$identity $dtv" + fi + done + + printf '%s\n' "$identity" | tr '[:upper:]' '[:lower:]' +} + +# ----------------------------------------------------------------------------- +# Platform detect → lemans|monaco|kodiak|shikra|unknown # ----------------------------------------------------------------------------- video_detect_platform() { - model="" - compat="" - - if [ -r /proc/device-tree/model ]; then - model=$(tr -d '\000' /dev/null) - fi - - if [ -r /proc/device-tree/compatible ]; then - compat=$(tr -d '\000' /dev/null) - fi - - s=$(printf '%s\n%s\n' "$model" "$compat" | tr '[:upper:]' '[:lower:]') - + s="$(video_get_dt_identity)" + # Monaco: qcs8300-ride, iq-8275-evk, qcs8275, generic qcs8300, or ride-sx+8300 monaco_pat='qcs8300-ride|iq-8275-evk|qcs8275|qcs8300|ride-sx.*8300|8300.*ride-sx' @@ -687,22 +748,30 @@ video_detect_platform() { # Kodiak: qcs6490, qcm6490, or rb3+6490 kodiak_pat='qcs6490|qcm6490|rb3.*6490|6490.*rb3' - + + # Shikra: matched by substring, same identity string as the other boards. + shikra_pat='shikra' + if printf '%s' "$s" | grep -Eq "$lemans_pat"; then printf '%s\n' "lemans" return 0 fi - + if printf '%s' "$s" | grep -Eq "$monaco_pat"; then printf '%s\n' "monaco" return 0 fi - + if printf '%s' "$s" | grep -Eq "$kodiak_pat"; then printf '%s\n' "kodiak" return 0 fi - + + if printf '%s' "$s" | grep -Eq "$shikra_pat"; then + printf '%s\n' "shikra" + return 0 + fi + printf '%s\n' "unknown" } @@ -737,6 +806,15 @@ video_validate_upstream_loaded() { return 1 ;; + + shikra) + # Shikra is upstream-only; qcom_iris is the sole driver (modular or + # built-in). Use video_qcom_iris_active to cover both cases. + if video_qcom_iris_active; then + return 0 + fi + return 1 + ;; esac return 1 @@ -757,6 +835,10 @@ video_validate_downstream_loaded() { fi return 1 ;; + shikra) + # Shikra is upstream-only; downstream is not supported. + return 1 + ;; *) return 1 ;; @@ -779,6 +861,9 @@ video_assert_stack() { kodiak) log_fail "[STACK] Upstream requested but venus_core/dec/enc are not all present." ;; + shikra) + log_fail "[STACK] Upstream requested but qcom_iris is not present (Shikra)." + ;; *) log_fail "[STACK] Upstream requested but platform '$plat' is unknown." ;; @@ -786,6 +871,10 @@ video_assert_stack() { return 1 ;; downstream|overlay|down) + if [ "$plat" = "shikra" ]; then + log_fail "[STACK] Downstream is not supported on Shikra (upstream-only platform)." + return 1 + fi if video_validate_downstream_loaded "$plat"; then return 0 fi @@ -858,6 +947,15 @@ video_stack_status() { return 0 fi ;; + + shikra) + # Shikra is upstream-only; qcom_iris is the sole driver (modular or + # built-in). Use video_qcom_iris_active to cover both cases. + if video_qcom_iris_active; then + printf '%s\n' "upstream" + return 0 + fi + ;; esac printf '%s\n' "unknown" @@ -900,6 +998,10 @@ video_unload_all_video_modules() { tryrmmod "$IRIS_VPU_MOD" tryrmmod "$IRIS_UP_MOD" ;; + shikra) + # Shikra is upstream-only; only qcom_iris needs to be unloaded. + tryrmmod "$IRIS_UP_MOD" + ;; *) : ;; @@ -992,6 +1094,22 @@ video_hot_switch_modules() { video_log_fw_hint fi ;; + shikra) + # Shikra is upstream-only; downstream is not supported. + if [ "$stack" = "downstream" ]; then + log_warn "Shikra is upstream-only; downstream stack is not supported." + rc=1 + else + video_unblock_mod_now "$IRIS_UP_MOD" + video_usleep "${MOD_SETTLE_SLEEP}" + video_unload_all_video_modules "$plat" + if ! video_modprobe_or_insmod "$IRIS_UP_MOD"; then + log_warn "modprobe $IRIS_UP_MOD failed on Shikra" + rc=1 + fi + video_usleep "${MOD_SETTLE_SLEEP}" + fi + ;; *) rc=1 ;; @@ -1945,6 +2063,9 @@ video_apply_blacklist_for_stack() { video_remove_blacklist "iris_vpu" fi ;; + shikra) + # Shikra is upstream-only; no blacklisting is needed for any stack request. + ;; *) return 1 ;; @@ -1981,8 +2102,343 @@ video_auto_preference_from_blacklist() { return 0 fi ;; + shikra) + # Shikra is upstream-only; always prefer upstream regardless of blacklist state. + printf '%s\n' "upstream" + return 0 + ;; esac printf '%s\n' "unknown" return 0 } + +# ----------------------------------------------------------------------------- +# Per-target policy overrides +# ----------------------------------------------------------------------------- +# Policy table: platform|mode|codec|control|value +# +# Adding a new SoC requires one new row in video_policy_lookup(); no run.sh +# changes are needed. The generic staging helper (video_stage_control_override) +# applies each row's control/value rewrite to a staged copy of the config, +# structurally validating that exactly one control object was changed. +# +# Public functions: +# video_target_matches -> 0/1 +# video_policy_lookup -> control|value lines +# video_stage_control_override -> staged path or empty +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# video_target_matches +# +# Case-insensitive substring match against the runtime board identity. +# Delegates to platform_identity_matches() from functestlib.sh when available +# (the shared contract consumed by both audio and video). Falls back to an +# inline implementation using video_get_dt_identity() for environments where +# functestlib.sh is not sourced before lib_video.sh. +# ----------------------------------------------------------------------------- +video_target_matches() { + tok="$1" + [ -z "$tok" ] && return 1 + + if command -v platform_identity_matches >/dev/null 2>&1; then + platform_identity_matches "$tok" + return + fi + + identity_l="$(video_get_dt_identity)" + tok_l="$(printf '%s' "$tok" | tr '[:upper:]' '[:lower:]')" + + case "$identity_l" in + *"$tok_l"*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +# ----------------------------------------------------------------------------- +# video_policy_lookup +# +# Prints zero or more "control|value" lines for the given platform/mode/codec +# combination. Adding a new SoC requires only a new row in the built-in table; +# no changes to run.sh are needed. +# +# Built-in policy table (platform|mode|codec|control|value): +# shikra|encode|h264|Level|4.0 +# shikra|encode|hevc|Level|4.0 +# +# Extend at runtime via VIDEO_POLICY_EXTRA (same pipe-separated format, +# one row per line). +# ----------------------------------------------------------------------------- +video_policy_lookup() { + vpl_plat="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" + vpl_mode="$(printf '%s' "$2" | tr '[:upper:]' '[:lower:]')" + vpl_codec="$(printf '%s' "$3" | tr '[:upper:]' '[:lower:]')" + + { + printf '%s\n' \ + "shikra|encode|h264|Level|4.0" \ + "shikra|encode|hevc|Level|4.0" + if [ -n "${VIDEO_POLICY_EXTRA:-}" ]; then + printf '%s\n' "$VIDEO_POLICY_EXTRA" + fi + } | while IFS='|' read -r p_plat p_mode p_codec p_ctrl p_val; do + [ -z "$p_plat" ] && continue + if [ "$p_plat" = "$vpl_plat" ] && \ + [ "$p_mode" = "$vpl_mode" ] && \ + [ "$p_codec" = "$vpl_codec" ]; then + printf '%s|%s\n' "$p_ctrl" "$p_val" + fi + done +} + +# ----------------------------------------------------------------------------- +# video_stage_control_override +# +# Rewrites the StaticControls entry with "Id": "" to +# "Value": "" in a staged copy of the config. The awk rewrite: +# - Tracks the enclosing control object so only the Value belonging to the +# matched Id is changed (not Value fields in Profile/BitRate/etc. objects). +# - Handles both compact single-line and pretty-printed multi-line layouts. +# - Verifies that exactly one control was changed; exits non-zero if the +# config is malformed (ambiguous Id/Value pairing) or if more than one +# match is found. +# +# Prints the staged file path on stdout when a change was made. +# Prints nothing and returns 0 when the file has no matching control (no-op). +# Returns 1 on structural error (malformed/ambiguous config). +# ----------------------------------------------------------------------------- +video_stage_control_override() { + vsc_cfg="$1" + vsc_ctrl="$2" + vsc_val="$3" + vsc_out_dir="$4" + + [ -z "$vsc_cfg" ] && return 0 + [ -f "$vsc_cfg" ] || return 0 + [ -z "$vsc_ctrl" ] && return 1 + [ -z "$vsc_val" ] && return 1 + + # Quick check: does this file have the target control at all? + # When a policy row was matched, the control is required — return a distinct + # code (2) so the caller can fail preparation rather than silently running + # the original config with the platform-default (potentially unsupported) value. + if ! grep -q "\"Id\"[[:space:]]*:[[:space:]]*\"${vsc_ctrl}\"" "$vsc_cfg" 2>/dev/null; then + return 2 + fi + + mkdir -p "$vsc_out_dir" 2>/dev/null || true + vsc_base="$(basename "$vsc_cfg" .json)" + vsc_tmp="${vsc_out_dir}/${vsc_base}.staged-${vsc_ctrl}.$$.json" + + # The awk program: + # - Restricts processing to the StaticControls array (enters on + # "StaticControls", exits on a bare ] at line start). + # - Tracks the enclosing control object so only the Value belonging to + # the matched Id is changed. + # - Handles both compact single-line and pretty-printed multi-line layouts. + # - Explicitly rejects non-string Value fields (e.g. "Value": 1) with + # exit code 3 — a string override cannot be applied to a non-string. + # + # Exit codes: + # 0 = exactly one string Value rewritten + # 1 = Id found, string Value found, already at desired setting (no-op) + # 2 = structural error (duplicate Id, missing Value, duplicate Value) + # 3 = non-string Value field — string override not applicable + awk -v ctrl="$vsc_ctrl" -v newval="$vsc_val" ' + BEGIN { + in_static = 0; in_ctrl_obj = 0 + changes = 0; found_id = 0; found_val = 0; non_string = 0 + } + { + line = $0 + + # Track StaticControls array scope. + if (!in_static && line ~ /"StaticControls"[[:space:]]*:/) { + in_static = 1 + } + if (in_static && line ~ /^[[:space:]]*\]/) { + in_static = 0 + in_ctrl_obj = 0 + } + + if (in_static) { + # Detect the target control Id within StaticControls. + if (line ~ ("\"Id\"[[:space:]]*:[[:space:]]*\"" ctrl "\"")) { + found_id++ + # Compact layout: Id and Value on the same line. + # Use object-boundary scanning to find the { before and } + # after the matched Id, then replace the Value only within + # that specific object. This handles both Id-before-Value + # and Value-before-Id orderings within the same object, + # preventing a Value field in an adjacent object from being + # overwritten when the entire JSON is on one line. + if (line ~ /"Value"[[:space:]]*:/) { + placeholder = "___IDMATCH___" + id_pat = "\"Id\"[[:space:]]*:[[:space:]]*\"" ctrl "\"" + temp = line + sub(id_pat, placeholder, temp) + ph_pos = index(temp, placeholder) + if (ph_pos > 0) { + before_ph = substr(temp, 1, ph_pos - 1) + after_ph = substr(temp, ph_pos + length(placeholder)) + # Scan backwards through before_ph to find the { + # that opens the object containing the placeholder. + obj_open_pos = 0 + depth = 0 + for (i = length(before_ph); i >= 1; i--) { + c = substr(before_ph, i, 1) + if (c == "}") depth++ + else if (c == "{") { + if (depth == 0) { obj_open_pos = i; break } + depth-- + } + } + # Scan forward through after_ph to find the } + # that closes the object containing the placeholder. + obj_close_pos = 0 + depth = 0 + for (i = 1; i <= length(after_ph); i++) { + c = substr(after_ph, i, 1) + if (c == "{") depth++ + else if (c == "}") { + if (depth == 0) { obj_close_pos = i; break } + depth-- + } + } + if (obj_open_pos > 0 && obj_close_pos > 0) { + obj_before = substr(before_ph, 1, obj_open_pos - 1) + obj_content = substr(before_ph, obj_open_pos) \ + placeholder \ + substr(after_ph, 1, obj_close_pos) + obj_after = substr(after_ph, obj_close_pos + 1) + if (obj_content ~ /"Value"[[:space:]]*:[[:space:]]*"[^"]*"/) { + orig_obj = obj_content + sub(/"Value"[[:space:]]*:[[:space:]]*"[^"]*"/, \ + "\"Value\": \"" newval "\"", obj_content) + if (obj_content != orig_obj) changes++ + found_val++ + sub(placeholder, "\"Id\": \"" ctrl "\"", obj_content) + line = obj_before obj_content obj_after + } else if (obj_content ~ /"Value"[[:space:]]*:/) { + # Non-string value (e.g. "Value": 1) + found_val++ + non_string = 1 + sub(placeholder, "\"Id\": \"" ctrl "\"", temp) + line = temp + } else { + # No Value in this object — multi-line layout + sub(placeholder, "\"Id\": \"" ctrl "\"", temp) + line = temp + in_ctrl_obj = 1 + print line + next + } + } else { + # Cannot determine object boundaries — reject + sub(placeholder, "\"Id\": \"" ctrl "\"", temp) + line = temp + non_string = 1 + } + } + in_ctrl_obj = 0 + print line + next + } + # Multi-line layout: set flag to rewrite the next Value line. + in_ctrl_obj = 1 + print line + next + } + + if (in_ctrl_obj) { + if (line ~ /"Value"[[:space:]]*:/) { + if (line ~ /"Value"[[:space:]]*:[[:space:]]*"[^"]*"/) { + orig = line + sub(/"Value"[[:space:]]*:[[:space:]]*"[^"]*"/, \ + "\"Value\": \"" newval "\"", line) + if (line != orig) changes++ + found_val++ + } else { + # Non-string value + found_val++ + non_string = 1 + } + in_ctrl_obj = 0 + print line + next + } + # Object boundary crossed without finding Value — malformed. + if (line ~ /"Id"[[:space:]]*:/ || line ~ /^[[:space:]]*\}/) { + in_ctrl_obj = 0 + } + } + } + + print line + } + END { + # Non-string Value field — string override cannot be applied. + if (non_string) exit 3 + # More than one control object with the target Id — ambiguous. + if (found_id > 1) exit 2 + # Id found but no Value found in the object — malformed config. + if (found_id > 0 && found_val == 0) exit 2 + # More than one Value found — ambiguous. + if (found_val > 1) exit 2 + # Value found but already at the desired setting — valid no-op. + if (changes == 0) exit 1 + # Multiple changes — ambiguous guard. + if (changes > 1) exit 2 + exit 0 + } + ' "$vsc_cfg" > "$vsc_tmp" + vsc_awk_rc=$? + + if [ "$vsc_awk_rc" -eq 1 ]; then + # Value found but already at the desired setting — valid no-op. + rm -f "$vsc_tmp" 2>/dev/null || true + return 0 + fi + + if [ "$vsc_awk_rc" -eq 3 ]; then + # Non-string Value field — string override cannot be applied. + log_warn "video_stage_control_override: control '${vsc_ctrl}' has a non-string Value in ${vsc_cfg}; string override not applicable" >&2 + rm -f "$vsc_tmp" 2>/dev/null || true + return 1 + fi + + if [ "$vsc_awk_rc" -ne 0 ]; then + log_warn "video_stage_control_override: malformed or ambiguous config for control '${vsc_ctrl}' in ${vsc_cfg} (awk rc=${vsc_awk_rc})" >&2 + rm -f "$vsc_tmp" 2>/dev/null || true + return 1 + fi + + if [ ! -s "$vsc_tmp" ]; then + rm -f "$vsc_tmp" 2>/dev/null || true + return 0 + fi + + if cmp -s "$vsc_cfg" "$vsc_tmp" 2>/dev/null; then + # Value was already at the desired setting; no change needed. + rm -f "$vsc_tmp" 2>/dev/null || true + return 0 + fi + + # Verify the staged file contains exactly the requested value for the + # control. This catches any edge case where the substitution succeeded + # structurally but did not produce the expected output. + if ! grep -q "\"Value\"[[:space:]]*:[[:space:]]*\"${vsc_val}\"" "$vsc_tmp" 2>/dev/null; then + log_warn "video_stage_control_override: staged file does not contain expected value '${vsc_val}' for control '${vsc_ctrl}'" >&2 + rm -f "$vsc_tmp" 2>/dev/null || true + return 1 + fi + + log_info "video_stage_control_override: ${vsc_ctrl}=${vsc_val} staged -> ${vsc_tmp} (source unchanged: ${vsc_cfg})" >&2 + printf '%s\n' "$vsc_tmp" + return 0 +}