diff --git a/docs/docs/frigate/installation.md b/docs/docs/frigate/installation.md index 17d5abc3e5..abe3d6b9d5 100644 --- a/docs/docs/frigate/installation.md +++ b/docs/docs/frigate/installation.md @@ -574,16 +574,26 @@ The operator build is architecture-specific. Build and validate the image on the ::: -Then pass the Metis device into the container. Add the following lines to your `docker-compose.yml`: +Then pass the Metis device and the GPU/dma-buf nodes into the container. Add the following lines to your `docker-compose.yml`: ```yaml devices: - /dev/metis-0-1-0 + - /dev/dma_heap/system:/dev/dma_heap/system + - /dev/dri/renderD128:/dev/dri/renderD128 # Intel/AMD GPU render node (OpenCL) ``` +The SDK needs the dma-buf exporter for its shared-memory pool (`No dmabuf driver found` at startup means it is missing). The GPU render node is required for the SDK to detect OpenCL and use the GPU hardware path (`hardware_caps.opencl`); without it the SDK falls back to software decoding. Which nodes exist depends on the host (Intel iGPU render nodes are numbered, e.g. `renderD128`; AMD uses `renderD129`). + Metis device node names depend on the driver version (for example `/dev/metis-0-1-0` or `/dev/metis-0:1:0`); list the nodes under `/dev/metis*` after installing the driver and pass the one that appears for your card. -If you are using `docker run`, add this option to your command: `--device /dev/metis-0-1-0` +If you are using `docker run`, add these options to your command: + +```bash +--device /dev/metis-0-1-0 \ +--device /dev/dma_heap/system:/dev/dma_heap/system \ +--device /dev/dri/renderD128:/dev/dri/renderD128 +``` #### Configuration diff --git a/frigate/stats/util.py b/frigate/stats/util.py index 6e20197391..278d800603 100644 --- a/frigate/stats/util.py +++ b/frigate/stats/util.py @@ -21,6 +21,7 @@ from frigate.util.services import ( calculate_shm_requirements, get_amd_gpu_stats, get_axcl_npu_stats, + get_axelera_board_temp, get_bandwidth_stats, get_cpu_stats, get_fs_type, @@ -132,6 +133,10 @@ def get_detector_temperature( # Rockchip temperatures are handled by the GPU / NPU stats # as there are not detector specific temperatures pass + elif detector_type == "axelera": + # Axelera Metis temperature is read from the board controller + # through the SDK's axcmd tool + return get_axelera_board_temp() return None diff --git a/frigate/util/services.py b/frigate/util/services.py index 38737907ef..8542eb6f91 100644 --- a/frigate/util/services.py +++ b/frigate/util/services.py @@ -800,6 +800,50 @@ def get_axcl_npu_stats() -> dict[str, str | float] | None: return None +def get_axelera_board_temp() -> float | None: + """Get the Axelera Metis board temperature through the SDK's axcmd tool. + + The read goes through the board controller and does not open an inference + stream, so it is safe to call while a detector is running. + """ + axcmd_path = None + + for candidate in ( + shutil.which("axcmd"), + "/usr/local/bin/axcmd", + "/opt/axelera/runtime-1.8.0-1/bin/axcmd", + ): + if candidate and os.path.exists(candidate): + axcmd_path = candidate + break + + if axcmd_path is None: + return None + + try: + result = sp.run( + [axcmd_path, "--board-temp"], + capture_output=True, + text=True, + timeout=5, + ) + except (OSError, sp.TimeoutExpired): + return None + + if result.returncode != 0: + return None + + match = re.search(r"=\s*([\d.]+)", result.stdout) + + if match is None: + return None + + try: + return round(float(match.group(1)), 1) + except ValueError: + return None + + def try_get_info(f, h, default="N/A", sensor=None): try: if h: