From patchwork Fri Mar 22 22:56:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Baryshkov X-Patchwork-Id: 13600641 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 203A9CD11BF for ; Sat, 23 Mar 2024 12:53:10 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3353910F67E; Sat, 23 Mar 2024 12:53:05 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.b="kicoJb/8"; dkim-atps=neutral Received: from mail-lf1-f42.google.com (mail-lf1-f42.google.com [209.85.167.42]) by gabe.freedesktop.org (Postfix) with ESMTPS id 19D0B112894 for ; Fri, 22 Mar 2024 22:57:06 +0000 (UTC) Received: by mail-lf1-f42.google.com with SMTP id 2adb3069b0e04-513dd2d2415so2776065e87.3 for ; Fri, 22 Mar 2024 15:57:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1711148224; x=1711753024; darn=lists.freedesktop.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=JfHIWzSVFFAiWUrH5iuAF8YX0d1TVVF80MsruQqCHrc=; b=kicoJb/8guxkjQJwrYbs0+GJOkieYAIOaqbkO3FNW3sYCLOH9o9BDkBO0RPXAeT56z 7UcfVi/+/eIYDTpKDLB+yaowRMYj3yofLmq2Z2jUfeTErnQWJNyl9KUme4Y6bWYZlF75 pmnJ6mCcmI84ZRHyw/qx+ZmJcpO8bte1jJpT9p9gdaeiqX7Pm2XRHr0nkyVc1984mqYC yxQJnZZ4xjJg881tZD5mHygmOgk4BY4l5hX+sKxv9w2o9OZS8qzzA3FxvaLv6KOqOuNB wPF5G4ycGcvkSkjHbANITHBnHqehLN5wJfrvgek1lqlkPvg/lfAlPYb3YiyQagf8cB+o IuxQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711148224; x=1711753024; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=JfHIWzSVFFAiWUrH5iuAF8YX0d1TVVF80MsruQqCHrc=; b=aYcxL5ANn0SUbnoVeSvu0QnuS9WTaCxuE7bbZwIGCRHPOeJ7m5ybdmkFApRjFlBN3H CzOpIyubE+2pxTYgWQd6RapmN4V3fSIt78HEGPrwcQb3o5mNqzb2KKrYqiDINs0QvN5Z 5y6Bgml2XsoApt4VDtlFEocuwOvzTLzlsaqOAh2k/WuyOxU0LsbVhMSjKqIMLQGDPmy2 quNXKxuXx5iWjiYbB+tHKR2CIYK8KZE1AnK9eAYIT8gop67uKZxtcDjG6ufxUAp53uKl /mFfunTvKrSREpRhbUmiyWWd/3Da4KQSDoz78S6hTgpEtKURu9b6ZbGXFJsCBCfXqfUT QQrg== X-Forwarded-Encrypted: i=1; AJvYcCXEbqs/w1nndxLknLEfMs239CLSgNK7nz5X5myZFgvFY7FQYNOQ0sTeZyAolPzImabbs6VuFUsHQYNlGRxLjUijmn9fGnYYwygja+pshsnV X-Gm-Message-State: AOJu0Yy3DBmfCdahpeNvm+E552WCuyUJhcq95OllRAaCy3YSGIl0b9/y x4siMuizmADiv6qPcp5mEA95yhTmG9CUfDqOhuKT566Qgsg8CMHaGTk1Sqmv8sk= X-Google-Smtp-Source: AGHT+IF+SzS2fkdjiZwHJw913CGMrCrtFBStVpgbBYsCe4YDyUFKZk5weR0uFFZq8lysl6jF7SAuRQ== X-Received: by 2002:ac2:4898:0:b0:513:c9ca:1333 with SMTP id x24-20020ac24898000000b00513c9ca1333mr529390lfc.22.1711148223965; Fri, 22 Mar 2024 15:57:03 -0700 (PDT) Received: from umbar.lan ([192.130.178.91]) by smtp.gmail.com with ESMTPSA id e7-20020a196907000000b005158982f42csm78530lfc.16.2024.03.22.15.57.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 22 Mar 2024 15:57:03 -0700 (PDT) From: Dmitry Baryshkov Date: Sat, 23 Mar 2024 00:56:58 +0200 Subject: [PATCH v4 06/16] drm/msm: import A2xx-A4xx XML display registers database MIME-Version: 1.0 Message-Id: <20240323-fd-xml-shipped-v4-6-cca5e8457b9e@linaro.org> References: <20240323-fd-xml-shipped-v4-0-cca5e8457b9e@linaro.org> In-Reply-To: <20240323-fd-xml-shipped-v4-0-cca5e8457b9e@linaro.org> To: Masahiro Yamada , Nathan Chancellor , Nicolas Schier , Rob Clark , Abhinav Kumar , Sean Paul , Marijn Suijten , David Airlie , Daniel Vetter Cc: linux-kbuild@vger.kernel.org, linux-arm-msm@vger.kernel.org, dri-devel@lists.freedesktop.org, freedreno@lists.freedesktop.org, Dmitry Baryshkov X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=410472; i=dmitry.baryshkov@linaro.org; h=from:subject:message-id; bh=b9zFRaF4Jsj291RDi1zobsJceckeNTNeeUhM8CrSfNQ=; b=owGbwMvMwMXYbdNlx6SpcZXxtFoSQ+o/nu2tfW43r3PxfyqQnLnd/faHDTrKsREzhd+V14TFf egSyZHsZDRmYWDkYpAVU2TxKWiZGrMpOezDjqn1MINYmUCmMHBxCsBEZK3Z/0oL1She4l6hv+tn r/HTgE8lK1bm+FrzGbvqbVgsu3Py5F2pR/PbPaTLXPh4irPmz1f8U+qygNFGuiB+a36sTJPlvsL T3Y4Wn+yPryuv97y99PrstfzJW9U0xN+qhF5+PbHR3Pb3hTW5i+IdXNxrT2zSYpjxQz3xsfOjd7 XfQwLMzjDkyc02COO7c/qbSQLz1s77YhxRJYx+t7Y49THn711+1Xx5WZ/hpX9HvznMTTwmyhRYe iIx88P6lR/LbKRyq6+v6d1VlJLZ88lXfP8CxbXVCTytYfM22xkuEzrl9nnaggPsjJ4ez94eErPz +WxQUxqrGPbLf7XHok2r3/293KR14EDN8fYbdglpJb8aAA== X-Developer-Key: i=dmitry.baryshkov@linaro.org; a=openpgp; fpr=8F88381DD5C873E4AE487DA5199BF1243632046A X-Mailman-Approved-At: Sat, 23 Mar 2024 12:53:03 +0000 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Import Adreno registers database for A2xx-A4xx from the Mesa, commit 639488f924d9 ("freedreno/registers: limit the rules schema"). Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/registers/adreno/a2xx.xml | 1865 +++++++++++++++ drivers/gpu/drm/msm/registers/adreno/a3xx.xml | 1751 ++++++++++++++ drivers/gpu/drm/msm/registers/adreno/a4xx.xml | 2409 ++++++++++++++++++++ .../gpu/drm/msm/registers/adreno/adreno_common.xml | 400 ++++ .../gpu/drm/msm/registers/adreno/adreno_pm4.xml | 2268 ++++++++++++++++++ 5 files changed, 8693 insertions(+) diff --git a/drivers/gpu/drm/msm/registers/adreno/a2xx.xml b/drivers/gpu/drm/msm/registers/adreno/a2xx.xml new file mode 100644 index 000000000000..22caddaa0db9 --- /dev/null +++ b/drivers/gpu/drm/msm/registers/adreno/a2xx.xml @@ -0,0 +1,1865 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + note: only 0x3f worth of valid register values for VS_REGS and + PS_REGS, but high bit is set to indicate '0 registers used': + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Texture state dwords + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/gpu/drm/msm/registers/adreno/a3xx.xml b/drivers/gpu/drm/msm/registers/adreno/a3xx.xml new file mode 100644 index 000000000000..6717abc0a897 --- /dev/null +++ b/drivers/gpu/drm/msm/registers/adreno/a3xx.xml @@ -0,0 +1,1751 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The pair of MEM_SIZE/ADDR registers get programmed + in sequence with the size/addr of each buffer. + + + + + + + + + + + + + + + + + aka clip_halfz + + + + + + + + + + + + + + + + + + + + + + + + + + range of -8.0 to 8.0 + + + range of -512.0 to 512.0 + + + + + + + + + + + + + + + + + + + + + + + + + + RENDER_MODE is RB_RESOLVE_PASS for gmem->mem, otherwise RB_RENDER_PASS + + + + render targets - 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pitch (actually, appears to be pitch in bytes, so really is a stride) + in GMEM, so pitch of the current tile. + + + + + offset into GMEM (or system memory address in bypass mode) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + actually, appears to be pitch in bytes, so really is a stride + + + + + + + + + + + + + + + + + + + + Z_READ_ENABLE bit is set for zfunc other than GL_ALWAYS or GL_NEVER + + + + seems to be always set to 0x00000000 + + + + + DEPTH_BASE is offset in GMEM to depth/stencil buffer, ie + bin_w * bin_h / 1024 (possible rounded up to multiple of + something?? ie. 39 becomes 40, 78 becomes 80.. 75 becomes + 80.. so maybe it needs to be multiple of 8?? + + + + + + Pitch of depth buffer or combined depth+stencil buffer + in z24s8 cases. + + + + + + + + + + + + + + + + + + seems to be always set to 0x00000000 + + + Base address for stencil when not using interleaved depth/stencil + + + + pitch of stencil buffer when not using interleaved depth/stencil + + + + + + seems to be set to 0x00000002 during binning pass + + + + X/Y offset of current bin + + + + + + + + + + + + + + + seems to be where firmware writes BIN_DATA_ADDR from + CP_SET_BIN_DATA packet.. probably should be called + PC_BIN_BASE (just using name from yamato for now) + + + + probably should be PC_BIN_SIZE + + + SIZE is current pipe width * height (in tiles) + + + N is some sort of slot # between 0..(SIZE-1). In case + multiple tiles use same pipe, each tile gets unique slot # + + + + + + + STRIDE_IN_VPC: ALIGN(next_outloc - 8, 4) / 4 + (but, in cases where you'd expect 1, the blob driver uses + 2, so possibly 0 (no varying) or minimum of 2) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + indexed by dimension + + + + + + + + indexed by dimension, global_size / local_size + + + + + + + + + + TOTALATTRTOVS is # of attributes to vertex shader, in register + slots (ie. vec4+vec3 -> 7) + + + + STRMDECINSTRCNT is # of VFD_DECODE_INSTR registers valid + + STRMFETCHINSTRCNT is # of VFD_FETCH_INSTR registers valid + + + + MAXSTORAGE could be # of attributes/vbo's + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SHIFTCNT appears to be size, ie. FLOAT_32_32_32 is 12, and BYTE_8 is 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From register spec: + SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET [16:24]: Constant object + start offset in on chip RAM, + 128bit aligned + + + + + + + + + + + + + + + + + + + + + + The full/half register footprint is in units of four components, + so if r0.x is used, that counts as all of r0.[xyzw] as used. + There are separate full/half register footprint values as the + full and half registers are independent (not overlapping). + Presumably the thread scheduler hardware allocates the full/half + register names from the actual physical register file and + handles the register renaming. + + + + + + + From regspec: + SP_FS_CTRL_REG0.FS_LENGTH [31:24]: FS length, unit = 256bits. + If bit31 is 1, it means overflow + or any long shader. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + These seem to be offsets for storage of the varyings. + Always seems to start from 8, possibly loc 0 and 4 + are for gl_Position and gl_PointSize? + + + + + + + + + + SP_VS_OBJ_START_REG contains pointer to the vertex shader program, + immediately followed by the binning shader program (although I + guess that is probably just re-using the same gpu buffer) + + + + + The size of memory that ldp/stp can address, in 128 byte increments. + + + + + + + + + + + + + + + + + + The full/half register footprint is in units of four components, + so if r0.x is used, that counts as all of r0.[xyzw] as used. + There are separate full/half register footprint values as the + full and half registers are independent (not overlapping). + Presumably the thread scheduler hardware allocates the full/half + register names from the actual physical register file and + handles the register renaming. + + + + + + + + + + + + From regspec: + SP_FS_CTRL_REG0.FS_LENGTH [31:24]: FS length, unit = 256bits. + If bit31 is 1, it means overflow + or any long shader. + + + + + + + + + + + SP_FS_OBJ_START_REG contains pointer to fragment shader program + + + + + + + + + + + + + seems to be one bit per scalar, '1' for flat, '0' for smooth + + + seems to be one bit per scalar, '1' for flat, '0' for smooth + + + + render targets - 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Configures the mapping between VSC_PIPE buffer and + bin, X/Y specify the bin index in the horiz/vert + direction (0,0 is upper left, 0,1 is leftmost bin + on second row, and so on). W/H specify the number + of bins assigned to this VSC_PIPE in the horiz/vert + dimension. + + + + + + + + + + + seems to be set to 0x00000001 during binning pass + + + + seems to be always set to 0x00000001 + + + + + + + seems to be always set to 0x00000001 + + + + + + + + + + + + + + + + + + + + + + + + + + + + seems to be always set to 0x00000001 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + seems to be always set to 0x00000003 + + + seems to be always set to 0x00000001 + + + + + + + + + + + + + + + + + + + Texture sampler dwords + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Texture constant dwords + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INDX is index of texture address(es) in MIPMAP state block + + Pitch in bytes (so actually stride) + + SWAP bit is set for BGRA instead of RGBA + + + + + + + + + + + diff --git a/drivers/gpu/drm/msm/registers/adreno/a4xx.xml b/drivers/gpu/drm/msm/registers/adreno/a4xx.xml new file mode 100644 index 000000000000..69a9f9b02bc9 --- /dev/null +++ b/drivers/gpu/drm/msm/registers/adreno/a4xx.xml @@ -0,0 +1,2409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pitch (actually, appears to be pitch in bytes, so really is a stride) + in GMEM, so pitch of the current tile. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + actually, appears to be pitch in bytes, so really is a stride + + + + + + + + + + + + + + + + + + + + + + + + + + Z_READ_ENABLE bit is set for zfunc other than GL_ALWAYS or GL_NEVER + + + + + + + DEPTH_BASE is offset in GMEM to depth/stencil buffer, ie + bin_w * bin_h / 1024 (possible rounded up to multiple of + something?? ie. 39 becomes 40, 78 becomes 80.. 75 becomes + 80.. so maybe it needs to be multiple of 8?? + + + + + stride of depth/stencil buffer + + + ??? + + + + + + + + + + + + + + + + + + + + + + Base address for stencil when not using interleaved depth/stencil + + + + pitch of stencil buffer when not using interleaved depth/stencil + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The full/half register footprint is in units of four components, + so if r0.x is used, that counts as all of r0.[xyzw] as used. + There are separate full/half register footprint values as the + full and half registers are independent (not overlapping). + Presumably the thread scheduler hardware allocates the full/half + register names from the actual physical register file and + handles the register renaming. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + These seem to be offsets for storage of the varyings. + Always seems to start from 8, possibly loc 0 and 4 + are for gl_Position and gl_PointSize? + + + + + + + + + + + + From register spec: + SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET [16:24]: Constant object + start offset in on chip RAM, + 128bit aligned + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + These seem to be offsets for storage of the varyings. + Always seems to start from 8, possibly loc 0 and 4 + are for gl_Position and gl_PointSize? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + These seem to be offsets for storage of the varyings. + Always seems to start from 8, possibly loc 0 and 4 + are for gl_Position and gl_PointSize? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Configures the mapping between VSC_PIPE buffer and + bin, X/Y specify the bin index in the horiz/vert + direction (0,0 is upper left, 0,1 is leftmost bin + on second row, and so on). W/H specify the number + of bins assigned to this VSC_PIPE in the horiz/vert + dimension. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TOTALATTRTOVS is # of attributes to vertex shader, in register + slots (ie. vec4+vec3 -> 7) + + + + BYPASSATTROVS seems to count varyings that are just directly + assigned from attributes (ie, "vFoo = aFoo;") + + + STRMDECINSTRCNT is # of VFD_DECODE_INSTR registers valid + + STRMFETCHINSTRCNT is # of VFD_FETCH_INSTR registers valid + + + + MAXSTORAGE could be # of attributes/vbo's + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SHIFTCNT appears to be size, ie. FLOAT_32_32_32 is 12, and BYTE_8 is 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SIZE is current pipe width * height (in tiles) + + + N is some sort of slot # between 0..(SIZE-1). In case + multiple tiles use same pipe, each tile gets unique slot # + + + + + + + in groups of 4x vec4, blob only uses values + 0, 1, 2, 4, 6, 8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Texture sampler dwords + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Texture constant dwords + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pitch in bytes (so actually stride) + + + + + + + + + + + + + + + + + + + + + + + Pitch in bytes (so actually stride) + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/gpu/drm/msm/registers/adreno/adreno_common.xml b/drivers/gpu/drm/msm/registers/adreno/adreno_common.xml new file mode 100644 index 000000000000..218ec8bb966e --- /dev/null +++ b/drivers/gpu/drm/msm/registers/adreno/adreno_common.xml @@ -0,0 +1,400 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Registers in common between a2xx and a3xx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Address mode for a5xx+ + + + + + + + Line mode for a5xx+ + Note that Bresenham lines are only supported with MSAA disabled. + + + + + + + + Blob (v615) seem to only use SAM and I wasn't able to coerce + it to produce any other command. + Probably valid for a4xx+ but not enabled or tested on anything + but a6xx. + + + Produces garbage + + + + + + + Causes reads from an invalid address + + Results in color being zero + + + + diff --git a/drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml b/drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml new file mode 100644 index 000000000000..cab01af55d22 --- /dev/null +++ b/drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml @@ -0,0 +1,2268 @@ + + + + + + + + + + + + Flushes dirty data from UCHE, and also writes a GPU timestamp to + the address if one is provided. + + + + + + + + + + + + + + + + + + + If A6XX_RB_SAMPLE_COUNT_CONTROL.copy is true, writes OQ Z passed + sample counts to RB_SAMPLE_COUNT_ADDR. This writes to main + memory, skipping UCHE. + + + + + + Writes the GPU timestamp to the address that follows, once RB + access and flushes are complete. + + + + + + + + + + + + + + + + + + + + + Invalidates depth attachment data from the CCU. We assume this + happens in the last stage. + + + + + Invalidates color attachment data from the CCU. We assume this + happens in the last stage. + + + + + Flushes the small cache used by CP_EVENT_WRITE::BLIT (which, + along with its registers, would be better named RESOLVE). + + + + + Flushes depth attachment data from the CCU. We assume this + happens in the last stage. + + + + + Flushes color attachment data from the CCU. We assume this + happens in the last stage. + + + + + 2D blit to resolve GMEM to system memory (skipping CCU) at the + end of a render pass. Compare to CP_BLIT's BLIT_OP_SCALE for + more general blitting. + + + + + Clears based on GRAS_LRZ_CNTL configuration, could clear + fast-clear buffer or LRZ direction. + LRZ direction is stored at lrz_fc_offset + 0x200, has 1 byte which + could be expressed by enum: + CUR_DIR_DISABLED = 0x0 + CUR_DIR_GE = 0x1 + CUR_DIR_LE = 0x2 + CUR_DIR_UNSET = 0x3 + Clear of direction means setting the direction to CUR_DIR_UNSET. + + + + + + + + + + + + + + + Invalidates UCHE. + + + + + + + + Doesn't seem to do anything + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + initialize CP's micro-engine + + skip N 32-bit words to get to the next packet + + + indirect buffer dispatch. prefetch parser uses this packet + type to determine whether to pre-fetch the IB + + + + + + Takes the same arguments as CP_INDIRECT_BUFFER, but jumps to + another buffer at the same level. Must be at the end of IB, and + doesn't work with draw state IB's. + + + indirect buffer dispatch. same as IB, but init is pipelined + + + Waits for the IDLE state of the engine before further drawing. + This is pipelined, so the CP may continue. + + + wait until a register or memory location is a specific value + + wait until a register location is equal to a specific value + + wait until a register location is >= a specific value + + wait until a read completes + + wait until all base/size writes from an IB_PFD packet have completed + + + register read/modify/write + + Set binning configuration registers + + + reads register in chip and writes to memory + + write N 32-bit words to memory + + write CP_PROG_COUNTER value to memory + + conditional execution of a sequence of packets + + conditional write to memory or register + + + generate an event that creates a write to memory when completed + + + generate a VS|PS_done event + + generate a cache flush done event + + generate a z_pass done event + + + not sure the real name, but this seems to be what is used for + opencl, instead of CP_DRAW_INDX.. + + + initiate fetch of index buffer and draw + + draw using supplied indices in packet + + initiate fetch of index buffer and binIDs and draw + + initiate fetch of bin IDs and draw using supplied indices + + begin/end initiator for viz query extent processing + + fetch state sub-blocks and initiate shader code DMAs + + load constant into chip and to memory + + load sequencer instruction memory (pointer-based) + + load sequencer instruction memory (code embedded in packet) + + load constants from a location in memory + + selective invalidation of state pointers + + dynamically changes shader instruction memory partition + + sets the 64-bit BIN_MASK register in the PFP + + sets the 64-bit BIN_SELECT register in the PFP + + updates the current context, if needed + + generate interrupt from the command stream + + copy sequencer instruction memory to system memory + + + + + + + + sets draw initiator flags register in PFP, gets bitwise-ORed into + every draw initiator + + + sets the register protection mode + + + + + + load high level sequencer command + + + Conditionally load a IB based on a flag, prefetch enabled + + Conditionally load a IB based on a flag, prefetch disabled + + Load a buffer with pre-fetch enabled + + Set bin (?) + + + test 2 memory locations to dword values specified + + + Write register, ignoring context state for context sensitive registers + + + Record the real-time when this packet is processed by PFP + + + + + + PFP waits until the FIFO between the PFP and the ME is empty + + + + + Used a bit like CP_SET_CONSTANT on a2xx, but can write multiple + groups of registers. Looks like it can be used to create state + objects in GPU memory, and on state change only emit pointer + (via CP_SET_DRAW_STATE), which should be nice for reducing CPU + overhead: + + (A4x) save PM4 stream pointers to execute upon a visible draw + + + + + + + + + + Enable or disable predication globally. Also resets the + predicate to "passing" and the local bit to enabled when + enabling global predication. + + + + + Enable or disable predication locally. Unlike globally enabling + predication, this packet doesn't touch any other state. + Predication only happens when enabled globally and locally and a + predicate has been set. This should be used for internal draws + which aren't supposed to use the predication state: + + CP_DRAW_PRED_ENABLE_LOCAL(0) + ... do draw... + CP_DRAW_PRED_ENABLE_LOCAL(1) + + + + + Latch a draw predicate into the internal register. + + + + + for A4xx + Write to register with address that does not fit into type-0 pkt + + + + copy from ME scratch RAM to a register + + + Copy from REG to ME scratch RAM + + + Wait for memory writes to complete + + + Conditional execution based on register comparison + + + Memory to REG copy + + + + + + + for a5xx + + + + + + Tells CP the current mode of GPU operation + + Instruct CP to set a few internal CP registers + + + + + + + Enables IB2 skipping. If both GLOBAL and LOCAL are 1 and + nothing is left in the visibility stream, then + CP_INDIRECT_BUFFER will be skipped, and draws will early return + from their IB. + + + + + + + + + + + + + + + + + + + + General purpose 2D blit engine for image transfers and mipmap + generation. Reads through UCHE, writes through the CCU cache in + the PS stage. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Write CP_CONTEXT_SWITCH_*_INFO from CP to the following dwords, + and forcibly switch to the indicated context. + + + + + + + + + + These first appear in a650_sqe.bin. They can in theory be used + to loop any sequence of IB1 commands, but in practice they are + used to loop over bins. There is a fixed-size per-iteration + prefix, used to set per-bin state, and then the following IB1 + commands are executed until CP_END_BIN which are always the same + for each iteration and usually contain a list of + CP_INDIRECT_BUFFER calls to IB2 commands which setup state and + execute restore/draw/save commands. This replaces the previous + technique of just repeating the CP_INDIRECT_BUFFER calls and + "unrolling" the loop. + + + + + Make next dword 1 to disable preemption, 0 to re-enable it. + + + + + + + + + Can clear BV/BR counters, or wait until one catches up to another + + Clears, adds to local, or adds to global timestamp + + + + + Write to a scratch memory that is read by CP_REG_TEST with + SOURCE_SCRATCH_MEM set. It's not the same scratch as scratch registers. + However it uses the same memory space. + + + + + Executes an array of fixed-size command buffers where each + buffer is assumed to have one draw call, skipping buffers with + non-visible draw calls. + + + + Reset various on-chip state used for synchronization + + + + + + Load state, a3xx (and later?) + + + + + + + + + + + + + + + + + inline with the CP_LOAD_STATE packet + + + + + in buffer pointed to by EXT_SRC_ADDR + + + + + + + + + + + + + + + + + + Load state, a4xx+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Load state, a6xx+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + SS6_UBO used by the a6xx vulkan blob with tesselation constants + in this case, EXT_SRC_ADDR is (ubo_id shl 16 | offset) + to load constants from a UBO loaded with DST_OFF = 14 and offset 0, + EXT_SRC_ADDR = 0xe0000 + (offset is a guess, should be in bytes given that maxUniformBufferRange=64k) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DST_OFF same as in CP_LOAD_STATE6 - vec4 VS const at this offset will + be updated for each draw to {draw_id, first_vertex, first_instance, 0} + value of 0 disables it + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Read a 64-bit value at the given address and + test if it equals/doesn't equal 0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + value at offset 0 always seems to be 0x00000000.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Like CP_SET_BIN_DATA5, but set the pointers as offsets from the + pointers stored in VSC_PIPE_{DATA,DATA2,SIZE}_ADDRESS. Useful + for Vulkan where these values aren't known when the command + stream is recorded. + + + + + + + + + + + + + + + + + + + + + + + + Modifies DST_REG using two sources that can either be registers + or immediates. If SRC1_ADD is set, then do the following: + + $dst = (($dst & $src0) rot $rotate) + $src1 + + Otherwise: + + $dst = (($dst & $src0) rot $rotate) | $src1 + + Here "rot" means rotate left. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Like CP_REG_TO_MEM, but the memory address to write to can be + offsetted using either one or two registers or scratch + registers. + + + + + + + + + + + + + + + + + + + + + + + + Like CP_REG_TO_MEM, but the memory address to write to can be + offsetted using a DWORD in memory. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Wait until a memory value is greater than or equal to the + reference, using signed comparison. + + + + + + + + + + + + + + + + + + + This uses the same internal comparison as CP_COND_WRITE, + but waits until the comparison is true instead. It busy-loops in + the CP for the given number of cycles before trying again. + + + + + + + + + + + + + + + + + + + + + + + + + + + Waits for REG0 to not be 0 or REG1 to not equal REF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tell CP the current operation mode, indicates save and restore procedure + + + + + + + + + + + + + + + + + + + + + + + + + + Set internal CP registers, used to indicate context save data addresses + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tests bit in specified register and sets predicate for CP_COND_REG_EXEC. + So: + + opcode: CP_REG_TEST (39) (2 dwords) + { REG = 0xc10 | BIT = 0 } + 0000: 70b90001 00000c10 + opcode: CP_COND_REG_EXEC (47) (3 dwords) + 0000: 70c70002 10000000 00000004 + opcode: CP_INDIRECT_BUFFER (3f) (4 dwords) + + Will execute the CP_INDIRECT_BUFFER only if b0 in the register at + offset 0x0c10 is 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Executes the following DWORDs of commands if the dword at ADDR0 + is not equal to 0 and the dword at ADDR1 is less than REF + (signed comparison). + + + + + + + + + + + + + + + + + + + + + + + + Used by the userspace driver to set various IB's which are + executed during context save/restore for handling + state that isn't restored by the + context switch routine itself. + + + + Executed unconditionally when switching back to the context. + + + + Executed when switching back after switching + away during execution of + a CP_SET_MARKER packet with RM6_YIELD as the + payload *and* the normal save routine was + bypassed for a shorter one. I think this is + connected to the "skipsaverestore" bit set by + the kernel when preempting. + + + + + Executed when switching away from the context, + except for context switches initiated via + CP_YIELD. + + + + + This can only be set by the RB (i.e. the kernel) + and executes with protected mode off, but + is otherwise similar to SAVE_IB. + + Note, kgsl calls this CP_KMD_AMBLE_TYPE + + + + + + + + + + + + + + + + + + + Keep shadow copies of these registers and only set them + when drawing, avoiding redundant writes: + - VPC_CNTL_0 + - HLSQ_CONTROL_1_REG + - HLSQ_UNKNOWN_B980 + + + + Track RB_RENDER_CNTL, and insert a WFI in the following + situation: + - There is a write that disables binning + - There was a draw with binning left enabled, but in + BYPASS mode + Presumably this is a hang workaround? + + + + Do a mysterious CP_EVENT_WRITE 0x3f when the low bit of + the data to write is 0. Used by the Vulkan blob with + PC_MULTIVIEW_CNTL, but this isn't predicated on particular + register(s) like the others. + + + + Tracks GRAS_LRZ_CNTL::GREATER, GRAS_LRZ_CNTL::DIR, and + GRAS_LRZ_DEPTH_VIEW with previous values, and if one of + the following is true: + - GRAS_LRZ_CNTL::GREATER has changed + - GRAS_LRZ_CNTL::DIR has changed, the old value is not + CUR_DIR_GE, and the new value is not CUR_DIR_DISABLED + - GRAS_LRZ_DEPTH_VIEW has changed + then it does a LRZ_FLUSH with GRAS_LRZ_CNTL::ENABLE + forced to 1. + Only exists in a650_sqe.fw. + + + + + + + + + + + + + Note that the SMMU's definition of TTBRn can take different forms + depending on the pgtable format. But a5xx+ only uses aarch64 + format. + + + + + + + + + + Unused, does not apply to aarch64 pgtable format + + + + + + + + + + + + + Size of prefix for each bin. For each bin index i, the + prefix commands at PREFIX_ADDR + i * PREFIX_DWORDS are + executed in an IB2 before the IB1 commands following + this packet. + + + + Number of dwords after this packet until CP_END_BIN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Best guess is that it is a faster way to fetch all the VSC_STATE registers + and keep them in a local scratch memory instead of fetching every time + when skipping IBs. + + + + + + Scratch memory size is 48 dwords` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +