Show a patch.

GET /api/patches/15148/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 15148,
    "url": "https://patchwork.libcamera.org/api/patches/15148/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/15148/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20211213122847.26260-2-hpa@redhat.com>",
    "date": "2021-12-13T12:28:47",
    "name": "[libcamera-devel,RFC,v4,1/1] ipa: ipu3: af: Auto focus for dw9719 Surface Go2 VCM",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "9d2a66a31ce349c89ed3c9a7a5f529ac7dd7c394",
    "submitter": {
        "id": 105,
        "url": "https://patchwork.libcamera.org/api/people/105/?format=api",
        "name": "Kate Hsuan",
        "email": "hpa@redhat.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/15148/mbox/",
    "series": [
        {
            "id": 2839,
            "url": "https://patchwork.libcamera.org/api/series/2839/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=2839",
            "date": "2021-12-13T12:28:46",
            "name": "ipa: ipu3: af: Auto focus for dw9719 Surface Go2 VCM",
            "version": 4,
            "mbox": "https://patchwork.libcamera.org/series/2839/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/15148/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/15148/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id E46A2BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 13 Dec 2021 12:29:20 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 994F36087E;\n\tMon, 13 Dec 2021 13:29:20 +0100 (CET)",
            "from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.133.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A7CB060868\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 13 Dec 2021 13:29:18 +0100 (CET)",
            "from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com\n\t[209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n\tus-mta-486-vqKPyZCmPOSYRAa9zy6fsw-1; Mon, 13 Dec 2021 07:29:16 -0500",
            "from smtp.corp.redhat.com\n\t(int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16])\n\t(using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits))\n\t(No client certificate requested)\n\tby mimecast-mx01.redhat.com (Postfix) with ESMTPS id A8807874982\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 13 Dec 2021 12:29:15 +0000 (UTC)",
            "from fedora.redhat.com (unknown [10.39.193.56])\n\tby smtp.corp.redhat.com (Postfix) with ESMTP id 1B1207AB46;\n\tMon, 13 Dec 2021 12:29:12 +0000 (UTC)"
        ],
        "Authentication-Results": [
            "lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"g65daocc\"; dkim-atps=neutral",
            "relay.mimecast.com;\n\tauth=pass smtp.auth=CUSA124A263 smtp.mailfrom=hpa@redhat.com"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1639398557;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tcontent-transfer-encoding:content-transfer-encoding:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=/QAzdHmNCUDs7yasrdyZE59ZkF3FK8w956HxHvsI5gs=;\n\tb=g65daocc6BBbCHB8Mx0NAu3WalpB+BZ7PL/rsSwRgzzjIqfsHFCeeuJ8tB+1AQAModbWmJ\n\tX0Tw9R/optJhb2IlyTBe6jFvWxgib4E9yEA1VS2fgxS7OBEL2i6YWedqZ2mtJ/wYH292E3\n\trqUbHtenignEHniiC8MtZQvfU9ej5II=",
        "X-MC-Unique": "vqKPyZCmPOSYRAa9zy6fsw-1",
        "From": "Kate Hsuan <hpa@redhat.com>",
        "To": "libcamera devel <libcamera-devel@lists.libcamera.org>",
        "Date": "Mon, 13 Dec 2021 20:28:47 +0800",
        "Message-Id": "<20211213122847.26260-2-hpa@redhat.com>",
        "In-Reply-To": "<20211213122847.26260-1-hpa@redhat.com>",
        "References": "<20211213122847.26260-1-hpa@redhat.com>",
        "MIME-Version": "1.0",
        "X-Scanned-By": "MIMEDefang 2.79 on 10.5.11.16",
        "X-Mimecast-Spam-Score": "0",
        "X-Mimecast-Originator": "redhat.com",
        "Content-Transfer-Encoding": "8bit",
        "Content-Type": "text/plain; charset=\"US-ASCII\"",
        "Subject": "[libcamera-devel] [RFC v4 1/1] ipa: ipu3: af: Auto focus for dw9719\n\tSurface Go2 VCM",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "Since VCM for surface Go 2 (dw9719) had been successfully\ndriven, this Af module can be used to control the VCM and\ndetermine the focus value based on the IPU3 AF state.\n\nThe variance of each focus step is determined and a greedy\napproah is used to find the maximum variance of the AF\nstate and a appropriate focus value.\n\nChanges in v2:\n1. Add grid configuration interface.\n2. Add AF accelerator configuration.\n3. Move default focus area to the center of the image.\n\nChanges in v3:\n1. Squash Daniel's commit- Remove v4l2 interaction from AF\n   algorithm.\n2. Fix greedy AF algorithm since V4l2 interface had been\n   removed.\n3. Simplify default grid config for AF.\n4. AF start area is determined by bdsOutputSize.\n\nChanges in v4:\nIn v4, it significant improves the AF scan time using a two pass\nscaning method and the AF scan will be terminated when it finds\na negative gradient.\n1. Introduce 2 pass AF scan (coarse and fine scan) to increase\n   the AF success rate.\n2. The low pass filter convolution results are used to determine\n   a coarse AF result.\n3. The high pass convolution result is used to find a good lens\n   step in a given AF range determined in pass 1.\n\nSigned-off-by: Kate Hsuan <hpa@redhat.com>\n---\n src/ipa/ipu3/algorithms/af.cpp      | 334 ++++++++++++++++++++++++++++\n src/ipa/ipu3/algorithms/af.h        |  66 ++++++\n src/ipa/ipu3/algorithms/meson.build |   3 +-\n src/ipa/ipu3/ipa_context.cpp        |  27 +++\n src/ipa/ipu3/ipa_context.h          |  11 +\n src/ipa/ipu3/ipu3.cpp               |   2 +\n 6 files changed, 442 insertions(+), 1 deletion(-)\n create mode 100644 src/ipa/ipu3/algorithms/af.cpp\n create mode 100644 src/ipa/ipu3/algorithms/af.h",
    "diff": "diff --git a/src/ipa/ipu3/algorithms/af.cpp b/src/ipa/ipu3/algorithms/af.cpp\nnew file mode 100644\nindex 00000000..620be9fa\n--- /dev/null\n+++ b/src/ipa/ipu3/algorithms/af.cpp\n@@ -0,0 +1,334 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2021, Red Hat\n+ *\n+ * af.cpp - IPU3 auto focus control\n+ */\n+\n+#include \"af.h\"\n+\n+#include <algorithm>\n+#include <chrono>\n+#include <cmath>\n+#include <fcntl.h>\n+#include <numeric>\n+#include <sys/ioctl.h>\n+#include <sys/stat.h>\n+#include <sys/types.h>\n+#include <unistd.h>\n+\n+#include <linux/videodev2.h>\n+\n+#include <libcamera/base/log.h>\n+\n+#include <libcamera/ipa/core_ipa_interface.h>\n+\n+#include \"libipa/histogram.h\"\n+\n+/**\n+ * \\file af.h\n+ */\n+\n+namespace libcamera {\n+\n+using namespace std::literals::chrono_literals;\n+\n+namespace ipa::ipu3::algorithms {\n+\n+/**\n+ * \\class Af\n+ * \\brief A IPU3 auto-focus accelerator based auto focus algorthim\n+ *\n+ * This algorithm is used to determine the position of the lens and get a\n+ * focused image. The IPU3 AF accelerator computes the statistics, composed\n+ * by high pass and low pass filtered value and stores in a AF buffer.\n+ * Typically, for a focused image, it has relative high contrast than a\n+ * blurred image, i.e. an out of focus image. Therefore, if an image with the\n+ * highest contrast can be found from the AF scan, the lens' position is the\n+ * best step of the focus.\n+ *\n+ */\n+\n+LOG_DEFINE_CATEGORY(IPU3Af)\n+\n+/**\n+ * Maximum focus value of the VCM control\n+ * \\todo should be obtained from the VCM driver\n+ */\n+static constexpr uint32_t MaxFocusSteps_ = 1023;\n+\n+/* minimum focus step for searching appropriate focus*/\n+static constexpr uint32_t coarseSearchStep_ = 10;\n+static constexpr uint32_t fineSearchStep_ = 1;\n+\n+/* max ratio of variance change, 0.0 < MaxChange_ < 1.0*/\n+static constexpr double MaxChange_ = 0.8;\n+\n+/* settings for Auto Focus from the kernel */\n+static struct ipu3_uapi_af_config_s imgu_css_af_defaults = {\n+\t.filter_config = {\n+\t\t{ 0, 0, 0, 0 },\n+\t\t{ 0, 0, 0, 0 },\n+\t\t{ 0, 0, 0, 128 },\n+\t\t0,\n+\t\t{ 0, 0, 0, 0 },\n+\t\t{ 0, 0, 0, 0 },\n+\t\t{ 0, 0, 0, 128 },\n+\t\t0,\n+\t\t.y_calc = { 8, 8, 8, 8 },\n+\t\t.nf = { 0, 7, 0, 7, 0 },\n+\t},\n+\t.grid_cfg = {\n+\t\t.width = 16,\n+\t\t.height = 16,\n+\t\t.block_width_log2 = 3,\n+\t\t.block_height_log2 = 3,\n+\t\t.x_start = 10,\n+\t\t.y_start = 2 | IPU3_UAPI_GRID_Y_START_EN,\n+\t},\n+};\n+\n+Af::Af()\n+\t: focus_(0), goodFocus_(0), currentVariance_(0.0), previousVariance_(0.0),\n+\t  pass1Done_(false), pass2Done_(false)\n+{\n+\tmaxStep_ = MaxFocusSteps_;\n+}\n+\n+Af::~Af()\n+{\n+}\n+\n+void Af::prepare(IPAContext &context, ipu3_uapi_params *params)\n+{\n+\tparams->use.acc_af = 1;\n+\tparams->acc_param.af = imgu_css_af_defaults;\n+\tparams->acc_param.af.grid_cfg.x_start = context.configuration.af.start_x;\n+\tparams->acc_param.af.grid_cfg.y_start = context.configuration.af.start_y | IPU3_UAPI_GRID_Y_START_EN;\n+}\n+\n+/**\n+ * \\brief Configure the Af given a configInfo\n+ * \\param[in] context The shared IPA context\n+ * \\param[in] configInfo The IPA configuration data\n+ *\n+ * \\return 0\n+ */\n+int Af::configure(IPAContext &context, const IPAConfigInfo &configInfo)\n+{\n+\t/* determined focus value i.e. current focus value */\n+\tcontext.frameContext.af.focus = 0;\n+\t/* maximum variance of the AF statistics */\n+\tcontext.frameContext.af.maxVariance = 0;\n+\t/* is focused? if it is true, the AF should be in a stable state. */\n+\tcontext.frameContext.af.stable = false;\n+\t/* frame to be ignored before start to estimate AF variance. */\n+\tignoreFrame_ = 10;\n+\n+\t/*\n+\t * AF default area configuration\n+\t * Move AF area to the center of the image.\n+\t */\n+\t/* Default AF width is 16x8 = 128 */\n+\tcontext.configuration.af.start_x = (configInfo.bdsOutputSize.width / 2) - 64;\n+\tcontext.configuration.af.start_y = (configInfo.bdsOutputSize.height / 2) - 64;\n+\n+\tLOG(IPU3Af, Debug) << \"BDS X: \"\n+\t\t\t   << configInfo.bdsOutputSize.width\n+\t\t\t   << \" Y: \"\n+\t\t\t   << configInfo.bdsOutputSize.height;\n+\tLOG(IPU3Af, Debug) << \"AF start from X: \"\n+\t\t\t   << context.configuration.af.start_x\n+\t\t\t   << \" Y: \"\n+\t\t\t   << context.configuration.af.start_y;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\brief AF coarse scan\n+ * \\param[in] context The shared IPA context\n+ *\n+ */\n+void Af::af_coarse_scan(IPAContext &context)\n+{\n+\tif (pass1Done_ == true)\n+\t\treturn;\n+\n+\tif (af_scan(context, coarseSearchStep_)) {\n+\t\tpass1Done_ = true;\n+\t\tcontext.frameContext.af.maxVariance = 0;\n+\t\tfocus_ = context.frameContext.af.focus - (context.frameContext.af.focus * 0.1);\n+\t\tcontext.frameContext.af.focus = focus_;\n+\t\tpreviousVariance_ = 0;\n+\t\tmaxStep_ = focus_ + (focus_ * 0.2);\n+\t}\n+}\n+\n+/**\n+ * \\brief AF fine scan\n+ * \\param[in] context The shared IPA context\n+ *\n+ */\n+void Af::af_fine_scan(IPAContext &context)\n+{\n+\tif (pass1Done_ != true)\n+\t\treturn;\n+\n+\tif (af_scan(context, fineSearchStep_)) {\n+\t\tcontext.frameContext.af.stable = true;\n+\t\tpass2Done_ = true;\n+\t}\n+}\n+\n+/**\n+ * \\brief AF reset\n+ * \\param[in] context The shared IPA context\n+ *\n+ */\n+void Af::af_reset(IPAContext &context)\n+{\n+\tcontext.frameContext.af.maxVariance = 0;\n+\tcontext.frameContext.af.focus = 0;\n+\tfocus_ = 0;\n+\tcontext.frameContext.af.stable = false;\n+\tignoreFrame_ = 60;\n+\tpreviousVariance_ = 0.0;\n+\tpass1Done_ = false;\n+\tpass2Done_ = false;\n+\tmaxStep_ = MaxFocusSteps_;\n+}\n+\n+/**\n+ * \\brief AF  scan\n+ * \\param[in] context The shared IPA context\n+ *\n+ * \\return True, if it finds a AF value.\n+ */\n+bool Af::af_scan(IPAContext &context, int min_step)\n+{\n+\t/* find the maximum variance during the AF scan using a greedy strategy */\n+\tif (currentVariance_ > context.frameContext.af.maxVariance) {\n+\t\tcontext.frameContext.af.maxVariance = currentVariance_;\n+\t\tgoodFocus_ = focus_;\n+\t}\n+\n+\tif (focus_ > maxStep_) {\n+\t\t/* if reach the max step, move lens to the position and set \"focus stable\". */\n+\t\tcontext.frameContext.af.focus = goodFocus_;\n+\t\treturn true;\n+\t} else {\n+\t\t/* check negative gradient */\n+\t\tif ((currentVariance_ - context.frameContext.af.maxVariance) > -(context.frameContext.af.maxVariance * 0.15)) {\n+\t\t\tfocus_ += min_step;\n+\t\t\tcontext.frameContext.af.focus = focus_;\n+\t\t} else {\n+\t\t\tcontext.frameContext.af.focus = goodFocus_;\n+\t\t\tpreviousVariance_ = currentVariance_;\n+\t\t\treturn true;\n+\t\t}\n+\t}\n+\tLOG(IPU3Af, Debug) << \"Variance prevrious: \"\n+\t\t\t   << previousVariance_\n+\t\t\t   << \" current: \"\n+\t\t\t   << currentVariance_\n+\t\t\t   << \" Diff: \"\n+\t\t\t   << (currentVariance_ - context.frameContext.af.maxVariance);\n+\tpreviousVariance_ = currentVariance_;\n+\tLOG(IPU3Af, Debug) << \"Focus searching max variance is: \"\n+\t\t\t   << context.frameContext.af.maxVariance\n+\t\t\t   << \" Focus step is \"\n+\t\t\t   << goodFocus_\n+\t\t\t   << \" Current scan is \"\n+\t\t\t   << focus_;\n+\treturn false;\n+}\n+\n+/**\n+ * \\brief Determine the max contrast image and lens position. y_table is the\n+ * statictic data from IPU3 and is composed of low pass and high pass filtered\n+ * value. High pass filtered value also represents the sharpness of the image.\n+ * Based on this, if the image with highest variance of the high pass filtered\n+ * value (contrast) during the AF scan, the position of the len should be the\n+ * best focus.\n+ * \\param[in] context The shared IPA context.\n+ * \\param[in] stats The statistic buffer of 3A from the IPU3.\n+ */\n+void Af::process(IPAContext &context, const ipu3_uapi_stats_3a *stats)\n+{\n+\tuint32_t total = 0;\n+\tdouble mean;\n+\tuint64_t var_sum = 0;\n+\ty_table_item_t *y_item;\n+\tint z = 0;\n+\n+\ty_item = (y_table_item_t *)stats->af_raw_buffer.y_table;\n+\n+\t/**\n+\t * Calculate the mean and the varience of each non-zero AF statistics, since IPU3 only determine the AF value\n+\t * for a given grid.\n+\t * For pass1: low pass results are used.\n+\t * For pass2: high pass results are used.\n+\t */\n+\tif (pass1Done_) {\n+\t\tfor (z = 0; z < (IPU3_UAPI_AF_Y_TABLE_MAX_SIZE) / 4; z++) {\n+\t\t\ttotal = total + y_item[z].y2_avg;\n+\t\t\tif (y_item[z].y2_avg == 0)\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tmean = total / z;\n+\n+\t\tfor (z = 0; z < (IPU3_UAPI_AF_Y_TABLE_MAX_SIZE) / 4 && y_item[z].y2_avg != 0; z++) {\n+\t\t\tvar_sum = var_sum + ((y_item[z].y2_avg - mean) * (y_item[z].y2_avg - mean));\n+\t\t\tif (y_item[z].y2_avg == 0)\n+\t\t\t\tbreak;\n+\t\t}\n+\t} else {\n+\t\tfor (z = 0; z < (IPU3_UAPI_AF_Y_TABLE_MAX_SIZE) / 4; z++) {\n+\t\t\ttotal = total + y_item[z].y1_avg;\n+\t\t\tif (y_item[z].y1_avg == 0)\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tmean = total / z;\n+\n+\t\tfor (z = 0; z < (IPU3_UAPI_AF_Y_TABLE_MAX_SIZE) / 4 && y_item[z].y1_avg != 0; z++) {\n+\t\t\tvar_sum = var_sum + ((y_item[z].y1_avg - mean) * (y_item[z].y1_avg - mean));\n+\t\t\tif (y_item[z].y1_avg == 0)\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\t/* Determine the average variance of the frame. */\n+\tcurrentVariance_ = static_cast<double>(var_sum) / static_cast<double>(z);\n+\tLOG(IPU3Af, Debug) << \"variance: \" << currentVariance_;\n+\n+\tif (context.frameContext.af.stable == true) {\n+\t\tconst uint32_t diff_var = std::abs(currentVariance_ - context.frameContext.af.maxVariance);\n+\t\tconst double var_ratio = diff_var / context.frameContext.af.maxVariance;\n+\t\tLOG(IPU3Af, Debug) << \"Change ratio: \"\n+\t\t\t\t   << var_ratio\n+\t\t\t\t   << \" current focus: \"\n+\t\t\t\t   << context.frameContext.af.focus;\n+\t\t/**\n+\t\t * If the change ratio of contrast is over Maxchange_ (out of focus),\n+\t\t * trigger AF again.\n+\t\t */\n+\t\tif (var_ratio > MaxChange_) {\n+\t\t\tif (ignoreFrame_ == 0) {\n+\t\t\t\taf_reset(context);\n+\t\t\t} else\n+\t\t\t\tignoreFrame_--;\n+\t\t} else\n+\t\t\tignoreFrame_ = 10;\n+\t} else {\n+\t\tif (ignoreFrame_ != 0)\n+\t\t\tignoreFrame_--;\n+\t\telse {\n+\t\t\taf_coarse_scan(context);\n+\t\t\taf_fine_scan(context);\n+\t\t}\n+\t}\n+}\n+\n+} /* namespace ipa::ipu3::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/ipu3/algorithms/af.h b/src/ipa/ipu3/algorithms/af.h\nnew file mode 100644\nindex 00000000..b9295b19\n--- /dev/null\n+++ b/src/ipa/ipu3/algorithms/af.h\n@@ -0,0 +1,66 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2021, Red Hat\n+ *\n+ * af.h - IPU3 Af control\n+ */\n+#ifndef __LIBCAMERA_IPU3_ALGORITHMS_AF_H__\n+#define __LIBCAMERA_IPU3_ALGORITHMS_AF_H__\n+\n+#include <linux/intel-ipu3.h>\n+\n+#include <libcamera/base/utils.h>\n+\n+#include <libcamera/geometry.h>\n+\n+#include \"algorithm.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::ipu3::algorithms {\n+\n+class Af : public Algorithm\n+{\n+\t/* The format of y_table. From ipu3-ipa repo */\n+\ttypedef struct y_table_item {\n+\t\tuint16_t y1_avg;\n+\t\tuint16_t y2_avg;\n+\t} y_table_item_t;\n+\n+public:\n+\tAf();\n+\t~Af();\n+\n+\tvoid prepare(IPAContext &context, ipu3_uapi_params *params) override;\n+\tint configure(IPAContext &context, const IPAConfigInfo &configInfo) override;\n+\tvoid process(IPAContext &context, const ipu3_uapi_stats_3a *stats) override;\n+\n+private:\n+\tvoid af_coarse_scan(IPAContext &context);\n+\tvoid af_fine_scan(IPAContext &context);\n+\tbool af_scan(IPAContext &context, int min_step);\n+\tvoid af_reset(IPAContext &context);\n+\n+\t/* Used for focus scan. */\n+\tuint32_t focus_;\n+\t/* Focus good */\n+\tuint32_t goodFocus_;\n+\t/* Recent AF statistic variance. */\n+\tdouble currentVariance_;\n+\t/* The frames to be ignore before starting measuring. */\n+\tuint32_t ignoreFrame_;\n+\t/* previous variance. it is used to determine the gradient */\n+\tdouble previousVariance_;\n+\t/* Max scan steps of each pass of AF scaning */\n+\tuint32_t maxStep_;\n+\t/* Pass 1 stable. Complete low pass search (coarse) scan) */\n+\tbool pass1Done_;\n+\t/* Pass 2 stable. Complete high pass scan (fine scan) */\n+\tbool pass2Done_;\n+};\n+\n+} /* namespace ipa::ipu3::algorithms */\n+\n+} /* namespace libcamera */\n+\n+#endif /* __LIBCAMERA_IPU3_ALGORITHMS_AF_H__ */\ndiff --git a/src/ipa/ipu3/algorithms/meson.build b/src/ipa/ipu3/algorithms/meson.build\nindex 4db6ae1d..e1099169 100644\n--- a/src/ipa/ipu3/algorithms/meson.build\n+++ b/src/ipa/ipu3/algorithms/meson.build\n@@ -1,8 +1,9 @@\n # SPDX-License-Identifier: CC0-1.0\n \n ipu3_ipa_algorithms = files([\n+    'af.cpp',\n     'agc.cpp',\n     'awb.cpp',\n     'blc.cpp',\n-    'tone_mapping.cpp',\n+    'tone_mapping.cpp'\n ])\ndiff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp\nindex 86794ac1..ee644d3c 100644\n--- a/src/ipa/ipu3/ipa_context.cpp\n+++ b/src/ipa/ipu3/ipa_context.cpp\n@@ -67,6 +67,33 @@ namespace libcamera::ipa::ipu3 {\n  *\n  * \\var IPASessionConfiguration::grid.stride\n  * \\brief Number of cells on one line including the ImgU padding\n+ *\n+ */\n+\n+/**\n+ * \\var IPASessionConfiguration::af\n+ * \\brief AF parameters configuration of the IPA\n+ *\n+ * \\var IPASessionConfiguration::af.start_x\n+ * \\brief The start X position of the AF area\n+ *\n+ * \\var IPASessionConfiguration::af.start_y\n+ * \\brief The start Y position of the AF area\n+ */\n+\n+/**\n+ * \\var IPAFrameContext::af\n+ * \\brief Context for the Automatic Focus algorithm\n+ *\n+ * \\struct  IPAFrameContext::af\n+ * \\var IPAFrameContext::af.focus\n+ * \\brief Current position of the lens\n+ *\n+ * \\var IPAFrameContext::af.maxVariance\n+ * \\brief The maximum variance of the current image.\n+ *\n+ * \\var IPAFrameContext::af.stable\n+ * \\brief is the image focused?\n  */\n \n /**\ndiff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h\nindex c6dc0814..aa5bf97f 100644\n--- a/src/ipa/ipu3/ipa_context.h\n+++ b/src/ipa/ipu3/ipa_context.h\n@@ -31,6 +31,11 @@ struct IPASessionConfiguration {\n \t\tdouble minAnalogueGain;\n \t\tdouble maxAnalogueGain;\n \t} agc;\n+\n+\tstruct {\n+\t\tuint16_t start_x;\n+\t\tuint16_t start_y;\n+\t} af;\n };\n \n struct IPAFrameContext {\n@@ -49,6 +54,12 @@ struct IPAFrameContext {\n \t\tdouble temperatureK;\n \t} awb;\n \n+\tstruct {\n+\t\tuint32_t focus;\n+\t\tdouble maxVariance;\n+\t\tbool stable;\n+\t} af;\n+\n \tstruct {\n \t\tuint32_t exposure;\n \t\tdouble gain;\ndiff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp\nindex 3d307708..93966c6f 100644\n--- a/src/ipa/ipu3/ipu3.cpp\n+++ b/src/ipa/ipu3/ipu3.cpp\n@@ -30,6 +30,7 @@\n \n #include \"libcamera/internal/mapped_framebuffer.h\"\n \n+#include \"algorithms/af.h\"\n #include \"algorithms/agc.h\"\n #include \"algorithms/algorithm.h\"\n #include \"algorithms/awb.h\"\n@@ -294,6 +295,7 @@ int IPAIPU3::init(const IPASettings &settings,\n \t}\n \n \t/* Construct our Algorithms */\n+\talgorithms_.push_back(std::make_unique<algorithms::Af>());\n \talgorithms_.push_back(std::make_unique<algorithms::Agc>());\n \talgorithms_.push_back(std::make_unique<algorithms::Awb>());\n \talgorithms_.push_back(std::make_unique<algorithms::BlackLevelCorrection>());\n",
    "prefixes": [
        "libcamera-devel",
        "RFC",
        "v4",
        "1/1"
    ]
}