whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

commit 996680d461f8f759082e64f2395c1f7c25d9d549
parent 5813540b584c3b1a507d1c61896bec164cad0905
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Tue, 25 Dec 2018 13:11:30 -0800

Merge tag 'media/v4.20-7' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media

Pull more media updates from Mauro Carvalho Chehab:
 "The Intel IPU3 camera driver"

* tag 'media/v4.20-7' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (23 commits)
  media: staging/ipu3-imgu: Add MAINTAINERS entry
  media: staging/ipu3-imgu: Address documentation comments
  media: v4l: Add Intel IPU3 meta buffer formats
  media: doc-rst: Add Intel IPU3 documentation
  media: ipu3-imgu: Fix firmware binary location
  media: ipu3-imgu: Fix compiler warnings
  media: staging/intel-ipu3: Add dual pipe support
  media: staging/intel-ipu3: Add Intel IPU3 meta data uAPI
  media: staging/intel-ipu3: Add imgu top level pci device driver
  media: staging/intel-ipu3: Add v4l2 driver based on media framework
  media: staging/intel-ipu3: Add css pipeline programming
  media: staging/intel-ipu3: css: Initialize css hardware
  media: staging/intel-ipu3: css: Compute and program ccs
  media: staging/intel-ipu3: css: Add static settings for image pipeline
  media: staging/intel-ipu3: css: Add support for firmware management
  media: staging/intel-ipu3: css: Add dma buff pool utility functions
  media: staging/intel-ipu3: Implement DMA mapping functions
  media: staging/intel-ipu3: mmu: Implement driver
  media: staging/intel-ipu3: abi: Add structs
  media: staging/intel-ipu3: abi: Add register definitions and enum
  ...

Diffstat:
MDocumentation/media/uapi/v4l/buffer.rst | 3+++
MDocumentation/media/uapi/v4l/dev-meta.rst | 33++++++++++++++++++++-------------
MDocumentation/media/uapi/v4l/meta-formats.rst | 1+
ADocumentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MDocumentation/media/uapi/v4l/vidioc-querycap.rst | 3+++
MDocumentation/media/v4l-drivers/index.rst | 1+
ADocumentation/media/v4l-drivers/ipu3.rst | 369+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MDocumentation/media/videodev2.h.rst.exceptions | 2++
MMAINTAINERS | 8++++++++
Mdrivers/media/common/videobuf2/videobuf2-v4l2.c | 1+
Mdrivers/media/v4l2-core/v4l2-compat-ioctl32.c | 2++
Mdrivers/media/v4l2-core/v4l2-dev.c | 12++++++++----
Mdrivers/media/v4l2-core/v4l2-fwnode.c | 4++--
Mdrivers/media/v4l2-core/v4l2-ioctl.c | 23+++++++++++++++++++++++
Mdrivers/staging/media/Kconfig | 2++
Mdrivers/staging/media/Makefile | 1+
Adrivers/staging/media/ipu3/Kconfig | 14++++++++++++++
Adrivers/staging/media/ipu3/Makefile | 11+++++++++++
Adrivers/staging/media/ipu3/TODO | 34++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/include/intel-ipu3.h | 2785+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-abi.h | 2011+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-css-fw.c | 265+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-css-fw.h | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-css-params.c | 2943+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-css-params.h | 28++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-css-pool.c | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-css-pool.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-css.c | 2391+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-css.h | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-dmamap.c | 270+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-dmamap.h | 22++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-mmu.c | 561+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-mmu.h | 35+++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-tables.c | 9609+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-tables.h | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3-v4l2.c | 1419+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3.c | 830+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/media/ipu3/ipu3.h | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/media/v4l2-ioctl.h | 17+++++++++++++++++
Minclude/uapi/linux/videodev2.h | 2++
40 files changed, 24661 insertions(+), 19 deletions(-)

diff --git a/Documentation/media/uapi/v4l/buffer.rst b/Documentation/media/uapi/v4l/buffer.rst @@ -472,6 +472,9 @@ enum v4l2_buf_type * - ``V4L2_BUF_TYPE_META_CAPTURE`` - 13 - Buffer for metadata capture, see :ref:`metadata`. + * - ``V4L2_BUF_TYPE_META_OUTPUT`` + - 14 + - Buffer for metadata output, see :ref:`metadata`. diff --git a/Documentation/media/uapi/v4l/dev-meta.rst b/Documentation/media/uapi/v4l/dev-meta.rst @@ -14,21 +14,27 @@ Metadata Interface ****************** Metadata refers to any non-image data that supplements video frames with -additional information. This may include statistics computed over the image -or frame capture parameters supplied by the image source. This interface is -intended for transfer of metadata to userspace and control of that operation. +additional information. This may include statistics computed over the image, +frame capture parameters supplied by the image source or device specific +parameters for specifying how the device processes images. This interface is +intended for transfer of metadata between the userspace and the hardware and +control of that operation. -The metadata interface is implemented on video capture device nodes. The device -can be dedicated to metadata or can implement both video and metadata capture -as specified in its reported capabilities. +The metadata interface is implemented on video device nodes. The device can be +dedicated to metadata or can support both video and metadata as specified in its +reported capabilities. Querying Capabilities ===================== -Device nodes supporting the metadata interface set the ``V4L2_CAP_META_CAPTURE`` -flag in the ``device_caps`` field of the +Device nodes supporting the metadata capture interface set the +``V4L2_CAP_META_CAPTURE`` flag in the ``device_caps`` field of the :c:type:`v4l2_capability` structure returned by the :c:func:`VIDIOC_QUERYCAP` -ioctl. That flag means the device can capture metadata to memory. +ioctl. That flag means the device can capture metadata to memory. Similarly, +device nodes supporting metadata output interface set the +``V4L2_CAP_META_OUTPUT`` flag in the ``device_caps`` field of +:c:type:`v4l2_capability` structure. That flag means the device can read +metadata from memory. At least one of the read/write or streaming I/O methods must be supported. @@ -42,10 +48,11 @@ to the basic :ref:`format` ioctls, the :c:func:`VIDIOC_ENUM_FMT` ioctl must be supported as well. To use the :ref:`format` ioctls applications set the ``type`` field of the -:c:type:`v4l2_format` structure to ``V4L2_BUF_TYPE_META_CAPTURE`` and use the -:c:type:`v4l2_meta_format` ``meta`` member of the ``fmt`` union as needed per -the desired operation. Both drivers and applications must set the remainder of -the :c:type:`v4l2_format` structure to 0. +:c:type:`v4l2_format` structure to ``V4L2_BUF_TYPE_META_CAPTURE`` or to +``V4L2_BUF_TYPE_META_OUTPUT`` and use the :c:type:`v4l2_meta_format` ``meta`` +member of the ``fmt`` union as needed per the desired operation. Both drivers +and applications must set the remainder of the :c:type:`v4l2_format` structure +to 0. .. c:type:: v4l2_meta_format diff --git a/Documentation/media/uapi/v4l/meta-formats.rst b/Documentation/media/uapi/v4l/meta-formats.rst @@ -19,6 +19,7 @@ These formats are used for the :ref:`metadata` interface only. .. toctree:: :maxdepth: 1 + pixfmt-meta-intel-ipu3 pixfmt-meta-d4xx pixfmt-meta-uvc pixfmt-meta-vsp1-hgo diff --git a/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst b/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst @@ -0,0 +1,178 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _v4l2-meta-fmt-params: +.. _v4l2-meta-fmt-stat-3a: + +****************************************************************** +V4L2_META_FMT_IPU3_PARAMS ('ip3p'), V4L2_META_FMT_IPU3_3A ('ip3s') +****************************************************************** + +.. c:type:: ipu3_uapi_stats_3a + +3A statistics +============= + +For IPU3 ImgU, the 3A statistics accelerators collect different statistics over +an input bayer frame. Those statistics, defined in data struct :c:type:`ipu3_uapi_stats_3a`, +are obtained from "ipu3-imgu 3a stat" metadata capture video node, which are then +passed to user space for statistics analysis using :c:type:`v4l2_meta_format` interface. + +The statistics collected are AWB (Auto-white balance) RGBS (Red, Green, Blue and +Saturation measure) cells, AWB filter response, AF (Auto-focus) filter response, +and AE (Auto-exposure) histogram. + +struct :c:type:`ipu3_uapi_4a_config` saves configurable parameters for all above. + +.. code-block:: c + + struct ipu3_uapi_stats_3a { + struct ipu3_uapi_awb_raw_buffer awb_raw_buffer; + struct ipu3_uapi_ae_raw_buffer_aligned ae_raw_buffer[IPU3_UAPI_MAX_STRIPES]; + struct ipu3_uapi_af_raw_buffer af_raw_buffer; + struct ipu3_uapi_awb_fr_raw_buffer awb_fr_raw_buffer; + struct ipu3_uapi_4a_config stats_4a_config; + __u32 ae_join_buffers; + __u8 padding[28]; + struct ipu3_uapi_stats_3a_bubble_info_per_stripe stats_3a_bubble_per_stripe; + struct ipu3_uapi_ff_status stats_3a_status; + }; + +.. c:type:: ipu3_uapi_params + +Pipeline parameters +=================== + +IPU3 pipeline has a number of image processing stages, each of which takes a +set of parameters as input. The major stages of pipelines are shown here: + +Raw pixels -> Bayer Downscaling -> Optical Black Correction -> + +Linearization -> Lens Shading Correction -> White Balance / Exposure / + +Focus Apply -> Bayer Noise Reduction -> ANR -> Demosaicing -> Color + +Correction Matrix -> Gamma correction -> Color Space Conversion -> + +Chroma Down Scaling -> Chromatic Noise Reduction -> Total Color + +Correction -> XNR3 -> TNR -> DDR + +The table below presents a description of the above algorithms. + +======================== ======================================================= +Name Description +======================== ======================================================= +Optical Black Correction Optical Black Correction block subtracts a pre-defined + value from the respective pixel values to obtain better + image quality. + Defined in :c:type:`ipu3_uapi_obgrid_param`. +Linearization This algo block uses linearization parameters to + address non-linearity sensor effects. The Lookup table + table is defined in + :c:type:`ipu3_uapi_isp_lin_vmem_params`. +SHD Lens shading correction is used to correct spatial + non-uniformity of the pixel response due to optical + lens shading. This is done by applying a different gain + for each pixel. The gain, black level etc are + configured in :c:type:`ipu3_uapi_shd_config_static`. +BNR Bayer noise reduction block removes image noise by + applying a bilateral filter. + See :c:type:`ipu3_uapi_bnr_static_config` for details. +ANR Advanced Noise Reduction is a block based algorithm + that performs noise reduction in the Bayer domain. The + convolution matrix etc can be found in + :c:type:`ipu3_uapi_anr_config`. +Demosaicing Demosaicing converts raw sensor data in Bayer format + into RGB (Red, Green, Blue) presentation. Then add + outputs of estimation of Y channel for following stream + processing by Firmware. The struct is defined as + :c:type:`ipu3_uapi_dm_config`. (TODO) +Color Correction Color Correction algo transforms sensor specific color + space to the standard "sRGB" color space. This is done + by applying 3x3 matrix defined in + :c:type:`ipu3_uapi_ccm_mat_config`. +Gamma correction Gamma correction :c:type:`ipu3_uapi_gamma_config` is a + basic non-linear tone mapping correction that is + applied per pixel for each pixel component. +CSC Color space conversion transforms each pixel from the + RGB primary presentation to YUV (Y: brightness, + UV: Luminance) presentation. This is done by applying + a 3x3 matrix defined in + :c:type:`ipu3_uapi_csc_mat_config` +CDS Chroma down sampling + After the CSC is performed, the Chroma Down Sampling + is applied for a UV plane down sampling by a factor + of 2 in each direction for YUV 4:2:0 using a 4x2 + configurable filter :c:type:`ipu3_uapi_cds_params`. +CHNR Chroma noise reduction + This block processes only the chrominance pixels and + performs noise reduction by cleaning the high + frequency noise. + See struct :c:type:`ipu3_uapi_yuvp1_chnr_config`. +TCC Total color correction as defined in struct + :c:type:`ipu3_uapi_yuvp2_tcc_static_config`. +XNR3 eXtreme Noise Reduction V3 is the third revision of + noise reduction algorithm used to improve image + quality. This removes the low frequency noise in the + captured image. Two related structs are being defined, + :c:type:`ipu3_uapi_isp_xnr3_params` for ISP data memory + and :c:type:`ipu3_uapi_isp_xnr3_vmem_params` for vector + memory. +TNR Temporal Noise Reduction block compares successive + frames in time to remove anomalies / noise in pixel + values. :c:type:`ipu3_uapi_isp_tnr3_vmem_params` and + :c:type:`ipu3_uapi_isp_tnr3_params` are defined for ISP + vector and data memory respectively. +======================== ======================================================= + +A few stages of the pipeline will be executed by firmware running on the ISP +processor, while many others will use a set of fixed hardware blocks also +called accelerator cluster (ACC) to crunch pixel data and produce statistics. + +ACC parameters of individual algorithms, as defined by +:c:type:`ipu3_uapi_acc_param`, can be chosen to be applied by the user +space through struct :c:type:`ipu3_uapi_flags` embedded in +:c:type:`ipu3_uapi_params` structure. For parameters that are configured as +not enabled by the user space, the corresponding structs are ignored by the +driver, in which case the existing configuration of the algorithm will be +preserved. + +Both 3A statistics and pipeline parameters described here are closely tied to +the underlying camera sub-system (CSS) APIs. They are usually consumed and +produced by dedicated user space libraries that comprise the important tuning +tools, thus freeing the developers from being bothered with the low level +hardware and algorithm details. + +It should be noted that IPU3 DMA operations require the addresses of all data +structures (that includes both input and output) to be aligned on 32 byte +boundaries. + +The meta data :c:type:`ipu3_uapi_params` will be sent to "ipu3-imgu parameters" +video node in ``V4L2_BUF_TYPE_META_CAPTURE`` format. + +.. code-block:: c + + struct ipu3_uapi_params { + /* Flags which of the settings below are to be applied */ + struct ipu3_uapi_flags use; + + /* Accelerator cluster parameters */ + struct ipu3_uapi_acc_param acc_param; + + /* ISP vector address space parameters */ + struct ipu3_uapi_isp_lin_vmem_params lin_vmem_params; + struct ipu3_uapi_isp_tnr3_vmem_params tnr3_vmem_params; + struct ipu3_uapi_isp_xnr3_vmem_params xnr3_vmem_params; + + /* ISP data memory (DMEM) parameters */ + struct ipu3_uapi_isp_tnr3_params tnr3_dmem_params; + struct ipu3_uapi_isp_xnr3_params xnr3_dmem_params; + + /* Optical black level compensation */ + struct ipu3_uapi_obgrid_param obgrid_param; + }; + +Intel IPU3 ImgU uAPI data types +=============================== + +.. kernel-doc:: drivers/staging/media/ipu3/include/intel-ipu3.h diff --git a/Documentation/media/uapi/v4l/vidioc-querycap.rst b/Documentation/media/uapi/v4l/vidioc-querycap.rst @@ -258,6 +258,9 @@ specification the ioctl returns an ``EINVAL`` error code. * - ``V4L2_CAP_STREAMING`` - 0x04000000 - The device supports the :ref:`streaming <mmap>` I/O method. + * - ``V4L2_CAP_META_OUTPUT`` + - 0x08000000 + - The device supports the :ref:`metadata` output interface. * - ``V4L2_CAP_TOUCH`` - 0x10000000 - This is a touch device. diff --git a/Documentation/media/v4l-drivers/index.rst b/Documentation/media/v4l-drivers/index.rst @@ -44,6 +44,7 @@ For more details see the file COPYING in the source distribution of Linux. davinci-vpbe fimc imx + ipu3 ivtv max2175 meye diff --git a/Documentation/media/v4l-drivers/ipu3.rst b/Documentation/media/v4l-drivers/ipu3.rst @@ -0,0 +1,369 @@ +.. include:: <isonum.txt> + +=============================================================== +Intel Image Processing Unit 3 (IPU3) Imaging Unit (ImgU) driver +=============================================================== + +Copyright |copy| 2018 Intel Corporation + +Introduction +============ + +This file documents the Intel IPU3 (3rd generation Image Processing Unit) +Imaging Unit drivers located under drivers/media/pci/intel/ipu3 (CIO2) as well +as under drivers/staging/media/ipu3 (ImgU). + +The Intel IPU3 found in certain Kaby Lake (as well as certain Sky Lake) +platforms (U/Y processor lines) is made up of two parts namely the Imaging Unit +(ImgU) and the CIO2 device (MIPI CSI2 receiver). + +The CIO2 device receives the raw Bayer data from the sensors and outputs the +frames in a format that is specific to the IPU3 (for consumption by the IPU3 +ImgU). The CIO2 driver is available as drivers/media/pci/intel/ipu3/ipu3-cio2* +and is enabled through the CONFIG_VIDEO_IPU3_CIO2 config option. + +The Imaging Unit (ImgU) is responsible for processing images captured +by the IPU3 CIO2 device. The ImgU driver sources can be found under +drivers/staging/media/ipu3 directory. The driver is enabled through the +CONFIG_VIDEO_IPU3_IMGU config option. + +The two driver modules are named ipu3_csi2 and ipu3_imgu, respectively. + +The drivers has been tested on Kaby Lake platforms (U/Y processor lines). + +Both of the drivers implement V4L2, Media Controller and V4L2 sub-device +interfaces. The IPU3 CIO2 driver supports camera sensors connected to the CIO2 +MIPI CSI-2 interfaces through V4L2 sub-device sensor drivers. + +CIO2 +==== + +The CIO2 is represented as a single V4L2 subdev, which provides a V4L2 subdev +interface to the user space. There is a video node for each CSI-2 receiver, +with a single media controller interface for the entire device. + +The CIO2 contains four independent capture channel, each with its own MIPI CSI-2 +receiver and DMA engine. Each channel is modelled as a V4L2 sub-device exposed +to userspace as a V4L2 sub-device node and has two pads: + +.. tabularcolumns:: |p{0.8cm}|p{4.0cm}|p{4.0cm}| + +.. flat-table:: + + * - pad + - direction + - purpose + + * - 0 + - sink + - MIPI CSI-2 input, connected to the sensor subdev + + * - 1 + - source + - Raw video capture, connected to the V4L2 video interface + +The V4L2 video interfaces model the DMA engines. They are exposed to userspace +as V4L2 video device nodes. + +Capturing frames in raw Bayer format +------------------------------------ + +CIO2 MIPI CSI2 receiver is used to capture frames (in packed raw Bayer format) +from the raw sensors connected to the CSI2 ports. The captured frames are used +as input to the ImgU driver. + +Image processing using IPU3 ImgU requires tools such as raw2pnm [#f1]_, and +yavta [#f2]_ due to the following unique requirements and / or features specific +to IPU3. + +-- The IPU3 CSI2 receiver outputs the captured frames from the sensor in packed +raw Bayer format that is specific to IPU3. + +-- Multiple video nodes have to be operated simultaneously. + +Let us take the example of ov5670 sensor connected to CSI2 port 0, for a +2592x1944 image capture. + +Using the media contorller APIs, the ov5670 sensor is configured to send +frames in packed raw Bayer format to IPU3 CSI2 receiver. + +# This example assumes /dev/media0 as the CIO2 media device + +export MDEV=/dev/media0 + +# and that ov5670 sensor is connected to i2c bus 10 with address 0x36 + +export SDEV=$(media-ctl -d $MDEV -e "ov5670 10-0036") + +# Establish the link for the media devices using media-ctl [#f3]_ +media-ctl -d $MDEV -l "ov5670:0 -> ipu3-csi2 0:0[1]" + +# Set the format for the media devices +media-ctl -d $MDEV -V "ov5670:0 [fmt:SGRBG10/2592x1944]" + +media-ctl -d $MDEV -V "ipu3-csi2 0:0 [fmt:SGRBG10/2592x1944]" + +media-ctl -d $MDEV -V "ipu3-csi2 0:1 [fmt:SGRBG10/2592x1944]" + +Once the media pipeline is configured, desired sensor specific settings +(such as exposure and gain settings) can be set, using the yavta tool. + +e.g + +yavta -w 0x009e0903 444 $SDEV + +yavta -w 0x009e0913 1024 $SDEV + +yavta -w 0x009e0911 2046 $SDEV + +Once the desired sensor settings are set, frame captures can be done as below. + +e.g + +yavta --data-prefix -u -c10 -n5 -I -s2592x1944 --file=/tmp/frame-#.bin \ + -f IPU3_SGRBG10 $(media-ctl -d $MDEV -e "ipu3-cio2 0") + +With the above command, 10 frames are captured at 2592x1944 resolution, with +sGRBG10 format and output as IPU3_SGRBG10 format. + +The captured frames are available as /tmp/frame-#.bin files. + +ImgU +==== + +The ImgU is represented as two V4L2 subdevs, each of which provides a V4L2 +subdev interface to the user space. + +Each V4L2 subdev represents a pipe, which can support a maximum of 2 streams. +This helps to support advanced camera features like Continuous View Finder (CVF) +and Snapshot During Video(SDV). + +The ImgU contains two independent pipes, each modelled as a V4L2 sub-device +exposed to userspace as a V4L2 sub-device node. + +Each pipe has two sink pads and three source pads for the following purpose: + +.. tabularcolumns:: |p{0.8cm}|p{4.0cm}|p{4.0cm}| + +.. flat-table:: + + * - pad + - direction + - purpose + + * - 0 + - sink + - Input raw video stream + + * - 1 + - sink + - Processing parameters + + * - 2 + - source + - Output processed video stream + + * - 3 + - source + - Output viewfinder video stream + + * - 4 + - source + - 3A statistics + +Each pad is connected to a corresponding V4L2 video interface, exposed to +userspace as a V4L2 video device node. + +Device operation +---------------- + +With ImgU, once the input video node ("ipu3-imgu 0/1":0, in +<entity>:<pad-number> format) is queued with buffer (in packed raw Bayer +format), ImgU starts processing the buffer and produces the video output in YUV +format and statistics output on respective output nodes. The driver is expected +to have buffers ready for all of parameter, output and statistics nodes, when +input video node is queued with buffer. + +At a minimum, all of input, main output, 3A statistics and viewfinder +video nodes should be enabled for IPU3 to start image processing. + +Each ImgU V4L2 subdev has the following set of video nodes. + +input, output and viewfinder video nodes +---------------------------------------- + +The frames (in packed raw Bayer format specific to the IPU3) received by the +input video node is processed by the IPU3 Imaging Unit and are output to 2 video +nodes, with each targeting a different purpose (main output and viewfinder +output). + +Details onand the Bayer format specific to the IPU3 can be found in +:ref:`v4l2-pix-fmt-ipu3-sbggr10`. + +The driver supports V4L2 Video Capture Interface as defined at :ref:`devices`. + +Only the multi-planar API is supported. More details can be found at +:ref:`planar-apis`. + +Parameters video node +--------------------- + +The parameters video node receives the ImgU algorithm parameters that are used +to configure how the ImgU algorithms process the image. + +Details on processing parameters specific to the IPU3 can be found in +:ref:`v4l2-meta-fmt-params`. + +3A statistics video node +------------------------ + +3A statistics video node is used by the ImgU driver to output the 3A (auto +focus, auto exposure and auto white balance) statistics for the frames that are +being processed by the ImgU to user space applications. User space applications +can use this statistics data to compute the desired algorithm parameters for +the ImgU. + +Configuring the Intel IPU3 +========================== + +The IPU3 ImgU pipelines can be configured using the Media Controller, defined at +:ref:`media_controller`. + +Firmware binary selection +------------------------- + +The firmware binary is selected using the V4L2_CID_INTEL_IPU3_MODE, currently +defined in drivers/staging/media/ipu3/include/intel-ipu3.h . "VIDEO" and "STILL" +modes are available. + +Processing the image in raw Bayer format +---------------------------------------- + +Configuring ImgU V4L2 subdev for image processing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ImgU V4L2 subdevs have to be configured with media controller APIs to have +all the video nodes setup correctly. + +Let us take "ipu3-imgu 0" subdev as an example. + +media-ctl -d $MDEV -r + +media-ctl -d $MDEV -l "ipu3-imgu 0 input":0 -> "ipu3-imgu 0":0[1] + +media-ctl -d $MDEV -l "ipu3-imgu 0":2 -> "ipu3-imgu 0 output":0[1] + +media-ctl -d $MDEV -l "ipu3-imgu 0":3 -> "ipu3-imgu 0 viewfinder":0[1] + +media-ctl -d $MDEV -l "ipu3-imgu 0":4 -> "ipu3-imgu 0 3a stat":0[1] + +Also the pipe mode of the corresponding V4L2 subdev should be set as desired +(e.g 0 for video mode or 1 for still mode) through the control id 0x009819a1 as +below. + +yavta -w "0x009819A1 1" /dev/v4l-subdev7 + +RAW Bayer frames go through the following ImgU pipeline HW blocks to have the +processed image output to the DDR memory. + +RAW Bayer frame -> Input Feeder -> Bayer Down Scaling (BDS) -> Geometric +Distortion Correction (GDC) -> DDR + +The ImgU V4L2 subdev has to be configured with the supported resolutions in all +the above HW blocks, for a given input resolution. + +For a given supported resolution for an input frame, the Input Feeder, Bayer +Down Scaling and GDC blocks should be configured with the supported resolutions. +This information can be obtained by looking at the following IPU3 ImgU +configuration table. + +https://chromium.googlesource.com/chromiumos/overlays/board-overlays/+/master + +Under baseboard-poppy/media-libs/cros-camera-hal-configs-poppy/files/gcss +directory, graph_settings_ov5670.xml can be used as an example. + +The following steps prepare the ImgU pipeline for the image processing. + +1. The ImgU V4L2 subdev data format should be set by using the +VIDIOC_SUBDEV_S_FMT on pad 0, using the GDC width and height obtained above. + +2. The ImgU V4L2 subdev cropping should be set by using the +VIDIOC_SUBDEV_S_SELECTION on pad 0, with V4L2_SEL_TGT_CROP as the target, +using the input feeder height and width. + +3. The ImgU V4L2 subdev composing should be set by using the +VIDIOC_SUBDEV_S_SELECTION on pad 0, with V4L2_SEL_TGT_COMPOSE as the target, +using the BDS height and width. + +For the ov5670 example, for an input frame with a resolution of 2592x1944 +(which is input to the ImgU subdev pad 0), the corresponding resolutions +for input feeder, BDS and GDC are 2592x1944, 2592x1944 and 2560x1920 +respectively. + +Once this is done, the received raw Bayer frames can be input to the ImgU +V4L2 subdev as below, using the open source application v4l2n [#f1]_. + +For an image captured with 2592x1944 [#f4]_ resolution, with desired output +resolution as 2560x1920 and viewfinder resolution as 2560x1920, the following +v4l2n command can be used. This helps process the raw Bayer frames and produces +the desired results for the main output image and the viewfinder output, in NV12 +format. + +v4l2n --pipe=4 --load=/tmp/frame-#.bin --open=/dev/video4 +--fmt=type:VIDEO_OUTPUT_MPLANE,width=2592,height=1944,pixelformat=0X47337069 +--reqbufs=type:VIDEO_OUTPUT_MPLANE,count:1 --pipe=1 --output=/tmp/frames.out +--open=/dev/video5 +--fmt=type:VIDEO_CAPTURE_MPLANE,width=2560,height=1920,pixelformat=NV12 +--reqbufs=type:VIDEO_CAPTURE_MPLANE,count:1 --pipe=2 --output=/tmp/frames.vf +--open=/dev/video6 +--fmt=type:VIDEO_CAPTURE_MPLANE,width=2560,height=1920,pixelformat=NV12 +--reqbufs=type:VIDEO_CAPTURE_MPLANE,count:1 --pipe=3 --open=/dev/video7 +--output=/tmp/frames.3A --fmt=type:META_CAPTURE,? +--reqbufs=count:1,type:META_CAPTURE --pipe=1,2,3,4 --stream=5 + +where /dev/video4, /dev/video5, /dev/video6 and /dev/video7 devices point to +input, output, viewfinder and 3A statistics video nodes respectively. + +Converting the raw Bayer image into YUV domain +---------------------------------------------- + +The processed images after the above step, can be converted to YUV domain +as below. + +Main output frames +~~~~~~~~~~~~~~~~~~ + +raw2pnm -x2560 -y1920 -fNV12 /tmp/frames.out /tmp/frames.out.ppm + +where 2560x1920 is output resolution, NV12 is the video format, followed +by input frame and output PNM file. + +Viewfinder output frames +~~~~~~~~~~~~~~~~~~~~~~~~ + +raw2pnm -x2560 -y1920 -fNV12 /tmp/frames.vf /tmp/frames.vf.ppm + +where 2560x1920 is output resolution, NV12 is the video format, followed +by input frame and output PNM file. + +Example user space code for IPU3 +================================ + +User space code that configures and uses IPU3 is available here. + +https://chromium.googlesource.com/chromiumos/platform/arc-camera/+/master/ + +The source can be located under hal/intel directory. + +References +========== + +.. [#f5] include/uapi/linux/intel-ipu3.h + +.. [#f1] https://github.com/intel/nvt + +.. [#f2] http://git.ideasonboard.org/yavta.git + +.. [#f3] http://git.ideasonboard.org/?p=media-ctl.git;a=summary + +.. [#f4] ImgU limitation requires an additional 16x16 for all input resolutions diff --git a/Documentation/media/videodev2.h.rst.exceptions b/Documentation/media/videodev2.h.rst.exceptions @@ -30,6 +30,7 @@ replace symbol V4L2_FIELD_TOP :c:type:`v4l2_field` # Documented enum v4l2_buf_type replace symbol V4L2_BUF_TYPE_META_CAPTURE :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_META_OUTPUT :c:type:`v4l2_buf_type` replace symbol V4L2_BUF_TYPE_SDR_CAPTURE :c:type:`v4l2_buf_type` replace symbol V4L2_BUF_TYPE_SDR_OUTPUT :c:type:`v4l2_buf_type` replace symbol V4L2_BUF_TYPE_SLICED_VBI_CAPTURE :c:type:`v4l2_buf_type` @@ -163,6 +164,7 @@ replace define V4L2_CAP_META_CAPTURE device-capabilities replace define V4L2_CAP_READWRITE device-capabilities replace define V4L2_CAP_ASYNCIO device-capabilities replace define V4L2_CAP_STREAMING device-capabilities +replace define V4L2_CAP_META_OUTPUT device-capabilities replace define V4L2_CAP_DEVICE_CAPS device-capabilities replace define V4L2_CAP_TOUCH device-capabilities diff --git a/MAINTAINERS b/MAINTAINERS @@ -7645,6 +7645,14 @@ S: Maintained F: drivers/media/pci/intel/ipu3/ F: Documentation/media/uapi/v4l/pixfmt-srggb10-ipu3.rst +INTEL IPU3 CSI-2 IMGU DRIVER +M: Sakari Ailus <sakari.ailus@linux.intel.com> +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/staging/media/ipu3/ +F: Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst +F: Documentation/media/v4l-drivers/ipu3.rst + INTEL IXP4XX QMGR, NPE, ETHERNET and HSS SUPPORT M: Krzysztof Halasa <khalasa@piap.pl> S: Maintained diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -709,6 +709,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) requested_sizes[0] = f->fmt.sdr.buffersize; break; case V4L2_BUF_TYPE_META_CAPTURE: + case V4L2_BUF_TYPE_META_OUTPUT: requested_sizes[0] = f->fmt.meta.buffersize; break; default: diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -323,6 +323,7 @@ static int __get_v4l2_format32(struct v4l2_format __user *p64, return copy_in_user(&p64->fmt.sdr, &p32->fmt.sdr, sizeof(p64->fmt.sdr)) ? -EFAULT : 0; case V4L2_BUF_TYPE_META_CAPTURE: + case V4L2_BUF_TYPE_META_OUTPUT: return copy_in_user(&p64->fmt.meta, &p32->fmt.meta, sizeof(p64->fmt.meta)) ? -EFAULT : 0; default: @@ -392,6 +393,7 @@ static int __put_v4l2_format32(struct v4l2_format __user *p64, return copy_in_user(&p32->fmt.sdr, &p64->fmt.sdr, sizeof(p64->fmt.sdr)) ? -EFAULT : 0; case V4L2_BUF_TYPE_META_CAPTURE: + case V4L2_BUF_TYPE_META_OUTPUT: return copy_in_user(&p32->fmt.meta, &p64->fmt.meta, sizeof(p64->fmt.meta)) ? -EFAULT : 0; default: diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c @@ -597,7 +597,8 @@ static void determine_valid_ioctls(struct video_device *vdev) ops->vidioc_enum_fmt_vid_overlay || ops->vidioc_enum_fmt_meta_cap)) || (is_tx && (ops->vidioc_enum_fmt_vid_out || - ops->vidioc_enum_fmt_vid_out_mplane))) + ops->vidioc_enum_fmt_vid_out_mplane || + ops->vidioc_enum_fmt_meta_out))) set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); if ((is_rx && (ops->vidioc_g_fmt_vid_cap || ops->vidioc_g_fmt_vid_cap_mplane || @@ -605,7 +606,8 @@ static void determine_valid_ioctls(struct video_device *vdev) ops->vidioc_g_fmt_meta_cap)) || (is_tx && (ops->vidioc_g_fmt_vid_out || ops->vidioc_g_fmt_vid_out_mplane || - ops->vidioc_g_fmt_vid_out_overlay))) + ops->vidioc_g_fmt_vid_out_overlay || + ops->vidioc_g_fmt_meta_out))) set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); if ((is_rx && (ops->vidioc_s_fmt_vid_cap || ops->vidioc_s_fmt_vid_cap_mplane || @@ -613,7 +615,8 @@ static void determine_valid_ioctls(struct video_device *vdev) ops->vidioc_s_fmt_meta_cap)) || (is_tx && (ops->vidioc_s_fmt_vid_out || ops->vidioc_s_fmt_vid_out_mplane || - ops->vidioc_s_fmt_vid_out_overlay))) + ops->vidioc_s_fmt_vid_out_overlay || + ops->vidioc_s_fmt_meta_out))) set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); if ((is_rx && (ops->vidioc_try_fmt_vid_cap || ops->vidioc_try_fmt_vid_cap_mplane || @@ -621,7 +624,8 @@ static void determine_valid_ioctls(struct video_device *vdev) ops->vidioc_try_fmt_meta_cap)) || (is_tx && (ops->vidioc_try_fmt_vid_out || ops->vidioc_try_fmt_vid_out_mplane || - ops->vidioc_try_fmt_vid_out_overlay))) + ops->vidioc_try_fmt_vid_out_overlay || + ops->vidioc_try_fmt_meta_out))) set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay); SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf); diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -310,8 +310,8 @@ v4l2_fwnode_endpoint_parse_parallel_bus(struct fwnode_handle *fwnode, } if (!fwnode_property_read_u32(fwnode, "data-active", &v)) { - flags &= ~(V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING); + flags &= ~(V4L2_MBUS_DATA_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_LOW); flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH : V4L2_MBUS_DATA_ACTIVE_LOW; pr_debug("data-active %s\n", v ? "high" : "low"); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -194,6 +194,7 @@ const char *v4l2_type_names[] = { [V4L2_BUF_TYPE_SDR_CAPTURE] = "sdr-cap", [V4L2_BUF_TYPE_SDR_OUTPUT] = "sdr-out", [V4L2_BUF_TYPE_META_CAPTURE] = "meta-cap", + [V4L2_BUF_TYPE_META_OUTPUT] = "meta-out", }; EXPORT_SYMBOL(v4l2_type_names); @@ -366,6 +367,7 @@ static void v4l_print_format(const void *arg, bool write_only) (sdr->pixelformat >> 24) & 0xff); break; case V4L2_BUF_TYPE_META_CAPTURE: + case V4L2_BUF_TYPE_META_OUTPUT: meta = &p->fmt.meta; pr_cont(", dataformat=%c%c%c%c, buffersize=%u\n", (meta->dataformat >> 0) & 0xff, @@ -999,6 +1001,10 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) if (is_vid && is_rx && ops->vidioc_g_fmt_meta_cap) return 0; break; + case V4L2_BUF_TYPE_META_OUTPUT: + if (is_vid && is_tx && ops->vidioc_g_fmt_meta_out) + return 0; + break; default: break; } @@ -1410,6 +1416,11 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, break; ret = ops->vidioc_enum_fmt_meta_cap(file, fh, arg); break; + case V4L2_BUF_TYPE_META_OUTPUT: + if (unlikely(!ops->vidioc_enum_fmt_meta_out)) + break; + ret = ops->vidioc_enum_fmt_meta_out(file, fh, arg); + break; } if (ret == 0) v4l_fill_fmtdesc(p); @@ -1488,6 +1499,8 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, return ops->vidioc_g_fmt_sdr_out(file, fh, arg); case V4L2_BUF_TYPE_META_CAPTURE: return ops->vidioc_g_fmt_meta_cap(file, fh, arg); + case V4L2_BUF_TYPE_META_OUTPUT: + return ops->vidioc_g_fmt_meta_out(file, fh, arg); } return -EINVAL; } @@ -1601,6 +1614,11 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, break; CLEAR_AFTER_FIELD(p, fmt.meta); return ops->vidioc_s_fmt_meta_cap(file, fh, arg); + case V4L2_BUF_TYPE_META_OUTPUT: + if (unlikely(!ops->vidioc_s_fmt_meta_out)) + break; + CLEAR_AFTER_FIELD(p, fmt.meta); + return ops->vidioc_s_fmt_meta_out(file, fh, arg); } return -EINVAL; } @@ -1693,6 +1711,11 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, break; CLEAR_AFTER_FIELD(p, fmt.meta); return ops->vidioc_try_fmt_meta_cap(file, fh, arg); + case V4L2_BUF_TYPE_META_OUTPUT: + if (unlikely(!ops->vidioc_try_fmt_meta_out)) + break; + CLEAR_AFTER_FIELD(p, fmt.meta); + return ops->vidioc_try_fmt_meta_out(file, fh, arg); } return -EINVAL; } diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig @@ -39,4 +39,6 @@ source "drivers/staging/media/tegra-vde/Kconfig" source "drivers/staging/media/zoran/Kconfig" +source "drivers/staging/media/ipu3/Kconfig" + endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/ obj-$(CONFIG_VIDEO_ROCKCHIP_VPU) += rockchip/vpu/ +obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ diff --git a/drivers/staging/media/ipu3/Kconfig b/drivers/staging/media/ipu3/Kconfig @@ -0,0 +1,14 @@ +config VIDEO_IPU3_IMGU + tristate "Intel ipu3-imgu driver" + depends on PCI && VIDEO_V4L2 + depends on MEDIA_CONTROLLER && VIDEO_V4L2_SUBDEV_API + depends on X86 + select IOMMU_IOVA + select VIDEOBUF2_DMA_SG + ---help--- + This is the Video4Linux2 driver for Intel IPU3 image processing unit, + found in Intel Skylake and Kaby Lake SoCs and used for processing + images and video. + + Say Y or M here if you have a Skylake/Kaby Lake SoC with a MIPI + camera. The module will be called ipu3-imgu. diff --git a/drivers/staging/media/ipu3/Makefile b/drivers/staging/media/ipu3/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the IPU3 ImgU drivers +# + +ipu3-imgu-objs += \ + ipu3-mmu.o ipu3-dmamap.o \ + ipu3-tables.o ipu3-css-pool.o \ + ipu3-css-fw.o ipu3-css-params.o \ + ipu3-css.o ipu3-v4l2.o ipu3.o + +obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3-imgu.o diff --git a/drivers/staging/media/ipu3/TODO b/drivers/staging/media/ipu3/TODO @@ -0,0 +1,34 @@ +This is a list of things that need to be done to get this driver out of the +staging directory. + +- Request API conversion. Remove of the dual pipeline and associate buffers + as well as formats and the binary used to a request. Remove the + opportunistic buffer management. (Sakari) + +- Using ENABLED and IMMUTABLE link flags for the links where those are + relevant. (Sakari) + +- Prefix imgu for all public APIs, i.e. change ipu3_v4l2_register() to + imgu_v4l2_register(). (Sakari) + +- Use V4L2_CTRL_TYPE_MENU for dual-pipe mode control. (Sakari) + +- IPU3 driver documentation (Laurent) + Add diagram in driver rst to describe output capability. + Comments on configuring v4l2 subdevs for CIO2 and ImgU. + +- uAPI documentation: + Further clarification on some ambiguities such as data type conversion of + IEFD CU inputs. (Sakari) + Move acronyms to doc-rst file. (Mauro) + +- Switch to yavta from v4l2n in driver docs. + +- Elaborate the functionality of different selection rectangles in driver + documentation. This may require driver changes as well. + +- More detailed documentation on calculating BDS, GCD etc. sizes needed. + +- Document different operation modes, and which buffer queues are relevant + in each mode. To process an image, which queues require a buffer an in + which ones is it optional? diff --git a/drivers/staging/media/ipu3/include/intel-ipu3.h b/drivers/staging/media/ipu3/include/intel-ipu3.h @@ -0,0 +1,2785 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2017 - 2018 Intel Corporation */ + +#ifndef __IPU3_UAPI_H +#define __IPU3_UAPI_H + +#include <linux/types.h> + +/* from /drivers/staging/media/ipu3/include/videodev2.h */ + +/* Vendor specific - used for IPU3 camera sub-system */ +#define V4L2_META_FMT_IPU3_PARAMS v4l2_fourcc('i', 'p', '3', 'p') /* IPU3 processing parameters */ +#define V4L2_META_FMT_IPU3_STAT_3A v4l2_fourcc('i', 'p', '3', 's') /* IPU3 3A statistics */ + +/* from include/uapi/linux/v4l2-controls.h */ +#define V4L2_CID_INTEL_IPU3_BASE (V4L2_CID_USER_BASE + 0x10c0) +#define V4L2_CID_INTEL_IPU3_MODE (V4L2_CID_INTEL_IPU3_BASE + 1) + +/* custom ctrl to set pipe mode */ +enum ipu3_running_mode { + IPU3_RUNNING_MODE_VIDEO = 0, + IPU3_RUNNING_MODE_STILL = 1, +}; + +/******************* ipu3_uapi_stats_3a *******************/ + +#define IPU3_UAPI_MAX_STRIPES 2 +#define IPU3_UAPI_MAX_BUBBLE_SIZE 10 + +#define IPU3_UAPI_GRID_START_MASK ((1 << 12) - 1) +#define IPU3_UAPI_GRID_Y_START_EN (1 << 15) + +/* controls generation of meta_data (like FF enable/disable) */ +#define IPU3_UAPI_AWB_RGBS_THR_B_EN (1 << 14) +#define IPU3_UAPI_AWB_RGBS_THR_B_INCL_SAT (1 << 15) + +/** + * struct ipu3_uapi_grid_config - Grid plane config + * + * @width: Grid horizontal dimensions, in number of grid blocks(cells). + * @height: Grid vertical dimensions, in number of grid cells. + * @block_width_log2: Log2 of the width of each cell in pixels. + * for (2^3, 2^4, 2^5, 2^6, 2^7), values [3, 7]. + * @block_height_log2: Log2 of the height of each cell in pixels. + * for (2^3, 2^4, 2^5, 2^6, 2^7), values [3, 7]. + * @height_per_slice: The number of blocks in vertical axis per slice. + * Default 2. + * @x_start: X value of top left corner of Region of Interest(ROI). + * @y_start: Y value of top left corner of ROI + * @x_end: X value of bottom right corner of ROI + * @y_end: Y value of bottom right corner of ROI + * + * Due to the size of total amount of collected data, most statistics + * create a grid-based output, and the data is then divided into "slices". + */ +struct ipu3_uapi_grid_config { + __u8 width; + __u8 height; + __u16 block_width_log2:3; + __u16 block_height_log2:3; + __u16 height_per_slice:8; + __u16 x_start; + __u16 y_start; + __u16 x_end; + __u16 y_end; +} __packed; + +/* + * The grid based data is divided into "slices" called set, each slice of setX + * refers to ipu3_uapi_grid_config width * height_per_slice. + */ +#define IPU3_UAPI_AWB_MAX_SETS 60 +/* Based on grid size 80 * 60 and cell size 16 x 16 */ +#define IPU3_UAPI_AWB_SET_SIZE 1280 +#define IPU3_UAPI_AWB_MD_ITEM_SIZE 8 +#define IPU3_UAPI_AWB_SPARE_FOR_BUBBLES \ + (IPU3_UAPI_MAX_BUBBLE_SIZE * IPU3_UAPI_MAX_STRIPES * \ + IPU3_UAPI_AWB_MD_ITEM_SIZE) +#define IPU3_UAPI_AWB_MAX_BUFFER_SIZE \ + (IPU3_UAPI_AWB_MAX_SETS * \ + (IPU3_UAPI_AWB_SET_SIZE + IPU3_UAPI_AWB_SPARE_FOR_BUBBLES)) + + +/** + * struct ipu3_uapi_awb_raw_buffer - AWB raw buffer + * + * @meta_data: buffer to hold auto white balance meta data which is + * the average values for each color channel. + */ +struct ipu3_uapi_awb_raw_buffer { + __u8 meta_data[IPU3_UAPI_AWB_MAX_BUFFER_SIZE] + __attribute__((aligned(32))); +} __packed; + +/** + * struct ipu3_uapi_awb_config_s - AWB config + * + * @rgbs_thr_gr: gr threshold value. + * @rgbs_thr_r: Red threshold value. + * @rgbs_thr_gb: gb threshold value. + * @rgbs_thr_b: Blue threshold value. + * @grid: &ipu3_uapi_grid_config, the default grid resolution is 16x16 cells. + * + * The threshold is a saturation measure range [0, 8191], 8191 is default. + * Values over threshold may be optionally rejected for averaging. + */ +struct ipu3_uapi_awb_config_s { + __u16 rgbs_thr_gr; + __u16 rgbs_thr_r; + __u16 rgbs_thr_gb; + __u16 rgbs_thr_b; + struct ipu3_uapi_grid_config grid; +} __attribute__((aligned(32))) __packed; + +/** + * struct ipu3_uapi_awb_config - AWB config wrapper + * + * @config: config for auto white balance as defined by &ipu3_uapi_awb_config_s + */ +struct ipu3_uapi_awb_config { + struct ipu3_uapi_awb_config_s config __attribute__((aligned(32))); +} __packed; + +#define IPU3_UAPI_AE_COLORS 4 /* R, G, B, Y */ +#define IPU3_UAPI_AE_BINS 256 +#define IPU3_UAPI_AE_WEIGHTS 96 + +/** + + * struct ipu3_uapi_ae_raw_buffer - AE global weighted histogram + + * + + * @vals: Sum of IPU3_UAPI_AE_COLORS in cell + + * + + * Each histogram contains IPU3_UAPI_AE_BINS bins. Each bin has 24 bit unsigned + + * for counting the number of the pixel. + + */ +struct ipu3_uapi_ae_raw_buffer { + __u32 vals[IPU3_UAPI_AE_BINS * IPU3_UAPI_AE_COLORS]; +} __packed; + +/** + * struct ipu3_uapi_ae_raw_buffer_aligned - AE raw buffer + * + * @buff: &ipu3_uapi_ae_raw_buffer to hold full frame meta data. + */ +struct ipu3_uapi_ae_raw_buffer_aligned { + struct ipu3_uapi_ae_raw_buffer buff __attribute__((aligned(32))); +} __packed; + +/** + * struct ipu3_uapi_ae_grid_config - AE weight grid + * + * @width: Grid horizontal dimensions. Value: [16, 32], default 16. + * @height: Grid vertical dimensions. Value: [16, 24], default 16. + * @block_width_log2: Log2 of the width of the grid cell, value: [3, 7]. + * @block_height_log2: Log2 of the height of the grid cell, value: [3, 7]. + * default is 3 (cell size 8x8), 4 cell per grid. + * @reserved0: reserved + * @ae_en: 0: does not write to &ipu3_uapi_ae_raw_buffer_aligned array, + * 1: write normally. + * @rst_hist_array: write 1 to trigger histogram array reset. + * @done_rst_hist_array: flag for histogram array reset done. + * @x_start: X value of top left corner of ROI, default 0. + * @y_start: Y value of top left corner of ROI, default 0. + * @x_end: X value of bottom right corner of ROI + * @y_end: Y value of bottom right corner of ROI + * + * The AE block accumulates 4 global weighted histograms(R, G, B, Y) over + * a defined ROI within the frame. The contribution of each pixel into the + * histogram, defined by &ipu3_uapi_ae_weight_elem LUT, is indexed by a grid. + */ +struct ipu3_uapi_ae_grid_config { + __u8 width; + __u8 height; + __u8 block_width_log2:4; + __u8 block_height_log2:4; + __u8 reserved0:5; + __u8 ae_en:1; + __u8 rst_hist_array:1; + __u8 done_rst_hist_array:1; + __u16 x_start; + __u16 y_start; + __u16 x_end; + __u16 y_end; +} __packed; + +/** + * struct ipu3_uapi_ae_weight_elem - AE weights LUT + * + * @cell0: weighted histogram grid value. + * @cell1: weighted histogram grid value. + * @cell2: weighted histogram grid value. + * @cell3: weighted histogram grid value. + * @cell4: weighted histogram grid value. + * @cell5: weighted histogram grid value. + * @cell6: weighted histogram grid value. + * @cell7: weighted histogram grid value. + * + * Use weighted grid value to give a different contribution factor to each cell. + * Precision u4, range [0, 15]. + */ +struct ipu3_uapi_ae_weight_elem { + __u32 cell0:4; + __u32 cell1:4; + __u32 cell2:4; + __u32 cell3:4; + __u32 cell4:4; + __u32 cell5:4; + __u32 cell6:4; + __u32 cell7:4; +} __packed; + +/** + * struct ipu3_uapi_ae_ccm - AE coefficients for WB and CCM + * + * @gain_gr: WB gain factor for the gr channels. Default 256. + * @gain_r: WB gain factor for the r channel. Default 256. + * @gain_b: WB gain factor for the b channel. Default 256. + * @gain_gb: WB gain factor for the gb channels. Default 256. + * @mat: 4x4 matrix that transforms Bayer quad output from WB to RGB+Y. + * + * Default: + * 128, 0, 0, 0, + * 0, 128, 0, 0, + * 0, 0, 128, 0, + * 0, 0, 0, 128, + * + * As part of the raw frame pre-process stage, the WB and color conversion need + * to be applied to expose the impact of these gain operations. + */ +struct ipu3_uapi_ae_ccm { + __u16 gain_gr; + __u16 gain_r; + __u16 gain_b; + __u16 gain_gb; + __s16 mat[16]; +} __packed; + +/** + * struct ipu3_uapi_ae_config - AE config + * + * @grid_cfg: config for auto exposure statistics grid. See struct + * &ipu3_uapi_ae_grid_config + * @weights: &IPU3_UAPI_AE_WEIGHTS is based on 32x24 blocks in the grid. + * Each grid cell has a corresponding value in weights LUT called + * grid value, global histogram is updated based on grid value and + * pixel value. + * @ae_ccm: Color convert matrix pre-processing block. + * + * Calculate AE grid from image resolution, resample ae weights. + */ +struct ipu3_uapi_ae_config { + struct ipu3_uapi_ae_grid_config grid_cfg __attribute__((aligned(32))); + struct ipu3_uapi_ae_weight_elem weights[ + IPU3_UAPI_AE_WEIGHTS] __attribute__((aligned(32))); + struct ipu3_uapi_ae_ccm ae_ccm __attribute__((aligned(32))); +} __packed; + +/** + * struct ipu3_uapi_af_filter_config - AF 2D filter for contrast measurements + * + * @y1_coeff_0: filter Y1, structure: 3x11, support both symmetry and + * anti-symmetry type. A12 is center, A1-A11 are neighbours. + * for analyzing low frequency content, used to calculate sum + * of gradients in x direction. + * @y1_coeff_0.a1: filter1 coefficients A1, u8, default 0. + * @y1_coeff_0.a2: filter1 coefficients A2, u8, default 0. + * @y1_coeff_0.a3: filter1 coefficients A3, u8, default 0. + * @y1_coeff_0.a4: filter1 coefficients A4, u8, default 0. + * @y1_coeff_1: Struct + * @y1_coeff_1.a5: filter1 coefficients A5, u8, default 0. + * @y1_coeff_1.a6: filter1 coefficients A6, u8, default 0. + * @y1_coeff_1.a7: filter1 coefficients A7, u8, default 0. + * @y1_coeff_1.a8: filter1 coefficients A8, u8, default 0. + * @y1_coeff_2: Struct + * @y1_coeff_2.a9: filter1 coefficients A9, u8, default 0. + * @y1_coeff_2.a10: filter1 coefficients A10, u8, default 0. + * @y1_coeff_2.a11: filter1 coefficients A11, u8, default 0. + * @y1_coeff_2.a12: filter1 coefficients A12, u8, default 128. + * @y1_sign_vec: Each bit corresponds to one coefficient sign bit, + * 0: positive, 1: negative, default 0. + * @y2_coeff_0: Y2, same structure as Y1. For analyzing high frequency content. + * @y2_coeff_0.a1: filter2 coefficients A1, u8, default 0. + * @y2_coeff_0.a2: filter2 coefficients A2, u8, default 0. + * @y2_coeff_0.a3: filter2 coefficients A3, u8, default 0. + * @y2_coeff_0.a4: filter2 coefficients A4, u8, default 0. + * @y2_coeff_1: Struct + * @y2_coeff_1.a5: filter2 coefficients A5, u8, default 0. + * @y2_coeff_1.a6: filter2 coefficients A6, u8, default 0. + * @y2_coeff_1.a7: filter2 coefficients A7, u8, default 0. + * @y2_coeff_1.a8: filter2 coefficients A8, u8, default 0. + * @y2_coeff_2: Struct + * @y2_coeff_2.a9: filter1 coefficients A9, u8, default 0. + * @y2_coeff_2.a10: filter1 coefficients A10, u8, default 0. + * @y2_coeff_2.a11: filter1 coefficients A11, u8, default 0. + * @y2_coeff_2.a12: filter1 coefficients A12, u8, default 128. + * @y2_sign_vec: Each bit corresponds to one coefficient sign bit, + * 0: positive, 1: negative, default 0. + * @y_calc: Pre-processing that converts Bayer quad to RGB+Y values to be + * used for building histogram. Range [0, 32], default 8. + * Rule: + * y_gen_rate_gr + y_gen_rate_r + y_gen_rate_b + y_gen_rate_gb = 32 + * A single Y is calculated based on sum of Gr/R/B/Gb based on + * their contribution ratio. + * @y_calc.y_gen_rate_gr: Contribution ratio Gr for Y + * @y_calc.y_gen_rate_r: Contribution ratio R for Y + * @y_calc.y_gen_rate_b: Contribution ratio B for Y + * @y_calc.y_gen_rate_gb: Contribution ratio Gb for Y + * @nf: The shift right value that should be applied during the Y1/Y2 filter to + * make sure the total memory needed is 2 bytes per grid cell. + * @nf.reserved0: reserved + * @nf.y1_nf: Normalization factor for the convolution coeffs of y1, + * should be log2 of the sum of the abs values of the filter + * coeffs, default 7 (2^7 = 128). + * @nf.reserved1: reserved + * @nf.y2_nf: Normalization factor for y2, should be log2 of the sum of the + * abs values of the filter coeffs. + * @nf.reserved2: reserved + */ +struct ipu3_uapi_af_filter_config { + struct { + __u8 a1; + __u8 a2; + __u8 a3; + __u8 a4; + } y1_coeff_0; + struct { + __u8 a5; + __u8 a6; + __u8 a7; + __u8 a8; + } y1_coeff_1; + struct { + __u8 a9; + __u8 a10; + __u8 a11; + __u8 a12; + } y1_coeff_2; + + __u32 y1_sign_vec; + + struct { + __u8 a1; + __u8 a2; + __u8 a3; + __u8 a4; + } y2_coeff_0; + struct { + __u8 a5; + __u8 a6; + __u8 a7; + __u8 a8; + } y2_coeff_1; + struct { + __u8 a9; + __u8 a10; + __u8 a11; + __u8 a12; + } y2_coeff_2; + + __u32 y2_sign_vec; + + struct { + __u8 y_gen_rate_gr; + __u8 y_gen_rate_r; + __u8 y_gen_rate_b; + __u8 y_gen_rate_gb; + } y_calc; + + struct { + __u32 reserved0:8; + __u32 y1_nf:4; + __u32 reserved1:4; + __u32 y2_nf:4; + __u32 reserved2:12; + } nf; +} __packed; + +#define IPU3_UAPI_AF_MAX_SETS 24 +#define IPU3_UAPI_AF_MD_ITEM_SIZE 4 +#define IPU3_UAPI_AF_SPARE_FOR_BUBBLES \ + (IPU3_UAPI_MAX_BUBBLE_SIZE * IPU3_UAPI_MAX_STRIPES * \ + IPU3_UAPI_AF_MD_ITEM_SIZE) +#define IPU3_UAPI_AF_Y_TABLE_SET_SIZE 128 +#define IPU3_UAPI_AF_Y_TABLE_MAX_SIZE \ + (IPU3_UAPI_AF_MAX_SETS * \ + (IPU3_UAPI_AF_Y_TABLE_SET_SIZE + IPU3_UAPI_AF_SPARE_FOR_BUBBLES) * \ + IPU3_UAPI_MAX_STRIPES) + +/** + * struct ipu3_uapi_af_raw_buffer - AF meta data + * + * @y_table: Each color component will be convolved separately with filter1 + * and filter2 and the result will be summed out and averaged for + * each cell. + */ +struct ipu3_uapi_af_raw_buffer { + __u8 y_table[IPU3_UAPI_AF_Y_TABLE_MAX_SIZE] __attribute__((aligned(32))); +} __packed; + +/** + * struct ipu3_uapi_af_config_s - AF config + * + * @filter_config: AF uses Y1 and Y2 filters as configured in + * &ipu3_uapi_af_filter_config + * @padding: paddings + * @grid_cfg: See &ipu3_uapi_grid_config, default resolution 16x16. Use large + * grid size for large image and vice versa. + */ +struct ipu3_uapi_af_config_s { + struct ipu3_uapi_af_filter_config filter_config __attribute__((aligned(32))); + __u8 padding[4]; + struct ipu3_uapi_grid_config grid_cfg __attribute__((aligned(32))); +} __packed; + +#define IPU3_UAPI_AWB_FR_MAX_SETS 24 +#define IPU3_UAPI_AWB_FR_MD_ITEM_SIZE 8 +#define IPU3_UAPI_AWB_FR_BAYER_TBL_SIZE 256 +#define IPU3_UAPI_AWB_FR_SPARE_FOR_BUBBLES \ + (IPU3_UAPI_MAX_BUBBLE_SIZE * IPU3_UAPI_MAX_STRIPES * \ + IPU3_UAPI_AWB_FR_MD_ITEM_SIZE) +#define IPU3_UAPI_AWB_FR_BAYER_TABLE_MAX_SIZE \ + (IPU3_UAPI_AWB_FR_MAX_SETS * \ + (IPU3_UAPI_AWB_FR_BAYER_TBL_SIZE + \ + IPU3_UAPI_AWB_FR_SPARE_FOR_BUBBLES) * IPU3_UAPI_MAX_STRIPES) + +/** + * struct ipu3_uapi_awb_fr_meta_data - AWB filter response meta data + * + * @meta_data: Statistics output on the grid after convolving with 1D filter. + */ +struct ipu3_uapi_awb_fr_raw_buffer { + __u8 meta_data[IPU3_UAPI_AWB_FR_BAYER_TABLE_MAX_SIZE] + __attribute__((aligned(32))); +} __packed; + +/** + * struct ipu3_uapi_awb_fr_config_s - AWB filter response config + * + * @grid_cfg: grid config, default 16x16. + * @bayer_coeff: 1D Filter 1x11 center symmetry/anti-symmetry. + * coeffcients defaults { 0, 0, 0, 0, 0, 128 }. + * Applied on whole image for each Bayer channel separately + * by a weighted sum of its 11x1 neighbors. + * @reserved1: reserved + * @bayer_sign: sign of filter coeffcients, default 0. + * @bayer_nf: normalization factor for the convolution coeffs, to make sure + * total memory needed is within pre-determined range. + * NF should be the log2 of the sum of the abs values of the + * filter coeffs, range [7, 14], default 7. + * @reserved2: reserved + */ +struct ipu3_uapi_awb_fr_config_s { + struct ipu3_uapi_grid_config grid_cfg; + __u8 bayer_coeff[6]; + __u16 reserved1; + __u32 bayer_sign; + __u8 bayer_nf; + __u8 reserved2[3]; +} __attribute__((aligned(32))) __packed; + +/** + * struct ipu3_uapi_4a_config - 4A config + * + * @awb_config: &ipu3_uapi_awb_config_s, default resolution 16x16 + * @ae_grd_config: auto exposure statistics &ipu3_uapi_ae_grid_config + * @padding: paddings + * @af_config: auto focus config &ipu3_uapi_af_config_s + * @awb_fr_config: &ipu3_uapi_awb_fr_config_s, default resolution 16x16 + */ +struct ipu3_uapi_4a_config { + struct ipu3_uapi_awb_config_s awb_config __attribute__((aligned(32))); + struct ipu3_uapi_ae_grid_config ae_grd_config; + __u8 padding[20]; + struct ipu3_uapi_af_config_s af_config; + struct ipu3_uapi_awb_fr_config_s awb_fr_config; +} __packed; + +/** + * struct ipu3_uapi_bubble_info - Bubble info for host side debugging + * + * @num_of_stripes: A single frame is divided into several parts called stripes + * due to limitation on line buffer memory. + * The separation between the stripes is vertical. Each such + * stripe is processed as a single frame by the ISP pipe. + * @padding: padding bytes. + * @num_sets: number of sets. + * @padding1: padding bytes. + * @size_of_set: set size. + * @padding2: padding bytes. + * @bubble_size: is the amount of padding in the bubble expressed in "sets". + * @padding3: padding bytes. + */ +struct ipu3_uapi_bubble_info { + __u32 num_of_stripes __attribute__((aligned(32))); + __u8 padding[28]; + __u32 num_sets; + __u8 padding1[28]; + __u32 size_of_set; + __u8 padding2[28]; + __u32 bubble_size; + __u8 padding3[28]; +} __packed; + +/* + * struct ipu3_uapi_stats_3a_bubble_info_per_stripe + */ +struct ipu3_uapi_stats_3a_bubble_info_per_stripe { + struct ipu3_uapi_bubble_info awb[IPU3_UAPI_MAX_STRIPES]; + struct ipu3_uapi_bubble_info af[IPU3_UAPI_MAX_STRIPES]; + struct ipu3_uapi_bubble_info awb_fr[IPU3_UAPI_MAX_STRIPES]; +} __packed; + +/** + * struct ipu3_uapi_ff_status - Enable bits for each 3A fixed function + * + * @awb_en: auto white balance enable + * @padding: padding config + * @ae_en: auto exposure enable + * @padding1: padding config + * @af_en: auto focus enable + * @padding2: padding config + * @awb_fr_en: awb filter response enable bit + * @padding3: padding config + */ +struct ipu3_uapi_ff_status { + __u32 awb_en __attribute__((aligned(32))); + __u8 padding[28]; + __u32 ae_en; + __u8 padding1[28]; + __u32 af_en; + __u8 padding2[28]; + __u32 awb_fr_en; + __u8 padding3[28]; +} __packed; + +/** + * struct ipu3_uapi_stats_3a - 3A statistics + * + * @awb_raw_buffer: auto white balance meta data &ipu3_uapi_awb_raw_buffer + * @ae_raw_buffer: auto exposure raw data &ipu3_uapi_ae_raw_buffer_aligned + * @af_raw_buffer: &ipu3_uapi_af_raw_buffer for auto focus meta data + * @awb_fr_raw_buffer: value as specified by &ipu3_uapi_awb_fr_raw_buffer + * @stats_4a_config: 4a statistics config as defined by &ipu3_uapi_4a_config. + * @ae_join_buffers: 1 to use ae_raw_buffer. + * @padding: padding config + * @stats_3a_bubble_per_stripe: a &ipu3_uapi_stats_3a_bubble_info_per_stripe + * @stats_3a_status: 3a statistics status set in &ipu3_uapi_ff_status + */ +struct ipu3_uapi_stats_3a { + struct ipu3_uapi_awb_raw_buffer awb_raw_buffer; + struct ipu3_uapi_ae_raw_buffer_aligned + ae_raw_buffer[IPU3_UAPI_MAX_STRIPES]; + struct ipu3_uapi_af_raw_buffer af_raw_buffer; + struct ipu3_uapi_awb_fr_raw_buffer awb_fr_raw_buffer; + struct ipu3_uapi_4a_config stats_4a_config; + __u32 ae_join_buffers; + __u8 padding[28]; + struct ipu3_uapi_stats_3a_bubble_info_per_stripe + stats_3a_bubble_per_stripe; + struct ipu3_uapi_ff_status stats_3a_status; +} __packed; + +/******************* ipu3_uapi_acc_param *******************/ + +#define IPU3_UAPI_ISP_VEC_ELEMS 64 +#define IPU3_UAPI_ISP_TNR3_VMEM_LEN 9 + +#define IPU3_UAPI_BNR_LUT_SIZE 32 + +/* number of elements in gamma correction LUT */ +#define IPU3_UAPI_GAMMA_CORR_LUT_ENTRIES 256 + +/* largest grid is 73x56, for grid_height_per_slice of 2, 73x2 = 146 */ +#define IPU3_UAPI_SHD_MAX_CELLS_PER_SET 146 +#define IPU3_UAPI_SHD_MAX_CFG_SETS 28 +/* Normalization shift aka nf */ +#define IPU3_UAPI_SHD_BLGR_NF_SHIFT 13 +#define IPU3_UAPI_SHD_BLGR_NF_MASK 7 + +#define IPU3_UAPI_YUVP2_TCC_MACC_TABLE_ELEMENTS 16 +#define IPU3_UAPI_YUVP2_TCC_INV_Y_LUT_ELEMENTS 14 +#define IPU3_UAPI_YUVP2_TCC_GAIN_PCWL_LUT_ELEMENTS 258 +#define IPU3_UAPI_YUVP2_TCC_R_SQR_LUT_ELEMENTS 24 + +#define IPU3_UAPI_ANR_LUT_SIZE 26 +#define IPU3_UAPI_ANR_PYRAMID_SIZE 22 + +#define IPU3_UAPI_LIN_LUT_SIZE 64 + +/* Bayer Noise Reduction related structs */ + +/** + * struct ipu3_uapi_bnr_static_config_wb_gains_config - White balance gains + * + * @gr: white balance gain for Gr channel. + * @r: white balance gain for R channel. + * @b: white balance gain for B channel. + * @gb: white balance gain for Gb channel. + * + * Precision u3.13, range [0, 8). White balance correction is done by applying + * a multiplicative gain to each color channels prior to BNR. + */ +struct ipu3_uapi_bnr_static_config_wb_gains_config { + __u16 gr; + __u16 r; + __u16 b; + __u16 gb; +} __packed; + +/** + * struct ipu3_uapi_bnr_static_config_wb_gains_thr_config - Threshold config + * + * @gr: white balance threshold gain for Gr channel. + * @r: white balance threshold gain for R channel. + * @b: white balance threshold gain for B channel. + * @gb: white balance threshold gain for Gb channel. + * + * Defines the threshold that specifies how different a defect pixel can be from + * its neighbors.(used by dynamic defect pixel correction sub block) + * Precision u4.4 range [0, 8]. + */ +struct ipu3_uapi_bnr_static_config_wb_gains_thr_config { + __u8 gr; + __u8 r; + __u8 b; + __u8 gb; +} __packed; + +/** + * struct ipu3_uapi_bnr_static_config_thr_coeffs_config - Noise model + * coefficients that controls noise threshold + * + * @cf: Free coefficient for threshold calculation, range [0, 8191], default 0. + * @reserved0: reserved + * @cg: Gain coefficient for threshold calculation, [0, 31], default 8. + * @ci: Intensity coefficient for threshold calculation. range [0, 0x1f] + * default 6. + * format: u3.2 (3 most significant bits represent whole number, + * 2 least significant bits represent the fractional part + * with each count representing 0.25) + * e.g. 6 in binary format is 00110, that translates to 1.5 + * @reserved1: reserved + * @r_nf: Normalization shift value for r^2 calculation, range [12, 20] + * where r is a radius of pixel [row, col] from centor of sensor. + * default 14. + * + * Threshold used to distinguish between noise and details. + */ +struct ipu3_uapi_bnr_static_config_thr_coeffs_config { + __u32 cf:13; + __u32 reserved0:3; + __u32 cg:5; + __u32 ci:5; + __u32 reserved1:1; + __u32 r_nf:5; +} __packed; + +/** + * struct ipu3_uapi_bnr_static_config_thr_ctrl_shd_config - Shading config + * + * @gr: Coefficient defines lens shading gain approximation for gr channel + * @r: Coefficient defines lens shading gain approximation for r channel + * @b: Coefficient defines lens shading gain approximation for b channel + * @gb: Coefficient defines lens shading gain approximation for gb channel + * + * Parameters for noise model (NM) adaptation of BNR due to shading correction. + * All above have precision of u3.3, default to 0. + */ +struct ipu3_uapi_bnr_static_config_thr_ctrl_shd_config { + __u8 gr; + __u8 r; + __u8 b; + __u8 gb; +} __packed; + +/** + * struct ipu3_uapi_bnr_static_config_opt_center_config - Optical center config + * + * @x_reset: Reset value of X (col start - X center). Precision s12.0. + * @reserved0: reserved + * @y_reset: Reset value of Y (row start - Y center). Precision s12.0. + * @reserved2: reserved + * + * Distance from corner to optical center for NM adaptation due to shading + * correction (should be calculated based on shading tables) + */ +struct ipu3_uapi_bnr_static_config_opt_center_config { + __s32 x_reset:13; + __u32 reserved0:3; + __s32 y_reset:13; + __u32 reserved2:3; +} __packed; + +/** + * struct ipu3_uapi_bnr_static_config_lut_config - BNR square root lookup table + * + * @values: pre-calculated values of square root function. + * + * LUT implementation of square root operation. + */ +struct ipu3_uapi_bnr_static_config_lut_config { + __u8 values[IPU3_UAPI_BNR_LUT_SIZE]; +} __packed; + +/** + * struct ipu3_uapi_bnr_static_config_bp_ctrl_config - Detect bad pixels (bp) + * + * @bp_thr_gain: Defines the threshold that specifies how different a + * defect pixel can be from its neighbors. Threshold is + * dependent on de-noise threshold calculated by algorithm. + * Range [4, 31], default 4. + * @reserved0: reserved + * @defect_mode: Mode of addressed defect pixels, + * 0 - single defect pixel is expected, + * 1 - 2 adjacent defect pixels are expected, default 1. + * @bp_gain: Defines how 2nd derivation that passes through a defect pixel + * is different from 2nd derivations that pass through + * neighbor pixels. u4.2, range [0, 256], default 8. + * @reserved1: reserved + * @w0_coeff: Blending coefficient of defect pixel correction. + * Precision u4, range [0, 8], default 8. + * @reserved2: reserved + * @w1_coeff: Enable influence of incorrect defect pixel correction to be + * avoided. Precision u4, range [1, 8], default 8. + * @reserved3: reserved + */ +struct ipu3_uapi_bnr_static_config_bp_ctrl_config { + __u32 bp_thr_gain:5; + __u32 reserved0:2; + __u32 defect_mode:1; + __u32 bp_gain:6; + __u32 reserved1:18; + __u32 w0_coeff:4; + __u32 reserved2:4; + __u32 w1_coeff:4; + __u32 reserved3:20; +} __packed; + +/** + * struct ipu3_uapi_bnr_static_config_dn_detect_ctrl_config - Denoising config + * + * @alpha: Weight of central element of smoothing filter. + * @beta: Weight of peripheral elements of smoothing filter, default 4. + * @gamma: Weight of diagonal elements of smoothing filter, default 4. + * + * beta and gamma parameter define the strength of the noise removal filter. + * All above has precision u0.4, range [0, 0xf] + * format: u0.4 (no / zero bits represent whole number, + * 4 bits represent the fractional part + * with each count representing 0.0625) + * e.g. 0xf translates to 0.0625x15 = 0.9375 + * + * @reserved0: reserved + * @max_inf: Maximum increase of peripheral or diagonal element influence + * relative to the pre-defined value range: [0x5, 0xa] + * @reserved1: reserved + * @gd_enable: Green disparity enable control, 0 - disable, 1 - enable. + * @bpc_enable: Bad pixel correction enable control, 0 - disable, 1 - enable. + * @bnr_enable: Bayer noise removal enable control, 0 - disable, 1 - enable. + * @ff_enable: Fixed function enable, 0 - disable, 1 - enable. + * @reserved2: reserved + */ +struct ipu3_uapi_bnr_static_config_dn_detect_ctrl_config { + __u32 alpha:4; + __u32 beta:4; + __u32 gamma:4; + __u32 reserved0:4; + __u32 max_inf:4; + __u32 reserved1:7; + __u32 gd_enable:1; + __u32 bpc_enable:1; + __u32 bnr_enable:1; + __u32 ff_enable:1; + __u32 reserved2:1; +} __packed; + +/** + * struct ipu3_uapi_bnr_static_config_opt_center_sqr_config - BNR optical square + * + * @x_sqr_reset: Reset value of X^2. + * @y_sqr_reset: Reset value of Y^2. + * + * Please note: + * + * #. X and Y ref to + * &ipu3_uapi_bnr_static_config_opt_center_config + * #. Both structs are used in threshold formula to calculate r^2, where r + * is a radius of pixel [row, col] from centor of sensor. + */ +struct ipu3_uapi_bnr_static_config_opt_center_sqr_config { + __u32 x_sqr_reset; + __u32 y_sqr_reset; +} __packed; + +/** + * struct ipu3_uapi_bnr_static_config - BNR static config + * + * @wb_gains: white balance gains &ipu3_uapi_bnr_static_config_wb_gains_config + * @wb_gains_thr: white balance gains threshold as defined by + * &ipu3_uapi_bnr_static_config_wb_gains_thr_config + * @thr_coeffs: coefficients of threshold + * &ipu3_uapi_bnr_static_config_thr_coeffs_config + * @thr_ctrl_shd: control of shading threshold + * &ipu3_uapi_bnr_static_config_thr_ctrl_shd_config + * @opt_center: optical center &ipu3_uapi_bnr_static_config_opt_center_config + * + * Above parameters and opt_center_sqr are used for white balance and shading. + * + * @lut: lookup table &ipu3_uapi_bnr_static_config_lut_config + * @bp_ctrl: detect and remove bad pixels as defined in struct + * &ipu3_uapi_bnr_static_config_bp_ctrl_config + * @dn_detect_ctrl: detect and remove noise. + * &ipu3_uapi_bnr_static_config_dn_detect_ctrl_config + * @column_size: The number of pixels in column. + * @opt_center_sqr: Reset value of r^2 to optical center, see + * &ipu3_uapi_bnr_static_config_opt_center_sqr_config. + */ +struct ipu3_uapi_bnr_static_config { + struct ipu3_uapi_bnr_static_config_wb_gains_config wb_gains; + struct ipu3_uapi_bnr_static_config_wb_gains_thr_config wb_gains_thr; + struct ipu3_uapi_bnr_static_config_thr_coeffs_config thr_coeffs; + struct ipu3_uapi_bnr_static_config_thr_ctrl_shd_config thr_ctrl_shd; + struct ipu3_uapi_bnr_static_config_opt_center_config opt_center; + struct ipu3_uapi_bnr_static_config_lut_config lut; + struct ipu3_uapi_bnr_static_config_bp_ctrl_config bp_ctrl; + struct ipu3_uapi_bnr_static_config_dn_detect_ctrl_config dn_detect_ctrl; + __u32 column_size; + struct ipu3_uapi_bnr_static_config_opt_center_sqr_config opt_center_sqr; +} __packed; + +/** + * struct ipu3_uapi_bnr_static_config_green_disparity - Correct green disparity + * + * @gd_red: Shading gain coeff for gr disparity level in bright red region. + * Precision u0.6, default 4(0.0625). + * @reserved0: reserved + * @gd_green: Shading gain coeff for gr disparity level in bright green + * region. Precision u0.6, default 4(0.0625). + * @reserved1: reserved + * @gd_blue: Shading gain coeff for gr disparity level in bright blue region. + * Precision u0.6, default 4(0.0625). + * @reserved2: reserved + * @gd_black: Maximal green disparity level in dark region (stronger disparity + * assumed to be image detail). Precision u14, default 80. + * @reserved3: reserved + * @gd_shading: Change maximal green disparity level according to square + * distance from image center. + * @reserved4: reserved + * @gd_support: Lower bound for the number of second green color pixels in + * current pixel neighborhood with less than threshold difference + * from it. + * + * The shading gain coeff of red, green, blue and black are used to calculate + * threshold given a pixel's color value and its coordinates in the image. + * + * @reserved5: reserved + * @gd_clip: Turn green disparity clip on/off, [0, 1], default 1. + * @gd_central_weight: Central pixel weight in 9 pixels weighted sum. + */ +struct ipu3_uapi_bnr_static_config_green_disparity { + __u32 gd_red:6; + __u32 reserved0:2; + __u32 gd_green:6; + __u32 reserved1:2; + __u32 gd_blue:6; + __u32 reserved2:10; + __u32 gd_black:14; + __u32 reserved3:2; + __u32 gd_shading:7; + __u32 reserved4:1; + __u32 gd_support:2; + __u32 reserved5:1; + __u32 gd_clip:1; + __u32 gd_central_weight:4; +} __packed; + +/** + * struct ipu3_uapi_dm_config - De-mosaic parameters + * + * @dm_en: de-mosaic enable. + * @ch_ar_en: Checker artifacts removal enable flag. Default 0. + * @fcc_en: False color correction (FCC) enable flag. Default 0. + * @reserved0: reserved + * @frame_width: do not care + * @gamma_sc: Sharpening coefficient (coefficient of 2-d derivation of + * complementary color in Hamilton-Adams interpolation). + * u5, range [0, 31], default 8. + * @reserved1: reserved + * @lc_ctrl: Parameter that controls weights of Chroma Homogeneity metric + * in calculation of final homogeneity metric. + * u5, range [0, 31], default 7. + * @reserved2: reserved + * @cr_param1: First parameter that defines Checker artifact removal + * feature gain. Precision u5, range [0, 31], default 8. + * @reserved3: reserved + * @cr_param2: Second parameter that defines Checker artifact removal + * feature gain. Precision u5, range [0, 31], default 8. + * @reserved4: reserved + * @coring_param: Defines power of false color correction operation. + * low for preserving edge colors, high for preserving gray + * edge artifacts. + * Precision u1.4, range [0, 1.9375], default 4 (0.25). + * @reserved5: reserved + * + * The demosaic fixed function block is responsible to covert Bayer(mosaiced) + * images into color images based on demosaicing algorithm. + */ +struct ipu3_uapi_dm_config { + __u32 dm_en:1; + __u32 ch_ar_en:1; + __u32 fcc_en:1; + __u32 reserved0:13; + __u32 frame_width:16; + + __u32 gamma_sc:5; + __u32 reserved1:3; + __u32 lc_ctrl:5; + __u32 reserved2:3; + __u32 cr_param1:5; + __u32 reserved3:3; + __u32 cr_param2:5; + __u32 reserved4:3; + + __u32 coring_param:5; + __u32 reserved5:27; +} __packed; + +/** + * struct ipu3_uapi_ccm_mat_config - Color correction matrix + * + * @coeff_m11: CCM 3x3 coefficient, range [-65536, 65535] + * @coeff_m12: CCM 3x3 coefficient, range [-8192, 8191] + * @coeff_m13: CCM 3x3 coefficient, range [-32768, 32767] + * @coeff_o_r: Bias 3x1 coefficient, range [-8191, 8181] + * @coeff_m21: CCM 3x3 coefficient, range [-32767, 32767] + * @coeff_m22: CCM 3x3 coefficient, range [-8192, 8191] + * @coeff_m23: CCM 3x3 coefficient, range [-32768, 32767] + * @coeff_o_g: Bias 3x1 coefficient, range [-8191, 8181] + * @coeff_m31: CCM 3x3 coefficient, range [-32768, 32767] + * @coeff_m32: CCM 3x3 coefficient, range [-8192, 8191] + * @coeff_m33: CCM 3x3 coefficient, range [-32768, 32767] + * @coeff_o_b: Bias 3x1 coefficient, range [-8191, 8181] + * + * Transform sensor specific color space to standard sRGB by applying 3x3 matrix + * and adding a bias vector O. The transformation is basically a rotation and + * translation in the 3-dimensional color spaces. Here are the defaults: + * + * 9775, -2671, 1087, 0 + * -1071, 8303, 815, 0 + * -23, -7887, 16103, 0 + */ +struct ipu3_uapi_ccm_mat_config { + __s16 coeff_m11; + __s16 coeff_m12; + __s16 coeff_m13; + __s16 coeff_o_r; + __s16 coeff_m21; + __s16 coeff_m22; + __s16 coeff_m23; + __s16 coeff_o_g; + __s16 coeff_m31; + __s16 coeff_m32; + __s16 coeff_m33; + __s16 coeff_o_b; +} __packed; + +/** + * struct ipu3_uapi_gamma_corr_ctrl - Gamma correction + * + * @enable: gamma correction enable. + * @reserved: reserved + */ +struct ipu3_uapi_gamma_corr_ctrl { + __u32 enable:1; + __u32 reserved:31; +} __packed; + +/** + * struct ipu3_uapi_gamma_corr_lut - Per-pixel tone mapping implemented as LUT. + * + * @lut: 256 tabulated values of the gamma function. LUT[1].. LUT[256] + * format u13.0, range [0, 8191]. + * + * The tone mapping operation is done by a Piece wise linear graph + * that is implemented as a lookup table(LUT). The pixel component input + * intensity is the X-axis of the graph which is the table entry. + */ +struct ipu3_uapi_gamma_corr_lut { + __u16 lut[IPU3_UAPI_GAMMA_CORR_LUT_ENTRIES]; +} __packed; + +/** + * struct ipu3_uapi_gamma_config - Gamma config + * + * @gc_ctrl: control of gamma correction &ipu3_uapi_gamma_corr_ctrl + * @gc_lut: lookup table of gamma correction &ipu3_uapi_gamma_corr_lut + */ +struct ipu3_uapi_gamma_config { + struct ipu3_uapi_gamma_corr_ctrl gc_ctrl __attribute__((aligned(32))); + struct ipu3_uapi_gamma_corr_lut gc_lut __attribute__((aligned(32))); +} __packed; + +/** + * struct ipu3_uapi_csc_mat_config - Color space conversion matrix config + * + * @coeff_c11: Conversion matrix value, format s0.14, range [-16384, 16383]. + * @coeff_c12: Conversion matrix value, format s0.14, range [-8192, 8191]. + * @coeff_c13: Conversion matrix value, format s0.14, range [-16384, 16383]. + * @coeff_b1: Bias 3x1 coefficient, s13.0 range [-8192, 8191]. + * @coeff_c21: Conversion matrix value, format s0.14, range [-16384, 16383]. + * @coeff_c22: Conversion matrix value, format s0.14, range [-8192, 8191]. + * @coeff_c23: Conversion matrix value, format s0.14, range [-16384, 16383]. + * @coeff_b2: Bias 3x1 coefficient, s13.0 range [-8192, 8191]. + * @coeff_c31: Conversion matrix value, format s0.14, range [-16384, 16383]. + * @coeff_c32: Conversion matrix value, format s0.14, range [-8192, 8191]. + * @coeff_c33: Conversion matrix value, format s0.14, range [-16384, 16383]. + * @coeff_b3: Bias 3x1 coefficient, s13.0 range [-8192, 8191]. + * + * To transform each pixel from RGB to YUV (Y - brightness/luminance, + * UV -chroma) by applying the pixel's values by a 3x3 matrix and adding an + * optional bias 3x1 vector. Here are the default values for the matrix: + * + * 4898, 9617, 1867, 0, + * -2410, -4732, 7143, 0, + * 10076, -8437, -1638, 0, + * + * (i.e. for real number 0.299, 0.299 * 2^14 becomes 4898.) + */ +struct ipu3_uapi_csc_mat_config { + __s16 coeff_c11; + __s16 coeff_c12; + __s16 coeff_c13; + __s16 coeff_b1; + __s16 coeff_c21; + __s16 coeff_c22; + __s16 coeff_c23; + __s16 coeff_b2; + __s16 coeff_c31; + __s16 coeff_c32; + __s16 coeff_c33; + __s16 coeff_b3; +} __packed; + +/** + * struct ipu3_uapi_cds_params - Chroma down-scaling + * + * @ds_c00: range [0, 3] + * @ds_c01: range [0, 3] + * @ds_c02: range [0, 3] + * @ds_c03: range [0, 3] + * @ds_c10: range [0, 3] + * @ds_c11: range [0, 3] + * @ds_c12: range [0, 3] + * @ds_c13: range [0, 3] + * + * In case user does not provide, above 4x2 filter will use following defaults: + * 1, 3, 3, 1, + * 1, 3, 3, 1, + * + * @ds_nf: Normalization factor for Chroma output downscaling filter, + * range 0,4, default 2. + * @reserved0: reserved + * @csc_en: Color space conversion enable + * @uv_bin_output: 0: output YUV 4.2.0, 1: output YUV 4.2.2(default). + * @reserved1: reserved + */ +struct ipu3_uapi_cds_params { + __u32 ds_c00:2; + __u32 ds_c01:2; + __u32 ds_c02:2; + __u32 ds_c03:2; + __u32 ds_c10:2; + __u32 ds_c11:2; + __u32 ds_c12:2; + __u32 ds_c13:2; + __u32 ds_nf:5; + __u32 reserved0:3; + __u32 csc_en:1; + __u32 uv_bin_output:1; + __u32 reserved1:6; +} __packed; + +/** + * struct ipu3_uapi_shd_grid_config - Bayer shading(darkening) correction + * + * @width: Grid horizontal dimensions, u8, [8, 128], default 73 + * @height: Grid vertical dimensions, u8, [8, 128], default 56 + * @block_width_log2: Log2 of the width of the grid cell in pixel count + * u4, [0, 15], default value 5. + * @reserved0: reserved + * @block_height_log2: Log2 of the height of the grid cell in pixel count + * u4, [0, 15], default value 6. + * @reserved1: reserved + * @grid_height_per_slice: SHD_MAX_CELLS_PER_SET/width. + * (with SHD_MAX_CELLS_PER_SET = 146). + * @x_start: X value of top left corner of sensor relative to ROI + * s13, [-4096, 0], default 0, only negative values. + * @y_start: Y value of top left corner of sensor relative to ROI + * s13, [-4096, 0], default 0, only negative values. + */ +struct ipu3_uapi_shd_grid_config { + /* reg 0 */ + __u8 width; + __u8 height; + __u8 block_width_log2:3; + __u8 reserved0:1; + __u8 block_height_log2:3; + __u8 reserved1:1; + __u8 grid_height_per_slice; + /* reg 1 */ + __s16 x_start; + __s16 y_start; +} __packed; + +/** + * struct ipu3_uapi_shd_general_config - Shading general config + * + * @init_set_vrt_offst_ul: set vertical offset, + * y_start >> block_height_log2 % grid_height_per_slice. + * @shd_enable: shading enable. + * @gain_factor: Gain factor. Shift calculated anti shading value. Precision u2. + * 0x0 - gain factor [1, 5], means no shift interpolated value. + * 0x1 - gain factor [1, 9], means shift interpolated by 1. + * 0x2 - gain factor [1, 17], means shift interpolated by 2. + * @reserved: reserved + * + * Correction is performed by multiplying a gain factor for each of the 4 Bayer + * channels as a function of the pixel location in the sensor. + */ +struct ipu3_uapi_shd_general_config { + __u32 init_set_vrt_offst_ul:8; + __u32 shd_enable:1; + __u32 gain_factor:2; + __u32 reserved:21; +} __packed; + +/** + * struct ipu3_uapi_shd_black_level_config - Black level correction + * + * @bl_r: Bios values for green red. s11 range [-2048, 2047]. + * @bl_gr: Bios values for green blue. s11 range [-2048, 2047]. + * @bl_gb: Bios values for red. s11 range [-2048, 2047]. + * @bl_b: Bios values for blue. s11 range [-2048, 2047]. + */ +struct ipu3_uapi_shd_black_level_config { + __s16 bl_r; + __s16 bl_gr; + __s16 bl_gb; + __s16 bl_b; +} __packed; + +/** + * struct ipu3_uapi_shd_config_static - Shading config static + * + * @grid: shading grid config &ipu3_uapi_shd_grid_config + * @general: shading general config &ipu3_uapi_shd_general_config + * @black_level: black level config for shading correction as defined by + * &ipu3_uapi_shd_black_level_config + */ +struct ipu3_uapi_shd_config_static { + struct ipu3_uapi_shd_grid_config grid; + struct ipu3_uapi_shd_general_config general; + struct ipu3_uapi_shd_black_level_config black_level; +} __packed; + +/** + * struct ipu3_uapi_shd_lut - Shading gain factor lookup table. + * + * @sets: array + * @sets.r_and_gr: Red and GreenR Lookup table. + * @sets.r_and_gr.r: Red shading factor. + * @sets.r_and_gr.gr: GreenR shading factor. + * @sets.reserved1: reserved + * @sets.gb_and_b: GreenB and Blue Lookup table. + * @sets.gb_and_b.gb: GreenB shading factor. + * @sets.gb_and_b.b: Blue shading factor. + * @sets.reserved2: reserved + * + * Map to shading correction LUT register set. + */ +struct ipu3_uapi_shd_lut { + struct { + struct { + __u16 r; + __u16 gr; + } r_and_gr[IPU3_UAPI_SHD_MAX_CELLS_PER_SET]; + __u8 reserved1[24]; + struct { + __u16 gb; + __u16 b; + } gb_and_b[IPU3_UAPI_SHD_MAX_CELLS_PER_SET]; + __u8 reserved2[24]; + } sets[IPU3_UAPI_SHD_MAX_CFG_SETS]; +} __packed; + +/** + * struct ipu3_uapi_shd_config - Shading config + * + * @shd: shading static config, see &ipu3_uapi_shd_config_static + * @shd_lut: shading lookup table &ipu3_uapi_shd_lut + */ +struct ipu3_uapi_shd_config { + struct ipu3_uapi_shd_config_static shd __attribute__((aligned(32))); + struct ipu3_uapi_shd_lut shd_lut __attribute__((aligned(32))); +} __packed; + +/* Image Enhancement Filter directed */ + +/** + * struct ipu3_uapi_iefd_cux2 - IEFd Config Unit 2 parameters + * + * @x0: X0 point of Config Unit, u9.0, default 0. + * @x1: X1 point of Config Unit, u9.0, default 0. + * @a01: Slope A of Config Unit, s4.4, default 0. + * @b01: Slope B, always 0. + * + * Calculate weight for blending directed and non-directed denoise elements + * + * Note: + * Each instance of Config Unit needs X coordinate of n points and + * slope A factor between points calculated by driver based on calibration + * parameters. + * + * All CU inputs are unsigned, they will be converted to signed when written + * to register, i.e. a01 will be written to 9 bit register in s4.4 format. + * This applies to &ipu3_uapi_iefd_cux6_ed, &ipu3_uapi_iefd_cux2_1, + * &ipu3_uapi_iefd_cux2_1, &ipu3_uapi_iefd_cux4 and &ipu3_uapi_iefd_cux6_rad. + */ +struct ipu3_uapi_iefd_cux2 { + __u32 x0:9; + __u32 x1:9; + __u32 a01:9; + __u32 b01:5; +} __packed; + +/** + * struct ipu3_uapi_iefd_cux6_ed - Calculate power of non-directed sharpening + * element, Config Unit 6 for edge detail (ED). + * + * @x0: X coordinate of point 0, u9.0, default 0. + * @x1: X coordinate of point 1, u9.0, default 0. + * @x2: X coordinate of point 2, u9.0, default 0. + * @reserved0: reserved + * @x3: X coordinate of point 3, u9.0, default 0. + * @x4: X coordinate of point 4, u9.0, default 0. + * @x5: X coordinate of point 5, u9.0, default 0. + * @reserved1: reserved + * @a01: slope A points 01, s4.4, default 0. + * @a12: slope A points 12, s4.4, default 0. + * @a23: slope A points 23, s4.4, default 0. + * @reserved2: reserved + * @a34: slope A points 34, s4.4, default 0. + * @a45: slope A points 45, s4.4, default 0. + * @reserved3: reserved + * @b01: slope B points 01, s4.4, default 0. + * @b12: slope B points 12, s4.4, default 0. + * @b23: slope B points 23, s4.4, default 0. + * @reserved4: reserved + * @b34: slope B points 34, s4.4, default 0. + * @b45: slope B points 45, s4.4, default 0. + * @reserved5: reserved. + */ +struct ipu3_uapi_iefd_cux6_ed { + __u32 x0:9; + __u32 x1:9; + __u32 x2:9; + __u32 reserved0:5; + + __u32 x3:9; + __u32 x4:9; + __u32 x5:9; + __u32 reserved1:5; + + __u32 a01:9; + __u32 a12:9; + __u32 a23:9; + __u32 reserved2:5; + + __u32 a34:9; + __u32 a45:9; + __u32 reserved3:14; + + __u32 b01:9; + __u32 b12:9; + __u32 b23:9; + __u32 reserved4:5; + + __u32 b34:9; + __u32 b45:9; + __u32 reserved5:14; +} __packed; + +/** + * struct ipu3_uapi_iefd_cux2_1 - Calculate power of non-directed denoise + * element apply. + * @x0: X0 point of Config Unit, u9.0, default 0. + * @x1: X1 point of Config Unit, u9.0, default 0. + * @a01: Slope A of Config Unit, s4.4, default 0. + * @reserved1: reserved + * @b01: offset B0 of Config Unit, u7.0, default 0. + * @reserved2: reserved + */ +struct ipu3_uapi_iefd_cux2_1 { + __u32 x0:9; + __u32 x1:9; + __u32 a01:9; + __u32 reserved1:5; + + __u32 b01:8; + __u32 reserved2:24; +} __packed; + +/** + * struct ipu3_uapi_iefd_cux4 - Calculate power of non-directed sharpening + * element. + * + * @x0: X0 point of Config Unit, u9.0, default 0. + * @x1: X1 point of Config Unit, u9.0, default 0. + * @x2: X2 point of Config Unit, u9.0, default 0. + * @reserved0: reserved + * @x3: X3 point of Config Unit, u9.0, default 0. + * @a01: Slope A0 of Config Unit, s4.4, default 0. + * @a12: Slope A1 of Config Unit, s4.4, default 0. + * @reserved1: reserved + * @a23: Slope A2 of Config Unit, s4.4, default 0. + * @b01: Offset B0 of Config Unit, s7.0, default 0. + * @b12: Offset B1 of Config Unit, s7.0, default 0. + * @reserved2: reserved + * @b23: Offset B2 of Config Unit, s7.0, default 0. + * @reserved3: reserved + */ +struct ipu3_uapi_iefd_cux4 { + __u32 x0:9; + __u32 x1:9; + __u32 x2:9; + __u32 reserved0:5; + + __u32 x3:9; + __u32 a01:9; + __u32 a12:9; + __u32 reserved1:5; + + __u32 a23:9; + __u32 b01:8; + __u32 b12:8; + __u32 reserved2:7; + + __u32 b23:8; + __u32 reserved3:24; +} __packed; + +/** + * struct ipu3_uapi_iefd_cux6_rad - Radial Config Unit (CU) + * + * @x0: x0 points of Config Unit radial, u8.0 + * @x1: x1 points of Config Unit radial, u8.0 + * @x2: x2 points of Config Unit radial, u8.0 + * @x3: x3 points of Config Unit radial, u8.0 + * @x4: x4 points of Config Unit radial, u8.0 + * @x5: x5 points of Config Unit radial, u8.0 + * @reserved1: reserved + * @a01: Slope A of Config Unit radial, s7.8 + * @a12: Slope A of Config Unit radial, s7.8 + * @a23: Slope A of Config Unit radial, s7.8 + * @a34: Slope A of Config Unit radial, s7.8 + * @a45: Slope A of Config Unit radial, s7.8 + * @reserved2: reserved + * @b01: Slope B of Config Unit radial, s9.0 + * @b12: Slope B of Config Unit radial, s9.0 + * @b23: Slope B of Config Unit radial, s9.0 + * @reserved4: reserved + * @b34: Slope B of Config Unit radial, s9.0 + * @b45: Slope B of Config Unit radial, s9.0 + * @reserved5: reserved + */ +struct ipu3_uapi_iefd_cux6_rad { + __u32 x0:8; + __u32 x1:8; + __u32 x2:8; + __u32 x3:8; + + __u32 x4:8; + __u32 x5:8; + __u32 reserved1:16; + + __u32 a01:16; + __u32 a12:16; + + __u32 a23:16; + __u32 a34:16; + + __u32 a45:16; + __u32 reserved2:16; + + __u32 b01:10; + __u32 b12:10; + __u32 b23:10; + __u32 reserved4:2; + + __u32 b34:10; + __u32 b45:10; + __u32 reserved5:12; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_iefd_cfg_units - IEFd Config Units parameters + * + * @cu_1: calculate weight for blending directed and + * non-directed denoise elements. See &ipu3_uapi_iefd_cux2 + * @cu_ed: calculate power of non-directed sharpening element, see + * &ipu3_uapi_iefd_cux6_ed + * @cu_3: calculate weight for blending directed and + * non-directed denoise elements. A &ipu3_uapi_iefd_cux2 + * @cu_5: calculate power of non-directed denoise element apply, use + * &ipu3_uapi_iefd_cux2_1 + * @cu_6: calculate power of non-directed sharpening element. See + * &ipu3_uapi_iefd_cux4 + * @cu_7: calculate weight for blending directed and + * non-directed denoise elements. Use &ipu3_uapi_iefd_cux2 + * @cu_unsharp: Config Unit of unsharp &ipu3_uapi_iefd_cux4 + * @cu_radial: Config Unit of radial &ipu3_uapi_iefd_cux6_rad + * @cu_vssnlm: Config Unit of vssnlm &ipu3_uapi_iefd_cux2 + */ +struct ipu3_uapi_yuvp1_iefd_cfg_units { + struct ipu3_uapi_iefd_cux2 cu_1; + struct ipu3_uapi_iefd_cux6_ed cu_ed; + struct ipu3_uapi_iefd_cux2 cu_3; + struct ipu3_uapi_iefd_cux2_1 cu_5; + struct ipu3_uapi_iefd_cux4 cu_6; + struct ipu3_uapi_iefd_cux2 cu_7; + struct ipu3_uapi_iefd_cux4 cu_unsharp; + struct ipu3_uapi_iefd_cux6_rad cu_radial; + struct ipu3_uapi_iefd_cux2 cu_vssnlm; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_iefd_config_s - IEFd config + * + * @horver_diag_coeff: Gradient compensation. Compared with vertical / + * horizontal (0 / 90 degree), coefficient of diagonal (45 / + * 135 degree) direction should be corrected by approx. + * 1/sqrt(2). + * @reserved0: reserved + * @clamp_stitch: Slope to stitch between clamped and unclamped edge values + * @reserved1: reserved + * @direct_metric_update: Update coeff for direction metric + * @reserved2: reserved + * @ed_horver_diag_coeff: Radial Coefficient that compensates for + * different distance for vertical/horizontal and + * diagonal gradient calculation (approx. 1/sqrt(2)) + * @reserved3: reserved + */ +struct ipu3_uapi_yuvp1_iefd_config_s { + __u32 horver_diag_coeff:7; + __u32 reserved0:1; + __u32 clamp_stitch:6; + __u32 reserved1:2; + __u32 direct_metric_update:5; + __u32 reserved2:3; + __u32 ed_horver_diag_coeff:7; + __u32 reserved3:1; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_iefd_control - IEFd control + * + * @iefd_en: Enable IEFd + * @denoise_en: Enable denoise + * @direct_smooth_en: Enable directional smooth + * @rad_en: Enable radial update + * @vssnlm_en: Enable VSSNLM output filter + * @reserved: reserved + */ +struct ipu3_uapi_yuvp1_iefd_control { + __u32 iefd_en:1; + __u32 denoise_en:1; + __u32 direct_smooth_en:1; + __u32 rad_en:1; + __u32 vssnlm_en:1; + __u32 reserved:27; +} __packed; + +/** + * struct ipu3_uapi_sharp_cfg - Sharpening config + * + * @nega_lmt_txt: Sharpening limit for negative overshoots for texture. + * @reserved0: reserved + * @posi_lmt_txt: Sharpening limit for positive overshoots for texture. + * @reserved1: reserved + * @nega_lmt_dir: Sharpening limit for negative overshoots for direction (edge). + * @reserved2: reserved + * @posi_lmt_dir: Sharpening limit for positive overshoots for direction (edge). + * @reserved3: reserved + * + * Fixed point type u13.0, range [0, 8191]. + */ +struct ipu3_uapi_sharp_cfg { + __u32 nega_lmt_txt:13; + __u32 reserved0:19; + __u32 posi_lmt_txt:13; + __u32 reserved1:19; + __u32 nega_lmt_dir:13; + __u32 reserved2:19; + __u32 posi_lmt_dir:13; + __u32 reserved3:19; +} __packed; + +/** + * struct struct ipu3_uapi_far_w - Sharpening config for far sub-group + * + * @dir_shrp: Weight of wide direct sharpening, u1.6, range [0, 64], default 64. + * @reserved0: reserved + * @dir_dns: Weight of wide direct denoising, u1.6, range [0, 64], default 0. + * @reserved1: reserved + * @ndir_dns_powr: Power of non-direct denoising, + * Precision u1.6, range [0, 64], default 64. + * @reserved2: reserved + */ +struct ipu3_uapi_far_w { + __u32 dir_shrp:7; + __u32 reserved0:1; + __u32 dir_dns:7; + __u32 reserved1:1; + __u32 ndir_dns_powr:7; + __u32 reserved2:9; +} __packed; + +/** + * struct struct ipu3_uapi_unsharp_cfg - Unsharp config + * + * @unsharp_weight: Unsharp mask blending weight. + * u1.6, range [0, 64], default 16. + * 0 - disabled, 64 - use only unsharp. + * @reserved0: reserved + * @unsharp_amount: Unsharp mask amount, u4.5, range [0, 511], default 0. + * @reserved1: reserved + */ +struct ipu3_uapi_unsharp_cfg { + __u32 unsharp_weight:7; + __u32 reserved0:1; + __u32 unsharp_amount:9; + __u32 reserved1:15; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_iefd_shrp_cfg - IEFd sharpness config + * + * @cfg: sharpness config &ipu3_uapi_sharp_cfg + * @far_w: wide range config, value as specified by &ipu3_uapi_far_w: + * The 5x5 environment is separated into 2 sub-groups, the 3x3 nearest + * neighbors (8 pixels called Near), and the second order neighborhood + * around them (16 pixels called Far). + * @unshrp_cfg: unsharpness config. &ipu3_uapi_unsharp_cfg + */ +struct ipu3_uapi_yuvp1_iefd_shrp_cfg { + struct ipu3_uapi_sharp_cfg cfg; + struct ipu3_uapi_far_w far_w; + struct ipu3_uapi_unsharp_cfg unshrp_cfg; +} __packed; + +/** + * struct ipu3_uapi_unsharp_coef0 - Unsharp mask coefficients + * + * @c00: Coeff11, s0.8, range [-255, 255], default 1. + * @c01: Coeff12, s0.8, range [-255, 255], default 5. + * @c02: Coeff13, s0.8, range [-255, 255], default 9. + * @reserved: reserved + * + * Configurable registers for common sharpening support. + */ +struct ipu3_uapi_unsharp_coef0 { + __u32 c00:9; + __u32 c01:9; + __u32 c02:9; + __u32 reserved:5; +} __packed; + +/** + * struct ipu3_uapi_unsharp_coef1 - Unsharp mask coefficients + * + * @c11: Coeff22, s0.8, range [-255, 255], default 29. + * @c12: Coeff23, s0.8, range [-255, 255], default 55. + * @c22: Coeff33, s0.8, range [-255, 255], default 96. + * @reserved: reserved + */ +struct ipu3_uapi_unsharp_coef1 { + __u32 c11:9; + __u32 c12:9; + __u32 c22:9; + __u32 reserved:5; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_iefd_unshrp_cfg - Unsharp mask config + * + * @unsharp_coef0: unsharp coefficient 0 config. See &ipu3_uapi_unsharp_coef0 + * @unsharp_coef1: unsharp coefficient 1 config. See &ipu3_uapi_unsharp_coef1 + */ +struct ipu3_uapi_yuvp1_iefd_unshrp_cfg { + struct ipu3_uapi_unsharp_coef0 unsharp_coef0; + struct ipu3_uapi_unsharp_coef1 unsharp_coef1; +} __packed; + +/** + * struct ipu3_uapi_radial_reset_xy - Radial coordinate reset + * + * @x: Radial reset of x coordinate. Precision s12, [-4095, 4095], default 0. + * @reserved0: reserved + * @y: Radial center y coordinate. Precision s12, [-4095, 4095], default 0. + * @reserved1: reserved + */ +struct ipu3_uapi_radial_reset_xy { + __s32 x:13; + __u32 reserved0:3; + __s32 y:13; + __u32 reserved1:3; +} __packed; + +/** + * struct ipu3_uapi_radial_reset_x2 - Radial X^2 reset + * + * @x2: Radial reset of x^2 coordinate. Precision u24, default 0. + * @reserved: reserved + */ +struct ipu3_uapi_radial_reset_x2 { + __u32 x2:24; + __u32 reserved:8; +} __packed; + +/** + * struct ipu3_uapi_radial_reset_y2 - Radial Y^2 reset + * + * @y2: Radial reset of y^2 coordinate. Precision u24, default 0. + * @reserved: reserved + */ +struct ipu3_uapi_radial_reset_y2 { + __u32 y2:24; + __u32 reserved:8; +} __packed; + +/** + * struct ipu3_uapi_radial_cfg - Radial config + * + * @rad_nf: Radial. R^2 normalization factor is scale down by 2^ - (15 + scale) + * @reserved0: reserved + * @rad_inv_r2: Radial R^-2 normelized to (0.5..1). + * Precision u7, range [0, 127]. + * @reserved1: reserved + */ +struct ipu3_uapi_radial_cfg { + __u32 rad_nf:4; + __u32 reserved0:4; + __u32 rad_inv_r2:7; + __u32 reserved1:17; +} __packed; + +/** + * struct ipu3_uapi_rad_far_w - Radial FAR sub-group + * + * @rad_dir_far_sharp_w: Weight of wide direct sharpening, u1.6, range [0, 64], + * default 64. + * @rad_dir_far_dns_w: Weight of wide direct denoising, u1.6, range [0, 64], + * default 0. + * @rad_ndir_far_dns_power: power of non-direct sharpening, u1.6, range [0, 64], + * default 0. + * @reserved: reserved + */ +struct ipu3_uapi_rad_far_w { + __u32 rad_dir_far_sharp_w:8; + __u32 rad_dir_far_dns_w:8; + __u32 rad_ndir_far_dns_power:8; + __u32 reserved:8; +} __packed; + +/** + * struct ipu3_uapi_cu_cfg0 - Radius Config Unit cfg0 register + * + * @cu6_pow: Power of CU6. Power of non-direct sharpening, u3.4. + * @reserved0: reserved + * @cu_unsharp_pow: Power of unsharp mask, u2.4. + * @reserved1: reserved + * @rad_cu6_pow: Radial/corner CU6. Directed sharpening power, u3.4. + * @reserved2: reserved + * @rad_cu_unsharp_pow: Radial power of unsharp mask, u2.4. + * @reserved3: reserved + */ +struct ipu3_uapi_cu_cfg0 { + __u32 cu6_pow:7; + __u32 reserved0:1; + __u32 cu_unsharp_pow:7; + __u32 reserved1:1; + __u32 rad_cu6_pow:7; + __u32 reserved2:1; + __u32 rad_cu_unsharp_pow:6; + __u32 reserved3:2; +} __packed; + +/** + * struct ipu3_uapi_cu_cfg1 - Radius Config Unit cfg1 register + * + * @rad_cu6_x1: X1 point of Config Unit 6, precision u9.0. + * @reserved0: reserved + * @rad_cu_unsharp_x1: X1 point for Config Unit unsharp for radial/corner point + * precision u9.0. + * @reserved1: reserved + */ +struct ipu3_uapi_cu_cfg1 { + __u32 rad_cu6_x1:9; + __u32 reserved0:1; + __u32 rad_cu_unsharp_x1:9; + __u32 reserved1:13; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_iefd_rad_cfg - IEFd parameters changed radially over + * the picture plane. + * + * @reset_xy: reset xy value in radial calculation. &ipu3_uapi_radial_reset_xy + * @reset_x2: reset x square value in radial calculation. See struct + * &ipu3_uapi_radial_reset_x2 + * @reset_y2: reset y square value in radial calculation. See struct + * &ipu3_uapi_radial_reset_y2 + * @cfg: radial config defined in &ipu3_uapi_radial_cfg + * @rad_far_w: weight for wide range radial. &ipu3_uapi_rad_far_w + * @cu_cfg0: configuration unit 0. See &ipu3_uapi_cu_cfg0 + * @cu_cfg1: configuration unit 1. See &ipu3_uapi_cu_cfg1 + */ +struct ipu3_uapi_yuvp1_iefd_rad_cfg { + struct ipu3_uapi_radial_reset_xy reset_xy; + struct ipu3_uapi_radial_reset_x2 reset_x2; + struct ipu3_uapi_radial_reset_y2 reset_y2; + struct ipu3_uapi_radial_cfg cfg; + struct ipu3_uapi_rad_far_w rad_far_w; + struct ipu3_uapi_cu_cfg0 cu_cfg0; + struct ipu3_uapi_cu_cfg1 cu_cfg1; +} __packed; + +/* Vssnlm - Very small scale non-local mean algorithm */ + +/** + * struct ipu3_uapi_vss_lut_x - Vssnlm LUT x0/x1/x2 + * + * @vs_x0: Vssnlm LUT x0, precision u8, range [0, 255], default 16. + * @vs_x1: Vssnlm LUT x1, precision u8, range [0, 255], default 32. + * @vs_x2: Vssnlm LUT x2, precision u8, range [0, 255], default 64. + * @reserved2: reserved + */ +struct ipu3_uapi_vss_lut_x { + __u32 vs_x0:8; + __u32 vs_x1:8; + __u32 vs_x2:8; + __u32 reserved2:8; +} __packed; + +/** + * struct ipu3_uapi_vss_lut_y - Vssnlm LUT y0/y1/y2 + * + * @vs_y1: Vssnlm LUT y1, precision u4, range [0, 8], default 1. + * @reserved0: reserved + * @vs_y2: Vssnlm LUT y2, precision u4, range [0, 8], default 3. + * @reserved1: reserved + * @vs_y3: Vssnlm LUT y3, precision u4, range [0, 8], default 8. + * @reserved2: reserved + */ +struct ipu3_uapi_vss_lut_y { + __u32 vs_y1:4; + __u32 reserved0:4; + __u32 vs_y2:4; + __u32 reserved1:4; + __u32 vs_y3:4; + __u32 reserved2:12; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_iefd_vssnlm_cf - IEFd Vssnlm Lookup table + * + * @vss_lut_x: vss lookup table. See &ipu3_uapi_vss_lut_x description + * @vss_lut_y: vss lookup table. See &ipu3_uapi_vss_lut_y description + */ +struct ipu3_uapi_yuvp1_iefd_vssnlm_cfg { + struct ipu3_uapi_vss_lut_x vss_lut_x; + struct ipu3_uapi_vss_lut_y vss_lut_y; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_iefd_config - IEFd config + * + * @units: configuration unit setting, &ipu3_uapi_yuvp1_iefd_cfg_units + * @config: configuration, as defined by &ipu3_uapi_yuvp1_iefd_config_s + * @control: control setting, as defined by &ipu3_uapi_yuvp1_iefd_control + * @sharp: sharpness setting, as defined by &ipu3_uapi_yuvp1_iefd_shrp_cfg + * @unsharp: unsharpness setting, as defined by &ipu3_uapi_yuvp1_iefd_unshrp_cfg + * @rad: radial setting, as defined by &ipu3_uapi_yuvp1_iefd_rad_cfg + * @vsslnm: vsslnm setting, as defined by &ipu3_uapi_yuvp1_iefd_vssnlm_cfg + */ +struct ipu3_uapi_yuvp1_iefd_config { + struct ipu3_uapi_yuvp1_iefd_cfg_units units; + struct ipu3_uapi_yuvp1_iefd_config_s config; + struct ipu3_uapi_yuvp1_iefd_control control; + struct ipu3_uapi_yuvp1_iefd_shrp_cfg sharp; + struct ipu3_uapi_yuvp1_iefd_unshrp_cfg unsharp; + struct ipu3_uapi_yuvp1_iefd_rad_cfg rad; + struct ipu3_uapi_yuvp1_iefd_vssnlm_cfg vsslnm; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_yds_config - Y Down-Sampling config + * + * @c00: range [0, 3], default 0x0 + * @c01: range [0, 3], default 0x1 + * @c02: range [0, 3], default 0x1 + * @c03: range [0, 3], default 0x0 + * @c10: range [0, 3], default 0x0 + * @c11: range [0, 3], default 0x1 + * @c12: range [0, 3], default 0x1 + * @c13: range [0, 3], default 0x0 + * + * Above are 4x2 filter coefficients for chroma output downscaling. + * + * @norm_factor: Normalization factor, range [0, 4], default 2 + * 0 - divide by 1 + * 1 - divide by 2 + * 2 - divide by 4 + * 3 - divide by 8 + * 4 - divide by 16 + * @reserved0: reserved + * @bin_output: Down sampling on Luma channel in two optional modes + * 0 - Bin output 4.2.0 (default), 1 output 4.2.2. + * @reserved1: reserved + */ +struct ipu3_uapi_yuvp1_yds_config { + __u32 c00:2; + __u32 c01:2; + __u32 c02:2; + __u32 c03:2; + __u32 c10:2; + __u32 c11:2; + __u32 c12:2; + __u32 c13:2; + __u32 norm_factor:5; + __u32 reserved0:4; + __u32 bin_output:1; + __u32 reserved1:6; +} __packed; + +/* Chroma Noise Reduction */ + +/** + * struct ipu3_uapi_yuvp1_chnr_enable_config - Chroma noise reduction enable + * + * @enable: enable/disable chroma noise reduction + * @yuv_mode: 0 - YUV420, 1 - YUV422 + * @reserved0: reserved + * @col_size: number of columns in the frame, max width is 2560 + * @reserved1: reserved + */ +struct ipu3_uapi_yuvp1_chnr_enable_config { + __u32 enable:1; + __u32 yuv_mode:1; + __u32 reserved0:14; + __u32 col_size:12; + __u32 reserved1:4; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_chnr_coring_config - Coring thresholds for UV + * + * @u: U coring level, u0.13, range [0.0, 1.0], default 0.0 + * @reserved0: reserved + * @v: V coring level, u0.13, range [0.0, 1.0], default 0.0 + * @reserved1: reserved + */ +struct ipu3_uapi_yuvp1_chnr_coring_config { + __u32 u:13; + __u32 reserved0:3; + __u32 v:13; + __u32 reserved1:3; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_chnr_sense_gain_config - Chroma noise reduction gains + * + * All sensitivity gain parameters have precision u13.0, range [0, 8191]. + * + * @vy: Sensitivity of horizontal edge of Y, default 100 + * @vu: Sensitivity of horizontal edge of U, default 100 + * @vv: Sensitivity of horizontal edge of V, default 100 + * @reserved0: reserved + * @hy: Sensitivity of vertical edge of Y, default 50 + * @hu: Sensitivity of vertical edge of U, default 50 + * @hv: Sensitivity of vertical edge of V, default 50 + * @reserved1: reserved + */ +struct ipu3_uapi_yuvp1_chnr_sense_gain_config { + __u32 vy:8; + __u32 vu:8; + __u32 vv:8; + __u32 reserved0:8; + + __u32 hy:8; + __u32 hu:8; + __u32 hv:8; + __u32 reserved1:8; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_chnr_iir_fir_config - Chroma IIR/FIR filter config + * + * @fir_0h: Value of center tap in horizontal FIR, range [0, 32], default 8. + * @reserved0: reserved + * @fir_1h: Value of distance 1 in horizontal FIR, range [0, 32], default 12. + * @reserved1: reserved + * @fir_2h: Value of distance 2 tap in horizontal FIR, range [0, 32], default 0. + * @dalpha_clip_val: weight for previous row in IIR, range [1, 256], default 0. + * @reserved2: reserved + */ +struct ipu3_uapi_yuvp1_chnr_iir_fir_config { + __u32 fir_0h:6; + __u32 reserved0:2; + __u32 fir_1h:6; + __u32 reserved1:2; + __u32 fir_2h:6; + __u32 dalpha_clip_val:9; + __u32 reserved2:1; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_chnr_config - Chroma noise reduction config + * + * @enable: chroma noise reduction enable, see + * &ipu3_uapi_yuvp1_chnr_enable_config + * @coring: coring config for chroma noise reduction, see + * &ipu3_uapi_yuvp1_chnr_coring_config + * @sense_gain: sensitivity config for chroma noise reduction, see + * ipu3_uapi_yuvp1_chnr_sense_gain_config + * @iir_fir: iir and fir config for chroma noise reduction, see + * ipu3_uapi_yuvp1_chnr_iir_fir_config + */ +struct ipu3_uapi_yuvp1_chnr_config { + struct ipu3_uapi_yuvp1_chnr_enable_config enable; + struct ipu3_uapi_yuvp1_chnr_coring_config coring; + struct ipu3_uapi_yuvp1_chnr_sense_gain_config sense_gain; + struct ipu3_uapi_yuvp1_chnr_iir_fir_config iir_fir; +} __packed; + +/* Edge Enhancement and Noise Reduction */ + +/** + * struct ipu3_uapi_yuvp1_y_ee_nr_lpf_config - Luma(Y) edge enhancement low-pass + * filter coefficients + * + * @a_diag: Smoothing diagonal coefficient, u5.0. + * @reserved0: reserved + * @a_periph: Image smoothing perpherial, u5.0. + * @reserved1: reserved + * @a_cent: Image Smoothing center coefficient, u5.0. + * @reserved2: reserved + * @enable: 0: Y_EE_NR disabled, output = input; 1: Y_EE_NR enabled. + */ +struct ipu3_uapi_yuvp1_y_ee_nr_lpf_config { + __u32 a_diag:5; + __u32 reserved0:3; + __u32 a_periph:5; + __u32 reserved1:3; + __u32 a_cent:5; + __u32 reserved2:9; + __u32 enable:1; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_y_ee_nr_sense_config - Luma(Y) edge enhancement + * noise reduction sensitivity gains + * + * @edge_sense_0: Sensitivity of edge in dark area. u13.0, default 8191. + * @reserved0: reserved + * @delta_edge_sense: Difference in the sensitivity of edges between + * the bright and dark areas. u13.0, default 0. + * @reserved1: reserved + * @corner_sense_0: Sensitivity of corner in dark area. u13.0, default 0. + * @reserved2: reserved + * @delta_corner_sense: Difference in the sensitivity of corners between + * the bright and dark areas. u13.0, default 8191. + * @reserved3: reserved + */ +struct ipu3_uapi_yuvp1_y_ee_nr_sense_config { + __u32 edge_sense_0:13; + __u32 reserved0:3; + __u32 delta_edge_sense:13; + __u32 reserved1:3; + __u32 corner_sense_0:13; + __u32 reserved2:3; + __u32 delta_corner_sense:13; + __u32 reserved3:3; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_y_ee_nr_gain_config - Luma(Y) edge enhancement + * noise reduction gain config + * + * @gain_pos_0: Gain for positive edge in dark area. u5.0, [0, 16], default 2. + * @reserved0: reserved + * @delta_gain_posi: Difference in the gain of edges between the bright and + * dark areas for positive edges. u5.0, [0, 16], default 0. + * @reserved1: reserved + * @gain_neg_0: Gain for negative edge in dark area. u5.0, [0, 16], default 8. + * @reserved2: reserved + * @delta_gain_neg: Difference in the gain of edges between the bright and + * dark areas for negative edges. u5.0, [0, 16], default 0. + * @reserved3: reserved + */ +struct ipu3_uapi_yuvp1_y_ee_nr_gain_config { + __u32 gain_pos_0:5; + __u32 reserved0:3; + __u32 delta_gain_posi:5; + __u32 reserved1:3; + __u32 gain_neg_0:5; + __u32 reserved2:3; + __u32 delta_gain_neg:5; + __u32 reserved3:3; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_y_ee_nr_clip_config - Luma(Y) edge enhancement + * noise reduction clipping config + * + * @clip_pos_0: Limit of positive edge in dark area + * u5, value [0, 16], default 8. + * @reserved0: reserved + * @delta_clip_posi: Difference in the limit of edges between the bright + * and dark areas for positive edges. + * u5, value [0, 16], default 8. + * @reserved1: reserved + * @clip_neg_0: Limit of negative edge in dark area + * u5, value [0, 16], default 8. + * @reserved2: reserved + * @delta_clip_neg: Difference in the limit of edges between the bright + * and dark areas for negative edges. + * u5, value [0, 16], default 8. + * @reserved3: reserved + */ +struct ipu3_uapi_yuvp1_y_ee_nr_clip_config { + __u32 clip_pos_0:5; + __u32 reserved0:3; + __u32 delta_clip_posi:5; + __u32 reserved1:3; + __u32 clip_neg_0:5; + __u32 reserved2:3; + __u32 delta_clip_neg:5; + __u32 reserved3:3; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_y_ee_nr_frng_config - Luma(Y) edge enhancement + * noise reduction fringe config + * + * @gain_exp: Common exponent of gains, u4, [0, 8], default 2. + * @reserved0: reserved + * @min_edge: Threshold for edge and smooth stitching, u13. + * @reserved1: reserved + * @lin_seg_param: Power of LinSeg, u4. + * @reserved2: reserved + * @t1: Parameter for enabling/disabling the edge enhancement, u1.0, [0, 1], + * default 1. + * @t2: Parameter for enabling/disabling the smoothing, u1.0, [0, 1], + * default 1. + * @reserved3: reserved + */ +struct ipu3_uapi_yuvp1_y_ee_nr_frng_config { + __u32 gain_exp:4; + __u32 reserved0:28; + __u32 min_edge:13; + __u32 reserved1:3; + __u32 lin_seg_param:4; + __u32 reserved2:4; + __u32 t1:1; + __u32 t2:1; + __u32 reserved3:6; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_y_ee_nr_diag_config - Luma(Y) edge enhancement + * noise reduction diagonal config + * + * @diag_disc_g: Coefficient that prioritize diagonal edge direction on + * horizontal or vertical for final enhancement. + * u4.0, [1, 15], default 1. + * @reserved0: reserved + * @hvw_hor: Weight of horizontal/vertical edge enhancement for hv edge. + * u2.2, [1, 15], default 4. + * @dw_hor: Weight of diagonal edge enhancement for hv edge. + * u2.2, [1, 15], default 1. + * @hvw_diag: Weight of horizontal/vertical edge enhancement for diagonal edge. + * u2.2, [1, 15], default 1. + * @dw_diag: Weight of diagonal edge enhancement for diagonal edge. + * u2.2, [1, 15], default 4. + * @reserved1: reserved + */ +struct ipu3_uapi_yuvp1_y_ee_nr_diag_config { + __u32 diag_disc_g:4; + __u32 reserved0:4; + __u32 hvw_hor:4; + __u32 dw_hor:4; + __u32 hvw_diag:4; + __u32 dw_diag:4; + __u32 reserved1:8; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_y_ee_nr_fc_coring_config - Luma(Y) edge enhancement + * noise reduction false color correction (FCC) coring config + * + * @pos_0: Gain for positive edge in dark, u13.0, [0, 16], default 0. + * @reserved0: reserved + * @pos_delta: Gain for positive edge in bright, value: pos_0 + pos_delta <=16 + * u13.0, default 0. + * @reserved1: reserved + * @neg_0: Gain for negative edge in dark area, u13.0, range [0, 16], default 0. + * @reserved2: reserved + * @neg_delta: Gain for negative edge in bright area. neg_0 + neg_delta <=16 + * u13.0, default 0. + * @reserved3: reserved + * + * Coring is a simple soft thresholding technique. + */ +struct ipu3_uapi_yuvp1_y_ee_nr_fc_coring_config { + __u32 pos_0:13; + __u32 reserved0:3; + __u32 pos_delta:13; + __u32 reserved1:3; + __u32 neg_0:13; + __u32 reserved2:3; + __u32 neg_delta:13; + __u32 reserved3:3; +} __packed; + +/** + * struct ipu3_uapi_yuvp1_y_ee_nr_config - Edge enhancement and noise reduction + * + * @lpf: low-pass filter config. See &ipu3_uapi_yuvp1_y_ee_nr_lpf_config + * @sense: sensitivity config. See &ipu3_uapi_yuvp1_y_ee_nr_sense_config + * @gain: gain config as defined in &ipu3_uapi_yuvp1_y_ee_nr_gain_config + * @clip: clip config as defined in &ipu3_uapi_yuvp1_y_ee_nr_clip_config + * @frng: fringe config as defined in &ipu3_uapi_yuvp1_y_ee_nr_frng_config + * @diag: diagonal edge config. See &ipu3_uapi_yuvp1_y_ee_nr_diag_config + * @fc_coring: coring config for fringe control. See + * &ipu3_uapi_yuvp1_y_ee_nr_fc_coring_config + */ +struct ipu3_uapi_yuvp1_y_ee_nr_config { + struct ipu3_uapi_yuvp1_y_ee_nr_lpf_config lpf; + struct ipu3_uapi_yuvp1_y_ee_nr_sense_config sense; + struct ipu3_uapi_yuvp1_y_ee_nr_gain_config gain; + struct ipu3_uapi_yuvp1_y_ee_nr_clip_config clip; + struct ipu3_uapi_yuvp1_y_ee_nr_frng_config frng; + struct ipu3_uapi_yuvp1_y_ee_nr_diag_config diag; + struct ipu3_uapi_yuvp1_y_ee_nr_fc_coring_config fc_coring; +} __packed; + +/* Total Color Correction */ + +/** + * struct ipu3_uapi_yuvp2_tcc_gen_control_static_config - Total color correction + * general control config + * + * @en: 0 - TCC disabled. Output = input 1 - TCC enabled. + * @blend_shift: blend shift, Range[3, 4], default NA. + * @gain_according_to_y_only: 0: Gain is calculated according to YUV, + * 1: Gain is calculated according to Y only + * @reserved0: reserved + * @gamma: Final blending coefficients. Values[-16, 16], default NA. + * @reserved1: reserved + * @delta: Final blending coefficients. Values[-16, 16], default NA. + * @reserved2: reserved + */ +struct ipu3_uapi_yuvp2_tcc_gen_control_static_config { + __u32 en:1; + __u32 blend_shift:3; + __u32 gain_according_to_y_only:1; + __u32 reserved0:11; + __s32 gamma:5; + __u32 reserved1:3; + __s32 delta:5; + __u32 reserved2:3; +} __packed; + +/** + * struct ipu3_uapi_yuvp2_tcc_macc_elem_static_config - Total color correction + * multi-axis color control (MACC) config + * + * @a: a coefficient for 2x2 MACC conversion matrix. + * @reserved0: reserved + * @b: b coefficient 2x2 MACC conversion matrix. + * @reserved1: reserved + * @c: c coefficient for 2x2 MACC conversion matrix. + * @reserved2: reserved + * @d: d coefficient for 2x2 MACC conversion matrix. + * @reserved3: reserved + */ +struct ipu3_uapi_yuvp2_tcc_macc_elem_static_config { + __s32 a:12; + __u32 reserved0:4; + __s32 b:12; + __u32 reserved1:4; + __s32 c:12; + __u32 reserved2:4; + __s32 d:12; + __u32 reserved3:4; +} __packed; + +/** + * struct ipu3_uapi_yuvp2_tcc_macc_table_static_config - Total color correction + * multi-axis color control (MACC) table array + * + * @entries: config for multi axis color correction, as specified by + * &ipu3_uapi_yuvp2_tcc_macc_elem_static_config + */ +struct ipu3_uapi_yuvp2_tcc_macc_table_static_config { + struct ipu3_uapi_yuvp2_tcc_macc_elem_static_config + entries[IPU3_UAPI_YUVP2_TCC_MACC_TABLE_ELEMENTS]; +} __packed; + +/** + * struct ipu3_uapi_yuvp2_tcc_inv_y_lut_static_config - Total color correction + * inverse y lookup table + * + * @entries: lookup table for inverse y estimation, and use it to estimate the + * ratio between luma and chroma. Chroma by approximate the absolute + * value of the radius on the chroma plane (R = sqrt(u^2+v^2) ) and + * luma by approximate by 1/Y. + */ +struct ipu3_uapi_yuvp2_tcc_inv_y_lut_static_config { + __u16 entries[IPU3_UAPI_YUVP2_TCC_INV_Y_LUT_ELEMENTS]; +} __packed; + +/** + * struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config - Total color + * correction lookup table for PCWL + * + * @entries: lookup table for gain piece wise linear transformation (PCWL) + */ +struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config { + __u16 entries[IPU3_UAPI_YUVP2_TCC_GAIN_PCWL_LUT_ELEMENTS]; +} __packed; + +/** + * struct ipu3_uapi_yuvp2_tcc_r_sqr_lut_static_config - Total color correction + * lookup table for r square root + * + * @entries: lookup table for r square root estimation + */ +struct ipu3_uapi_yuvp2_tcc_r_sqr_lut_static_config { + __s16 entries[IPU3_UAPI_YUVP2_TCC_R_SQR_LUT_ELEMENTS]; +} __packed; + +/** + * struct ipu3_uapi_yuvp2_tcc_static_config- Total color correction static + * + * @gen_control: general config for Total Color Correction + * @macc_table: config for multi axis color correction + * @inv_y_lut: lookup table for inverse y estimation + * @gain_pcwl: lookup table for gain PCWL + * @r_sqr_lut: lookup table for r square root estimation. + */ +struct ipu3_uapi_yuvp2_tcc_static_config { + struct ipu3_uapi_yuvp2_tcc_gen_control_static_config gen_control; + struct ipu3_uapi_yuvp2_tcc_macc_table_static_config macc_table; + struct ipu3_uapi_yuvp2_tcc_inv_y_lut_static_config inv_y_lut; + struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config gain_pcwl; + struct ipu3_uapi_yuvp2_tcc_r_sqr_lut_static_config r_sqr_lut; +} __packed; + +/* Advanced Noise Reduction related structs */ + +/* + * struct ipu3_uapi_anr_alpha - Advanced noise reduction alpha + * + * Tunable parameters that are subject to modification according to the + * total gain used. + */ +struct ipu3_uapi_anr_alpha { + __u16 gr; + __u16 r; + __u16 b; + __u16 gb; + __u16 dc_gr; + __u16 dc_r; + __u16 dc_b; + __u16 dc_gb; +} __packed; + +/* + * struct ipu3_uapi_anr_beta - Advanced noise reduction beta + * + * Tunable parameters that are subject to modification according to the + * total gain used. + */ +struct ipu3_uapi_anr_beta { + __u16 beta_gr; + __u16 beta_r; + __u16 beta_b; + __u16 beta_gb; +} __packed; + +/* + * struct ipu3_uapi_anr_plane_color - Advanced noise reduction per plane R, Gr, + * Gb and B register settings + * + * Tunable parameters that are subject to modification according to the + * total gain used. + */ +struct ipu3_uapi_anr_plane_color { + __u16 reg_w_gr[16]; + __u16 reg_w_r[16]; + __u16 reg_w_b[16]; + __u16 reg_w_gb[16]; +} __packed; + +/** + * struct ipu3_uapi_anr_transform_config - Advanced noise reduction transform + * + * @enable: advanced noise reduction enabled. + * @adaptive_treshhold_en: On IPU3, adaptive threshold is always enabled. + * @reserved1: reserved + * @reserved2: reserved + * @alpha: using following defaults: + * 13, 13, 13, 13, 0, 0, 0, 0 + * 11, 11, 11, 11, 0, 0, 0, 0 + * 14, 14, 14, 14, 0, 0, 0, 0 + * @beta: use following defaults: + * 24, 24, 24, 24 + * 21, 20, 20, 21 + * 25, 25, 25, 25 + * @color: use defaults defined in driver/media/pci/intel/ipu3-tables.c + * @sqrt_lut: 11 bits per element, values = + * [724 768 810 849 887 + * 923 958 991 1024 1056 + * 1116 1145 1173 1201 1086 + * 1228 1254 1280 1305 1330 + * 1355 1379 1402 1425 1448] + * @xreset: Reset value of X for r^2 calculation Value: col_start-X_center + * Constraint: Xreset + FrameWdith=4095 Xreset= -4095, default -1632. + * @reserved3: reserved + * @yreset: Reset value of Y for r^2 calculation Value: row_start-Y_center + * Constraint: Yreset + FrameHeight=4095 Yreset= -4095, default -1224. + * @reserved4: reserved + * @x_sqr_reset: Reset value of X^2 for r^2 calculation Value = (Xreset)^2 + * @r_normfactor: Normalization factor for R. Default 14. + * @reserved5: reserved + * @y_sqr_reset: Reset value of Y^2 for r^2 calculation Value = (Yreset)^2 + * @gain_scale: Parameter describing shading gain as a function of distance + * from the image center. + * A single value per frame, loaded by the driver. Default 115. + */ +struct ipu3_uapi_anr_transform_config { + __u32 enable:1; /* 0 or 1, disabled or enabled */ + __u32 adaptive_treshhold_en:1; /* On IPU3, always enabled */ + + __u32 reserved1:30; + __u8 reserved2[44]; + + struct ipu3_uapi_anr_alpha alpha[3]; + struct ipu3_uapi_anr_beta beta[3]; + struct ipu3_uapi_anr_plane_color color[3]; + + __u16 sqrt_lut[IPU3_UAPI_ANR_LUT_SIZE]; /* 11 bits per element */ + + __s16 xreset:13; + __u16 reserved3:3; + __s16 yreset:13; + __u16 reserved4:3; + + __u32 x_sqr_reset:24; + __u32 r_normfactor:5; + __u32 reserved5:3; + + __u32 y_sqr_reset:24; + __u32 gain_scale:8; +} __packed; + +/** + * struct ipu3_uapi_anr_stitch_pyramid - ANR stitch pyramid + * + * @entry0: pyramid LUT entry0, range [0x0, 0x3f] + * @entry1: pyramid LUT entry1, range [0x0, 0x3f] + * @entry2: pyramid LUT entry2, range [0x0, 0x3f] + * @reserved: reserved + */ +struct ipu3_uapi_anr_stitch_pyramid { + __u32 entry0:6; + __u32 entry1:6; + __u32 entry2:6; + __u32 reserved:14; +} __packed; + +/** + * struct ipu3_uapi_anr_stitch_config - ANR stitch config + * + * @anr_stitch_en: enable stitch. Enabled with 1. + * @reserved: reserved + * @pyramid: pyramid table as defined by &ipu3_uapi_anr_stitch_pyramid + * default values: + * { 1, 3, 5 }, { 7, 7, 5 }, { 3, 1, 3 }, + * { 9, 15, 21 }, { 21, 15, 9 }, { 3, 5, 15 }, + * { 25, 35, 35 }, { 25, 15, 5 }, { 7, 21, 35 }, + * { 49, 49, 35 }, { 21, 7, 7 }, { 21, 35, 49 }, + * { 49, 35, 21 }, { 7, 5, 15 }, { 25, 35, 35 }, + * { 25, 15, 5 }, { 3, 9, 15 }, { 21, 21, 15 }, + * { 9, 3, 1 }, { 3, 5, 7 }, { 7, 5, 3}, { 1 } + */ +struct ipu3_uapi_anr_stitch_config { + __u32 anr_stitch_en; + __u8 reserved[44]; + struct ipu3_uapi_anr_stitch_pyramid pyramid[IPU3_UAPI_ANR_PYRAMID_SIZE]; +} __packed; + +/** + * struct ipu3_uapi_anr_config - ANR config + * + * @transform: advanced noise reduction transform config as specified by + * &ipu3_uapi_anr_transform_config + * @stitch: create 4x4 patch from 4 surrounding 8x8 patches. + */ +struct ipu3_uapi_anr_config { + struct ipu3_uapi_anr_transform_config transform __attribute__((aligned(32))); + struct ipu3_uapi_anr_stitch_config stitch __attribute__((aligned(32))); +} __packed; + +/** + * struct ipu3_uapi_acc_param - Accelerator cluster parameters + * + * ACC refers to the HW cluster containing all Fixed Functions (FFs). Each FF + * implements a specific algorithm. + * + * @bnr: parameters for bayer noise reduction static config. See + * &ipu3_uapi_bnr_static_config + * @green_disparity: disparity static config between gr and gb channel. + * See &ipu3_uapi_bnr_static_config_green_disparity + * @dm: de-mosaic config. See &ipu3_uapi_dm_config + * @ccm: color correction matrix. See &ipu3_uapi_ccm_mat_config + * @gamma: gamma correction config. See &ipu3_uapi_gamma_config + * @csc: color space conversion matrix. See &ipu3_uapi_csc_mat_config + * @cds: color down sample config. See &ipu3_uapi_cds_params + * @shd: lens shading correction config. See &ipu3_uapi_shd_config + * @iefd: Image enhancement filter and denoise config. + * &ipu3_uapi_yuvp1_iefd_config + * @yds_c0: y down scaler config. &ipu3_uapi_yuvp1_yds_config + * @chnr_c0: chroma noise reduction config. &ipu3_uapi_yuvp1_chnr_config + * @y_ee_nr: y edge enhancement and noise reduction config. + * &ipu3_uapi_yuvp1_y_ee_nr_config + * @yds: y down scaler config. See &ipu3_uapi_yuvp1_yds_config + * @chnr: chroma noise reduction config. See &ipu3_uapi_yuvp1_chnr_config + * @reserved1: reserved + * @yds2: y channel down scaler config. See &ipu3_uapi_yuvp1_yds_config + * @tcc: total color correction config as defined in struct + * &ipu3_uapi_yuvp2_tcc_static_config + * @reserved2: reserved + * @anr: advanced noise reduction config.See &ipu3_uapi_anr_config + * @awb_fr: AWB filter response config. See ipu3_uapi_awb_fr_config + * @ae: auto exposure config As specified by &ipu3_uapi_ae_config + * @af: auto focus config. As specified by &ipu3_uapi_af_config + * @awb: auto white balance config. As specified by &ipu3_uapi_awb_config + */ +struct ipu3_uapi_acc_param { + struct ipu3_uapi_bnr_static_config bnr; + struct ipu3_uapi_bnr_static_config_green_disparity + green_disparity __attribute__((aligned(32))); + struct ipu3_uapi_dm_config dm __attribute__((aligned(32))); + struct ipu3_uapi_ccm_mat_config ccm __attribute__((aligned(32))); + struct ipu3_uapi_gamma_config gamma __attribute__((aligned(32))); + struct ipu3_uapi_csc_mat_config csc __attribute__((aligned(32))); + struct ipu3_uapi_cds_params cds __attribute__((aligned(32))); + struct ipu3_uapi_shd_config shd __attribute__((aligned(32))); + struct ipu3_uapi_yuvp1_iefd_config iefd __attribute__((aligned(32))); + struct ipu3_uapi_yuvp1_yds_config yds_c0 __attribute__((aligned(32))); + struct ipu3_uapi_yuvp1_chnr_config chnr_c0 __attribute__((aligned(32))); + struct ipu3_uapi_yuvp1_y_ee_nr_config y_ee_nr __attribute__((aligned(32))); + struct ipu3_uapi_yuvp1_yds_config yds __attribute__((aligned(32))); + struct ipu3_uapi_yuvp1_chnr_config chnr __attribute__((aligned(32))); + struct ipu3_uapi_yuvp1_yds_config yds2 __attribute__((aligned(32))); + struct ipu3_uapi_yuvp2_tcc_static_config tcc __attribute__((aligned(32))); + struct ipu3_uapi_anr_config anr; + struct ipu3_uapi_awb_fr_config_s awb_fr; + struct ipu3_uapi_ae_config ae; + struct ipu3_uapi_af_config_s af; + struct ipu3_uapi_awb_config awb; +} __packed; + +/** + * struct ipu3_uapi_isp_lin_vmem_params - Linearization parameters + * + * @lin_lutlow_gr: linearization look-up table for GR channel interpolation. + * @lin_lutlow_r: linearization look-up table for R channel interpolation. + * @lin_lutlow_b: linearization look-up table for B channel interpolation. + * @lin_lutlow_gb: linearization look-up table for GB channel interpolation. + * lin_lutlow_gr / lin_lutlow_r / lin_lutlow_b / + * lin_lutlow_gb <= LIN_MAX_VALUE - 1. + * @lin_lutdif_gr: lin_lutlow_gr[i+1] - lin_lutlow_gr[i]. + * @lin_lutdif_r: lin_lutlow_r[i+1] - lin_lutlow_r[i]. + * @lin_lutdif_b: lin_lutlow_b[i+1] - lin_lutlow_b[i]. + * @lin_lutdif_gb: lin_lutlow_gb[i+1] - lin_lutlow_gb[i]. + */ +struct ipu3_uapi_isp_lin_vmem_params { + __s16 lin_lutlow_gr[IPU3_UAPI_LIN_LUT_SIZE]; + __s16 lin_lutlow_r[IPU3_UAPI_LIN_LUT_SIZE]; + __s16 lin_lutlow_b[IPU3_UAPI_LIN_LUT_SIZE]; + __s16 lin_lutlow_gb[IPU3_UAPI_LIN_LUT_SIZE]; + __s16 lin_lutdif_gr[IPU3_UAPI_LIN_LUT_SIZE]; + __s16 lin_lutdif_r[IPU3_UAPI_LIN_LUT_SIZE]; + __s16 lin_lutdif_b[IPU3_UAPI_LIN_LUT_SIZE]; + __s16 lin_lutdif_gb[IPU3_UAPI_LIN_LUT_SIZE]; +} __packed; + +/* Temporal Noise Reduction */ + +/** + * struct ipu3_uapi_isp_tnr3_vmem_params - Temporal noise reduction vector + * memory parameters + * + * @slope: slope setting in interpolation curve for temporal noise reduction. + * @reserved1: reserved + * @sigma: knee point setting in interpolation curve for temporal + * noise reduction. + * @reserved2: reserved + */ +struct ipu3_uapi_isp_tnr3_vmem_params { + __u16 slope[IPU3_UAPI_ISP_TNR3_VMEM_LEN]; + __u16 reserved1[IPU3_UAPI_ISP_VEC_ELEMS + - IPU3_UAPI_ISP_TNR3_VMEM_LEN]; + __u16 sigma[IPU3_UAPI_ISP_TNR3_VMEM_LEN]; + __u16 reserved2[IPU3_UAPI_ISP_VEC_ELEMS + - IPU3_UAPI_ISP_TNR3_VMEM_LEN]; +} __packed; + +/** + * struct ipu3_uapi_isp_tnr3_params - Temporal noise reduction v3 parameters + * + * @knee_y1: Knee point TNR3 assumes standard deviation of Y,U and + * V at Y1 are TnrY1_Sigma_Y, U and V. + * @knee_y2: Knee point TNR3 assumes standard deviation of Y,U and + * V at Y2 are TnrY2_Sigma_Y, U and V. + * @maxfb_y: Max feedback gain for Y + * @maxfb_u: Max feedback gain for U + * @maxfb_v: Max feedback gain for V + * @round_adj_y: rounding Adjust for Y + * @round_adj_u: rounding Adjust for U + * @round_adj_v: rounding Adjust for V + * @ref_buf_select: selection of the reference frame buffer to be used. + */ +struct ipu3_uapi_isp_tnr3_params { + __u32 knee_y1; + __u32 knee_y2; + __u32 maxfb_y; + __u32 maxfb_u; + __u32 maxfb_v; + __u32 round_adj_y; + __u32 round_adj_u; + __u32 round_adj_v; + __u32 ref_buf_select; +} __packed; + +/* Extreme Noise Reduction version 3 */ + +/** + * struct ipu3_uapi_isp_xnr3_vmem_params - Extreme noise reduction v3 + * vector memory parameters + * + * @x: xnr3 parameters. + * @a: xnr3 parameters. + * @b: xnr3 parameters. + * @c: xnr3 parameters. + */ +struct ipu3_uapi_isp_xnr3_vmem_params { + __u16 x[IPU3_UAPI_ISP_VEC_ELEMS]; + __u16 a[IPU3_UAPI_ISP_VEC_ELEMS]; + __u16 b[IPU3_UAPI_ISP_VEC_ELEMS]; + __u16 c[IPU3_UAPI_ISP_VEC_ELEMS]; +} __packed; + +/** + * struct ipu3_uapi_xnr3_alpha_params - Extreme noise reduction v3 + * alpha tuning parameters + * + * @y0: Sigma for Y range similarity in dark area. + * @u0: Sigma for U range similarity in dark area. + * @v0: Sigma for V range similarity in dark area. + * @ydiff: Sigma difference for Y between bright area and dark area. + * @udiff: Sigma difference for U between bright area and dark area. + * @vdiff: Sigma difference for V between bright area and dark area. + */ +struct ipu3_uapi_xnr3_alpha_params { + __u32 y0; + __u32 u0; + __u32 v0; + __u32 ydiff; + __u32 udiff; + __u32 vdiff; +} __packed; + +/** + * struct ipu3_uapi_xnr3_coring_params - Extreme noise reduction v3 + * coring parameters + * + * @u0: Coring Threshold of U channel in dark area. + * @v0: Coring Threshold of V channel in dark area. + * @udiff: Threshold difference of U channel between bright and dark area. + * @vdiff: Threshold difference of V channel between bright and dark area. + */ +struct ipu3_uapi_xnr3_coring_params { + __u32 u0; + __u32 v0; + __u32 udiff; + __u32 vdiff; +} __packed; + +/** + * struct ipu3_uapi_xnr3_blending_params - Blending factor + * + * @strength: The factor for blending output with input. This is tuning + * parameterHigher values lead to more aggressive XNR operation. + */ +struct ipu3_uapi_xnr3_blending_params { + __u32 strength; +} __packed; + +/** + * struct ipu3_uapi_isp_xnr3_params - Extreme noise reduction v3 parameters + * + * @alpha: parameters for xnr3 alpha. See &ipu3_uapi_xnr3_alpha_params + * @coring: parameters for xnr3 coring. See &ipu3_uapi_xnr3_coring_params + * @blending: parameters for xnr3 blending. See &ipu3_uapi_xnr3_blending_params + */ +struct ipu3_uapi_isp_xnr3_params { + struct ipu3_uapi_xnr3_alpha_params alpha; + struct ipu3_uapi_xnr3_coring_params coring; + struct ipu3_uapi_xnr3_blending_params blending; +} __packed; + +/***** Obgrid (optical black level compensation) table entry *****/ + +/** + * struct ipu3_uapi_obgrid_param - Optical black level compensation parameters + * + * @gr: Grid table values for color GR + * @r: Grid table values for color R + * @b: Grid table values for color B + * @gb: Grid table values for color GB + * + * Black level is different for red, green, and blue channels. So black level + * compensation is different per channel. + */ +struct ipu3_uapi_obgrid_param { + __u16 gr; + __u16 r; + __u16 b; + __u16 gb; +} __packed; + +/******************* V4L2_META_FMT_IPU3_PARAMS *******************/ + +/** + * struct ipu3_uapi_flags - bits to indicate which pipeline needs update + * + * @gdc: 0 = no update, 1 = update. + * @obgrid: 0 = no update, 1 = update. + * @reserved1: Not used. + * @acc_bnr: 0 = no update, 1 = update. + * @acc_green_disparity: 0 = no update, 1 = update. + * @acc_dm: 0 = no update, 1 = update. + * @acc_ccm: 0 = no update, 1 = update. + * @acc_gamma: 0 = no update, 1 = update. + * @acc_csc: 0 = no update, 1 = update. + * @acc_cds: 0 = no update, 1 = update. + * @acc_shd: 0 = no update, 1 = update. + * @reserved2: Not used. + * @acc_iefd: 0 = no update, 1 = update. + * @acc_yds_c0: 0 = no update, 1 = update. + * @acc_chnr_c0: 0 = no update, 1 = update. + * @acc_y_ee_nr: 0 = no update, 1 = update. + * @acc_yds: 0 = no update, 1 = update. + * @acc_chnr: 0 = no update, 1 = update. + * @acc_ytm: 0 = no update, 1 = update. + * @acc_yds2: 0 = no update, 1 = update. + * @acc_tcc: 0 = no update, 1 = update. + * @acc_dpc: 0 = no update, 1 = update. + * @acc_bds: 0 = no update, 1 = update. + * @acc_anr: 0 = no update, 1 = update. + * @acc_awb_fr: 0 = no update, 1 = update. + * @acc_ae: 0 = no update, 1 = update. + * @acc_af: 0 = no update, 1 = update. + * @acc_awb: 0 = no update, 1 = update. + * @__acc_osys: 0 = no update, 1 = update. + * @reserved3: Not used. + * @lin_vmem_params: 0 = no update, 1 = update. + * @tnr3_vmem_params: 0 = no update, 1 = update. + * @xnr3_vmem_params: 0 = no update, 1 = update. + * @tnr3_dmem_params: 0 = no update, 1 = update. + * @xnr3_dmem_params: 0 = no update, 1 = update. + * @reserved4: Not used. + * @obgrid_param: 0 = no update, 1 = update. + * @reserved5: Not used. + */ +struct ipu3_uapi_flags { + __u32 gdc:1; + __u32 obgrid:1; + __u32 reserved1:30; + + __u32 acc_bnr:1; + __u32 acc_green_disparity:1; + __u32 acc_dm:1; + __u32 acc_ccm:1; + __u32 acc_gamma:1; + __u32 acc_csc:1; + __u32 acc_cds:1; + __u32 acc_shd:1; + __u32 reserved2:2; + __u32 acc_iefd:1; + __u32 acc_yds_c0:1; + __u32 acc_chnr_c0:1; + __u32 acc_y_ee_nr:1; + __u32 acc_yds:1; + __u32 acc_chnr:1; + __u32 acc_ytm:1; + __u32 acc_yds2:1; + __u32 acc_tcc:1; + __u32 acc_dpc:1; + __u32 acc_bds:1; + __u32 acc_anr:1; + __u32 acc_awb_fr:1; + __u32 acc_ae:1; + __u32 acc_af:1; + __u32 acc_awb:1; + __u32 reserved3:4; + + __u32 lin_vmem_params:1; + __u32 tnr3_vmem_params:1; + __u32 xnr3_vmem_params:1; + __u32 tnr3_dmem_params:1; + __u32 xnr3_dmem_params:1; + __u32 reserved4:1; + __u32 obgrid_param:1; + __u32 reserved5:25; +} __packed; + +/** + * struct ipu3_uapi_params - V4L2_META_FMT_IPU3_PARAMS + * + * @use: select which parameters to apply, see &ipu3_uapi_flags + * @acc_param: ACC parameters, as specified by &ipu3_uapi_acc_param + * @lin_vmem_params: linearization VMEM, as specified by + * &ipu3_uapi_isp_lin_vmem_params + * @tnr3_vmem_params: tnr3 VMEM as specified by + * &ipu3_uapi_isp_tnr3_vmem_params + * @xnr3_vmem_params: xnr3 VMEM as specified by + * &ipu3_uapi_isp_xnr3_vmem_params + * @tnr3_dmem_params: tnr3 DMEM as specified by &ipu3_uapi_isp_tnr3_params + * @xnr3_dmem_params: xnr3 DMEM as specified by &ipu3_uapi_isp_xnr3_params + * @obgrid_param: obgrid parameters as specified by + * &ipu3_uapi_obgrid_param + * + * The video queue "parameters" is of format V4L2_META_FMT_IPU3_PARAMS. + * This is a "single plane" v4l2_meta_format using V4L2_BUF_TYPE_META_OUTPUT. + * + * struct ipu3_uapi_params as defined below contains a lot of parameters and + * ipu3_uapi_flags selects which parameters to apply. + */ +struct ipu3_uapi_params { + /* Flags which of the settings below are to be applied */ + struct ipu3_uapi_flags use __attribute__((aligned(32))); + + /* Accelerator cluster parameters */ + struct ipu3_uapi_acc_param acc_param; + + /* ISP vector address space parameters */ + struct ipu3_uapi_isp_lin_vmem_params lin_vmem_params; + struct ipu3_uapi_isp_tnr3_vmem_params tnr3_vmem_params; + struct ipu3_uapi_isp_xnr3_vmem_params xnr3_vmem_params; + + /* ISP data memory (DMEM) parameters */ + struct ipu3_uapi_isp_tnr3_params tnr3_dmem_params; + struct ipu3_uapi_isp_xnr3_params xnr3_dmem_params; + + /* Optical black level compensation */ + struct ipu3_uapi_obgrid_param obgrid_param; +} __packed; + +#endif /* __IPU3_UAPI_H */ diff --git a/drivers/staging/media/ipu3/ipu3-abi.h b/drivers/staging/media/ipu3/ipu3-abi.h @@ -0,0 +1,2011 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ + +#ifndef __IPU3_ABI_H +#define __IPU3_ABI_H + +#include "include/intel-ipu3.h" + +/******************* IMGU Hardware information *******************/ + +typedef u32 imgu_addr_t; + +#define IMGU_ISP_VMEM_ALIGN 128 +#define IMGU_DVS_BLOCK_W 64 +#define IMGU_DVS_BLOCK_H 32 +#define IMGU_GDC_BUF_X (2 * IMGU_DVS_BLOCK_W) +#define IMGU_GDC_BUF_Y IMGU_DVS_BLOCK_H +/* n = 0..1 */ +#define IMGU_SP_PMEM_BASE(n) (0x20000 + (n) * 0x4000) +#define IMGU_MAX_BQ_GRID_WIDTH 80 +#define IMGU_MAX_BQ_GRID_HEIGHT 60 +#define IMGU_OBGRID_TILE_SIZE 16 +#define IMGU_PIXELS_PER_WORD 50 +#define IMGU_BYTES_PER_WORD 64 +#define IMGU_STRIPE_FIXED_HALF_OVERLAP 2 +#define IMGU_SHD_SETS 3 +#define IMGU_BDS_MIN_CLIP_VAL 0 +#define IMGU_BDS_MAX_CLIP_VAL 2 + +#define IMGU_ABI_AWB_MAX_CELLS_PER_SET 160 +#define IMGU_ABI_AF_MAX_CELLS_PER_SET 32 +#define IMGU_ABI_AWB_FR_MAX_CELLS_PER_SET 32 + +#define IMGU_ABI_ACC_OP_IDLE 0 +#define IMGU_ABI_ACC_OP_END_OF_ACK 1 +#define IMGU_ABI_ACC_OP_END_OF_OPS 2 +#define IMGU_ABI_ACC_OP_NO_OPS 3 + +#define IMGU_ABI_ACC_OPTYPE_PROCESS_LINES 0 +#define IMGU_ABI_ACC_OPTYPE_TRANSFER_DATA 1 + +/* Register definitions */ + +/* PM_CTRL_0_5_0_IMGHMMADR */ +#define IMGU_REG_PM_CTRL 0x0 +#define IMGU_PM_CTRL_START BIT(0) +#define IMGU_PM_CTRL_CFG_DONE BIT(1) +#define IMGU_PM_CTRL_RACE_TO_HALT BIT(2) +#define IMGU_PM_CTRL_NACK_ALL BIT(3) +#define IMGU_PM_CTRL_CSS_PWRDN BIT(4) +#define IMGU_PM_CTRL_RST_AT_EOF BIT(5) +#define IMGU_PM_CTRL_FORCE_HALT BIT(6) +#define IMGU_PM_CTRL_FORCE_UNHALT BIT(7) +#define IMGU_PM_CTRL_FORCE_PWRDN BIT(8) +#define IMGU_PM_CTRL_FORCE_RESET BIT(9) + +/* SYSTEM_REQ_0_5_0_IMGHMMADR */ +#define IMGU_REG_SYSTEM_REQ 0x18 +#define IMGU_SYSTEM_REQ_FREQ_MASK 0x3f +#define IMGU_SYSTEM_REQ_FREQ_DIVIDER 25 +#define IMGU_REG_INT_STATUS 0x30 +#define IMGU_REG_INT_ENABLE 0x34 +#define IMGU_REG_INT_CSS_IRQ BIT(31) +/* STATE_0_5_0_IMGHMMADR */ +#define IMGU_REG_STATE 0x130 +#define IMGU_STATE_HALT_STS BIT(0) +#define IMGU_STATE_IDLE_STS BIT(1) +#define IMGU_STATE_POWER_UP BIT(2) +#define IMGU_STATE_POWER_DOWN BIT(3) +#define IMGU_STATE_CSS_BUSY_MASK 0xc0 +#define IMGU_STATE_PM_FSM_MASK 0x180 +#define IMGU_STATE_PWRDNM_FSM_MASK 0x1E00000 +/* PM_STS_0_5_0_IMGHMMADR */ +#define IMGU_REG_PM_STS 0x140 + +#define IMGU_REG_BASE 0x4000 + +#define IMGU_REG_ISP_CTRL (IMGU_REG_BASE + 0x00) +#define IMGU_CTRL_RST BIT(0) +#define IMGU_CTRL_START BIT(1) +#define IMGU_CTRL_BREAK BIT(2) +#define IMGU_CTRL_RUN BIT(3) +#define IMGU_CTRL_BROKEN BIT(4) +#define IMGU_CTRL_IDLE BIT(5) +#define IMGU_CTRL_SLEEPING BIT(6) +#define IMGU_CTRL_STALLING BIT(7) +#define IMGU_CTRL_IRQ_CLEAR BIT(8) +#define IMGU_CTRL_IRQ_READY BIT(10) +#define IMGU_CTRL_IRQ_SLEEPING BIT(11) +#define IMGU_CTRL_ICACHE_INV BIT(12) +#define IMGU_CTRL_IPREFETCH_EN BIT(13) +#define IMGU_REG_ISP_START_ADDR (IMGU_REG_BASE + 0x04) +#define IMGU_REG_ISP_ICACHE_ADDR (IMGU_REG_BASE + 0x10) +#define IMGU_REG_ISP_PC (IMGU_REG_BASE + 0x1c) + +/* SP Registers, sp = 0:SP0; 1:SP1 */ +#define IMGU_REG_SP_CTRL(sp) (IMGU_REG_BASE + (sp) * 0x100 + 0x100) + /* For bits in IMGU_REG_SP_CTRL, see IMGU_CTRL_* */ +#define IMGU_REG_SP_START_ADDR(sp) (IMGU_REG_BASE + (sp) * 0x100 + 0x104) +#define IMGU_REG_SP_ICACHE_ADDR(sp) (IMGU_REG_BASE + (sp) * 0x100 + 0x11c) +#define IMGU_REG_SP_CTRL_SINK(sp) (IMGU_REG_BASE + (sp) * 0x100 + 0x130) +#define IMGU_REG_SP_PC(sp) (IMGU_REG_BASE + (sp) * 0x100 + 0x134) + +#define IMGU_REG_TLB_INVALIDATE (IMGU_REG_BASE + 0x300) +#define IMGU_TLB_INVALIDATE 1 +#define IMGU_REG_L1_PHYS (IMGU_REG_BASE + 0x304) /* 27-bit pfn */ + +#define IMGU_REG_CIO_GATE_BURST_STATE (IMGU_REG_BASE + 0x404) +#define IMGU_CIO_GATE_BURST_MASK 0x80 + +#define IMGU_REG_GP_BUSY (IMGU_REG_BASE + 0x500) +#define IMGU_REG_GP_STARVING (IMGU_REG_BASE + 0x504) +#define IMGU_REG_GP_WORKLOAD (IMGU_REG_BASE + 0x508) +#define IMGU_REG_GP_IRQ(n) (IMGU_REG_BASE + (n) * 4 + 0x50c) /* n = 0..4 */ +#define IMGU_REG_GP_SP1_STRMON_STAT (IMGU_REG_BASE + 0x520) +#define IMGU_REG_GP_SP2_STRMON_STAT (IMGU_REG_BASE + 0x524) +#define IMGU_REG_GP_ISP_STRMON_STAT (IMGU_REG_BASE + 0x528) +#define IMGU_REG_GP_MOD_STRMON_STAT (IMGU_REG_BASE + 0x52c) + +/* Port definitions for the streaming monitors. */ +/* For each definition there is signal pair : valid [bit 0]- accept [bit 1] */ +#define IMGU_GP_STRMON_STAT_SP1_PORT_SP12DMA BIT(0) +#define IMGU_GP_STRMON_STAT_SP1_PORT_DMA2SP1 BIT(2) +#define IMGU_GP_STRMON_STAT_SP1_PORT_SP12SP2 BIT(4) +#define IMGU_GP_STRMON_STAT_SP1_PORT_SP22SP1 BIT(6) +#define IMGU_GP_STRMON_STAT_SP1_PORT_SP12ISP BIT(8) +#define IMGU_GP_STRMON_STAT_SP1_PORT_ISP2SP1 BIT(10) + +#define IMGU_GP_STRMON_STAT_SP2_PORT_SP22DMA BIT(0) +#define IMGU_GP_STRMON_STAT_SP2_PORT_DMA2SP2 BIT(2) +#define IMGU_GP_STRMON_STAT_SP2_PORT_SP22SP1 BIT(4) +#define IMGU_GP_STRMON_STAT_SP2_PORT_SP12SP2 BIT(6) + +#define IMGU_GP_STRMON_STAT_ISP_PORT_ISP2DMA BIT(0) +#define IMGU_GP_STRMON_STAT_ISP_PORT_DMA2ISP BIT(2) +#define IMGU_GP_STRMON_STAT_ISP_PORT_ISP2SP1 BIT(4) +#define IMGU_GP_STRMON_STAT_ISP_PORT_SP12ISP BIT(6) + +/* Between the devices and the fifo */ +#define IMGU_GP_STRMON_STAT_MOD_PORT_SP12DMA BIT(0) +#define IMGU_GP_STRMON_STAT_MOD_PORT_DMA2SP1 BIT(2) +#define IMGU_GP_STRMON_STAT_MOD_PORT_SP22DMA BIT(4) +#define IMGU_GP_STRMON_STAT_MOD_PORT_DMA2SP2 BIT(6) +#define IMGU_GP_STRMON_STAT_MOD_PORT_ISP2DMA BIT(8) +#define IMGU_GP_STRMON_STAT_MOD_PORT_DMA2ISP BIT(10) +#define IMGU_GP_STRMON_STAT_MOD_PORT_CELLS2GDC BIT(12) +#define IMGU_GP_STRMON_STAT_MOD_PORT_GDC2CELLS BIT(14) +#define IMGU_GP_STRMON_STAT_MOD_PORT_CELLS2DECOMP BIT(16) +#define IMGU_GP_STRMON_STAT_MOD_PORT_DECOMP2CELLS BIT(18) +/* n = 1..6 */ +#define IMGU_GP_STRMON_STAT_MOD_PORT_S2V(n) (1 << (((n) - 1) * 2 + 20)) + +/* n = 1..15 */ +#define IMGU_GP_STRMON_STAT_ACCS_PORT_ACC(n) (1 << (((n) - 1) * 2)) + +/* After FIFO and demux before SP1, n = 1..15 */ +#define IMGU_GP_STRMON_STAT_ACCS2SP1_MON_PORT_ACC(n) (1 << (((n) - 1) * 2)) + +/* After FIFO and demux before SP2, n = 1..15 */ +#define IMGU_GP_STRMON_STAT_ACCS2SP2_MON_PORT_ACC(n) (1 << (((n) - 1) * 2)) + +#define IMGU_REG_GP_HALT (IMGU_REG_BASE + 0x5dc) + + /* n = 0..2 (main ctrl, SP0, SP1) */ +#define IMGU_REG_IRQCTRL_BASE(n) (IMGU_REG_BASE + (n) * 0x100 + 0x700) +#define IMGU_IRQCTRL_MAIN 0 +#define IMGU_IRQCTRL_SP0 1 +#define IMGU_IRQCTRL_SP1 2 +#define IMGU_IRQCTRL_NUM 3 +#define IMGU_IRQCTRL_IRQ_SP1 BIT(0) +#define IMGU_IRQCTRL_IRQ_SP2 BIT(1) +#define IMGU_IRQCTRL_IRQ_ISP BIT(2) +#define IMGU_IRQCTRL_IRQ_SP1_STREAM_MON BIT(3) +#define IMGU_IRQCTRL_IRQ_SP2_STREAM_MON BIT(4) +#define IMGU_IRQCTRL_IRQ_ISP_STREAM_MON BIT(5) +#define IMGU_IRQCTRL_IRQ_MOD_STREAM_MON BIT(6) +#define IMGU_IRQCTRL_IRQ_MOD_ISP_STREAM_MON BIT(7) +#define IMGU_IRQCTRL_IRQ_ACCS_STREAM_MON BIT(8) +#define IMGU_IRQCTRL_IRQ_ACCS_SP1_STREAM_MON BIT(9) +#define IMGU_IRQCTRL_IRQ_ACCS_SP2_STREAM_MON BIT(10) +#define IMGU_IRQCTRL_IRQ_ISP_PMEM_ERROR BIT(11) +#define IMGU_IRQCTRL_IRQ_ISP_BAMEM_ERROR BIT(12) +#define IMGU_IRQCTRL_IRQ_ISP_VMEM_ERROR BIT(13) +#define IMGU_IRQCTRL_IRQ_ISP_DMEM_ERROR BIT(14) +#define IMGU_IRQCTRL_IRQ_SP1_ICACHE_MEM_ERROR BIT(15) +#define IMGU_IRQCTRL_IRQ_SP1_DMEM_ERROR BIT(16) +#define IMGU_IRQCTRL_IRQ_SP2_ICACHE_MEM_ERROR BIT(17) +#define IMGU_IRQCTRL_IRQ_SP2_DMEM_ERROR BIT(18) +#define IMGU_IRQCTRL_IRQ_ACCS_SCRATCH_MEM_ERROR BIT(19) +#define IMGU_IRQCTRL_IRQ_GP_TIMER(n) BIT(20 + (n)) /* n=0..1 */ +#define IMGU_IRQCTRL_IRQ_DMA BIT(22) +#define IMGU_IRQCTRL_IRQ_SW_PIN(n) BIT(23 + (n)) /* n=0..4 */ +#define IMGU_IRQCTRL_IRQ_ACC_SYS BIT(28) +#define IMGU_IRQCTRL_IRQ_OUT_FORM_IRQ_CTRL BIT(29) +#define IMGU_IRQCTRL_IRQ_SP1_IRQ_CTRL BIT(30) +#define IMGU_IRQCTRL_IRQ_SP2_IRQ_CTRL BIT(31) +#define IMGU_REG_IRQCTRL_EDGE(n) (IMGU_REG_IRQCTRL_BASE(n) + 0x00) +#define IMGU_REG_IRQCTRL_MASK(n) (IMGU_REG_IRQCTRL_BASE(n) + 0x04) +#define IMGU_REG_IRQCTRL_STATUS(n) (IMGU_REG_IRQCTRL_BASE(n) + 0x08) +#define IMGU_REG_IRQCTRL_CLEAR(n) (IMGU_REG_IRQCTRL_BASE(n) + 0x0c) +#define IMGU_REG_IRQCTRL_ENABLE(n) (IMGU_REG_IRQCTRL_BASE(n) + 0x10) +#define IMGU_REG_IRQCTRL_EDGE_NOT_PULSE(n) (IMGU_REG_IRQCTRL_BASE(n) + 0x14) +#define IMGU_REG_IRQCTRL_STR_OUT_ENABLE(n) (IMGU_REG_IRQCTRL_BASE(n) + 0x18) + +#define IMGU_REG_GP_TIMER (IMGU_REG_BASE + 0xa34) + +#define IMGU_REG_SP_DMEM_BASE(n) (IMGU_REG_BASE + (n) * 0x4000 + 0x4000) +#define IMGU_REG_ISP_DMEM_BASE (IMGU_REG_BASE + 0xc000) + +#define IMGU_REG_GDC_BASE (IMGU_REG_BASE + 0x18000) +#define IMGU_REG_GDC_LUT_BASE (IMGU_REG_GDC_BASE + 0x140) +#define IMGU_GDC_LUT_MASK ((1 << 12) - 1) /* Range -1024..+1024 */ + +#define IMGU_SCALER_PHASES 32 +#define IMGU_SCALER_COEFF_BITS 24 +#define IMGU_SCALER_PHASE_COUNTER_PREC_REF 6 +#define IMGU_SCALER_MAX_EXPONENT_SHIFT 3 +#define IMGU_SCALER_FILTER_TAPS 4 +#define IMGU_SCALER_TAPS_Y IMGU_SCALER_FILTER_TAPS +#define IMGU_SCALER_TAPS_UV (IMGU_SCALER_FILTER_TAPS / 2) +#define IMGU_SCALER_FIR_PHASES \ + (IMGU_SCALER_PHASES << IMGU_SCALER_PHASE_COUNTER_PREC_REF) + +/******************* imgu_abi_acc_param *******************/ + +#define IMGU_ABI_SHD_MAX_PROCESS_LINES 31 +#define IMGU_ABI_SHD_MAX_TRANSFERS 31 +#define IMGU_ABI_SHD_MAX_OPERATIONS \ + (IMGU_ABI_SHD_MAX_PROCESS_LINES + IMGU_ABI_SHD_MAX_TRANSFERS) +#define IMGU_ABI_SHD_MAX_CELLS_PER_SET 146 +/* largest grid is 73x56 */ +#define IMGU_ABI_SHD_MAX_CFG_SETS (2 * 28) + +#define IMGU_ABI_DVS_STAT_MAX_OPERATIONS 100 +#define IMGU_ABI_DVS_STAT_MAX_PROCESS_LINES 52 +#define IMGU_ABI_DVS_STAT_MAX_TRANSFERS 52 + +#define IMGU_ABI_BDS_SAMPLE_PATTERN_ARRAY_SIZE 8 +#define IMGU_ABI_BDS_PHASE_COEFFS_ARRAY_SIZE 32 + +#define IMGU_ABI_AWB_FR_MAX_TRANSFERS 30 +#define IMGU_ABI_AWB_FR_MAX_PROCESS_LINES 30 +#define IMGU_ABI_AWB_FR_MAX_OPERATIONS \ + (IMGU_ABI_AWB_FR_MAX_TRANSFERS + IMGU_ABI_AWB_FR_MAX_PROCESS_LINES) + +#define IMGU_ABI_AF_MAX_TRANSFERS 30 +#define IMGU_ABI_AF_MAX_PROCESS_LINES 30 +#define IMGU_ABI_AF_MAX_OPERATIONS \ + (IMGU_ABI_AF_MAX_TRANSFERS + IMGU_ABI_AF_MAX_PROCESS_LINES) + +#define IMGU_ABI_AWB_MAX_PROCESS_LINES 68 +#define IMGU_ABI_AWB_MAX_TRANSFERS 68 +#define IMGU_ABI_AWB_MAX_OPERATIONS \ + (IMGU_ABI_AWB_MAX_PROCESS_LINES + IMGU_ABI_AWB_MAX_TRANSFERS) + +#define IMGU_ABI_OSYS_PIN_VF 0 +#define IMGU_ABI_OSYS_PIN_OUT 1 +#define IMGU_ABI_OSYS_PINS 2 + +#define IMGU_ABI_DVS_STAT_LEVELS 3 +#define IMGU_ABI_YUVP2_YTM_LUT_ENTRIES 256 +#define IMGU_ABI_GDC_FRAC_BITS 8 +#define IMGU_ABI_BINARY_MAX_OUTPUT_PORTS 2 +#define IMGU_ABI_MAX_BINARY_NAME 64 +#define IMGU_ABI_ISP_DDR_WORD_BITS 256 +#define IMGU_ABI_ISP_DDR_WORD_BYTES (IMGU_ABI_ISP_DDR_WORD_BITS / 8) +#define IMGU_ABI_MAX_STAGES 3 +#define IMGU_ABI_MAX_IF_CONFIGS 3 +#define IMGU_ABI_PIPE_CONFIG_ACQUIRE_ISP BIT(31) +#define IMGU_ABI_PORT_CONFIG_TYPE_INPUT_HOST BIT(0) +#define IMGU_ABI_PORT_CONFIG_TYPE_OUTPUT_HOST BIT(4) +#define IMGU_ABI_MAX_SP_THREADS 4 +#define IMGU_ABI_FRAMES_REF 3 +#define IMGU_ABI_FRAMES_TNR 4 +#define IMGU_ABI_BUF_SETS_TNR 1 + +#define IMGU_ABI_EVENT_BUFFER_ENQUEUED(thread, queue) \ + (0 << 24 | (thread) << 16 | (queue) << 8) +#define IMGU_ABI_EVENT_BUFFER_DEQUEUED(queue) (1 << 24 | (queue) << 8) +#define IMGU_ABI_EVENT_EVENT_DEQUEUED (2 << 24) +#define IMGU_ABI_EVENT_START_STREAM (3 << 24) +#define IMGU_ABI_EVENT_STOP_STREAM (4 << 24) +#define IMGU_ABI_EVENT_MIPI_BUFFERS_READY (5 << 24) +#define IMGU_ABI_EVENT_UNLOCK_RAW_BUFFER (6 << 24) +#define IMGU_ABI_EVENT_STAGE_ENABLE_DISABLE (7 << 24) + +#define IMGU_ABI_HOST2SP_BUFQ_SIZE 3 +#define IMGU_ABI_SP2HOST_BUFQ_SIZE (2 * IMGU_ABI_MAX_SP_THREADS) +#define IMGU_ABI_HOST2SP_EVTQ_SIZE (IMGU_ABI_QUEUE_NUM * \ + IMGU_ABI_MAX_SP_THREADS * 2 + IMGU_ABI_MAX_SP_THREADS * 4) +#define IMGU_ABI_SP2HOST_EVTQ_SIZE (6 * IMGU_ABI_MAX_SP_THREADS) + +#define IMGU_ABI_EVTTYPE_EVENT_SHIFT 0 +#define IMGU_ABI_EVTTYPE_EVENT_MASK (0xff << IMGU_ABI_EVTTYPE_EVENT_SHIFT) +#define IMGU_ABI_EVTTYPE_PIPE_SHIFT 8 +#define IMGU_ABI_EVTTYPE_PIPE_MASK (0xff << IMGU_ABI_EVTTYPE_PIPE_SHIFT) +#define IMGU_ABI_EVTTYPE_PIPEID_SHIFT 16 +#define IMGU_ABI_EVTTYPE_PIPEID_MASK (0xff << IMGU_ABI_EVTTYPE_PIPEID_SHIFT) +#define IMGU_ABI_EVTTYPE_MODULEID_SHIFT 8 +#define IMGU_ABI_EVTTYPE_MODULEID_MASK (0xff << IMGU_ABI_EVTTYPE_MODULEID_SHIFT) +#define IMGU_ABI_EVTTYPE_LINENO_SHIFT 16 +#define IMGU_ABI_EVTTYPE_LINENO_MASK (0xffff << IMGU_ABI_EVTTYPE_LINENO_SHIFT) + +/* Output frame ready */ +#define IMGU_ABI_EVTTYPE_OUT_FRAME_DONE 0 +/* Second output frame ready */ +#define IMGU_ABI_EVTTYPE_2ND_OUT_FRAME_DONE 1 +/* Viewfinder Output frame ready */ +#define IMGU_ABI_EVTTYPE_VF_OUT_FRAME_DONE 2 +/* Second viewfinder Output frame ready */ +#define IMGU_ABI_EVTTYPE_2ND_VF_OUT_FRAME_DONE 3 +/* Indication that 3A statistics are available */ +#define IMGU_ABI_EVTTYPE_3A_STATS_DONE 4 +/* Indication that DIS statistics are available */ +#define IMGU_ABI_EVTTYPE_DIS_STATS_DONE 5 +/* Pipeline Done event, sent after last pipeline stage */ +#define IMGU_ABI_EVTTYPE_PIPELINE_DONE 6 +/* Frame tagged */ +#define IMGU_ABI_EVTTYPE_FRAME_TAGGED 7 +/* Input frame ready */ +#define IMGU_ABI_EVTTYPE_INPUT_FRAME_DONE 8 +/* Metadata ready */ +#define IMGU_ABI_EVTTYPE_METADATA_DONE 9 +/* Indication that LACE statistics are available */ +#define IMGU_ABI_EVTTYPE_LACE_STATS_DONE 10 +/* Extension stage executed */ +#define IMGU_ABI_EVTTYPE_ACC_STAGE_COMPLETE 11 +/* Timing measurement data */ +#define IMGU_ABI_EVTTYPE_TIMER 12 +/* End Of Frame event, sent when in buffered sensor mode */ +#define IMGU_ABI_EVTTYPE_PORT_EOF 13 +/* Performance warning encountered by FW */ +#define IMGU_ABI_EVTTYPE_FW_WARNING 14 +/* Assertion hit by FW */ +#define IMGU_ABI_EVTTYPE_FW_ASSERT 15 + +#define IMGU_ABI_NUM_CONTINUOUS_FRAMES 10 +#define IMGU_ABI_SP_COMM_COMMAND 0x00 + +/* + * The host2sp_cmd_ready command is the only command written by the SP + * It acknowledges that is previous command has been received. + * (this does not mean that the command has been executed) + * It also indicates that a new command can be send (it is a queue + * with depth 1). + */ +#define IMGU_ABI_SP_COMM_COMMAND_READY 1 +/* Command written by the Host */ +#define IMGU_ABI_SP_COMM_COMMAND_DUMMY 2 /* No action */ +#define IMGU_ABI_SP_COMM_COMMAND_START_FLASH 3 /* Start the flash */ +#define IMGU_ABI_SP_COMM_COMMAND_TERMINATE 4 /* Terminate */ + +/* n = 0..IPU3_CSS_PIPE_ID_NUM-1 */ +#define IMGU_ABI_SP_COMM_EVENT_IRQ_MASK(n) ((n) * 4 + 0x60) +#define IMGU_ABI_SP_COMM_EVENT_IRQ_MASK_OR_SHIFT 0 +#define IMGU_ABI_SP_COMM_EVENT_IRQ_MASK_AND_SHIFT 16 + +#define IMGU_ABI_BL_DMACMD_TYPE_SP_PMEM 1 /* sp_pmem */ + +/***** For parameter computation *****/ + +#define IMGU_HIVE_OF_SYS_SCALER_TO_FA_OFFSET 0xC +#define IMGU_HIVE_OF_SYS_OF_TO_FA_OFFSET 0x8 +#define IMGU_HIVE_OF_SYS_OF_SYSTEM_NWAYS 32 + +#define IMGU_SCALER_ELEMS_PER_VEC 0x10 +#define IMGU_SCALER_FILTER_TAPS_Y 0x4 +#define IMGU_SCALER_OUT_BPP 0x8 + +#define IMGU_SCALER_MS_TO_OUTFORMACC_SL_ADDR 0x400 +#define IMGU_SCALER_TO_OF_ACK_FA_ADDR \ + (0xC00 + IMGU_HIVE_OF_SYS_SCALER_TO_FA_OFFSET) +#define IMGU_OF_TO_ACK_FA_ADDR (0xC00 + IMGU_HIVE_OF_SYS_OF_TO_FA_OFFSET) +#define IMGU_OUTFORMACC_MS_TO_SCALER_SL_ADDR 0 +#define IMGU_SCALER_INTR_BPP 10 + +#define IMGU_PS_SNR_PRESERVE_BITS 3 +#define IMGU_CNTX_BPP 11 +#define IMGU_SCALER_FILTER_TAPS_UV (IMGU_SCALER_FILTER_TAPS_Y / 2) + +#define IMGU_VMEM2_ELEMS_PER_VEC (IMGU_SCALER_ELEMS_PER_VEC) +#define IMGU_STRIDE_Y (IMGU_SCALER_FILTER_TAPS_Y + 1) +#define IMGU_MAX_FRAME_WIDTH 3840 +#define IMGU_VMEM3_ELEMS_PER_VEC (IMGU_SCALER_ELEMS_PER_VEC) + +#define IMGU_VER_CNTX_WORDS DIV_ROUND_UP((IMGU_SCALER_OUT_BPP + \ + IMGU_PS_SNR_PRESERVE_BITS), IMGU_CNTX_BPP) /* 1 */ +#define IMGU_MAX_INPUT_BLOCK_HEIGHT 64 +#define IMGU_HOR_CNTX_WORDS DIV_ROUND_UP((IMGU_SCALER_INTR_BPP + \ + IMGU_PS_SNR_PRESERVE_BITS), IMGU_CNTX_BPP) /* 2 */ +#define IMGU_MAX_OUTPUT_BLOCK_WIDTH 128 +#define IMGU_CNTX_STRIDE_UV (IMGU_SCALER_FILTER_TAPS_UV + 1) + +#define IMGU_OSYS_DMA_CROP_W_LIMIT 64 +#define IMGU_OSYS_DMA_CROP_H_LIMIT 4 +#define IMGU_OSYS_BLOCK_WIDTH (2 * IPU3_UAPI_ISP_VEC_ELEMS) +#define IMGU_OSYS_BLOCK_HEIGHT 32 +#define IMGU_OSYS_PHASES 0x20 +#define IMGU_OSYS_FILTER_TAPS 0x4 +#define IMGU_OSYS_PHASE_COUNTER_PREC_REF 6 +#define IMGU_OSYS_NUM_INPUT_BUFFERS 2 +#define IMGU_OSYS_FIR_PHASES \ + (IMGU_OSYS_PHASES << IMGU_OSYS_PHASE_COUNTER_PREC_REF) +#define IMGU_OSYS_TAPS_UV (IMGU_OSYS_FILTER_TAPS / 2) +#define IMGU_OSYS_TAPS_Y (IMGU_OSYS_FILTER_TAPS) +#define IMGU_OSYS_NUM_INTERM_BUFFERS 2 + +#define IMGU_VMEM1_Y_SIZE \ + (IMGU_OSYS_BLOCK_HEIGHT * IMGU_VMEM1_Y_STRIDE) +#define IMGU_VMEM1_UV_SIZE (IMGU_VMEM1_Y_SIZE / 4) +#define IMGU_VMEM1_OUT_BUF_ADDR (IMGU_VMEM1_INP_BUF_ADDR + \ + (IMGU_OSYS_NUM_INPUT_BUFFERS * IMGU_VMEM1_BUF_SIZE)) +#define IMGU_OSYS_NUM_OUTPUT_BUFFERS 2 + +/* transpose of input height */ +#define IMGU_VMEM2_VECS_PER_LINE \ + (DIV_ROUND_UP(IMGU_OSYS_BLOCK_HEIGHT, IMGU_VMEM2_ELEMS_PER_VEC)) +/* size in words (vectors) */ +#define IMGU_VMEM2_BUF_SIZE \ + (IMGU_VMEM2_VECS_PER_LINE * IMGU_VMEM2_LINES_PER_BLOCK) +#define IMGU_VMEM3_VER_Y_SIZE \ + ((IMGU_STRIDE_Y * IMGU_MAX_FRAME_WIDTH \ + / IMGU_VMEM3_ELEMS_PER_VEC) * IMGU_VER_CNTX_WORDS) +#define IMGU_VMEM3_HOR_Y_SIZE \ + ((IMGU_STRIDE_Y * IMGU_MAX_INPUT_BLOCK_HEIGHT \ + / IMGU_VMEM3_ELEMS_PER_VEC) * IMGU_HOR_CNTX_WORDS) +#define IMGU_VMEM3_VER_Y_EXTRA \ + ((IMGU_STRIDE_Y * IMGU_MAX_OUTPUT_BLOCK_WIDTH \ + / IMGU_VMEM3_ELEMS_PER_VEC) * IMGU_VER_CNTX_WORDS) +#define IMGU_VMEM3_VER_U_SIZE \ + (((IMGU_CNTX_STRIDE_UV * IMGU_MAX_FRAME_WIDTH \ + / IMGU_VMEM3_ELEMS_PER_VEC) * IMGU_VER_CNTX_WORDS) / 2) +#define IMGU_VMEM3_HOR_U_SIZE \ + (((IMGU_STRIDE_Y * IMGU_MAX_INPUT_BLOCK_HEIGHT \ + / IMGU_VMEM3_ELEMS_PER_VEC) * IMGU_HOR_CNTX_WORDS) / 2) +#define IMGU_VMEM3_VER_U_EXTRA \ + (((IMGU_CNTX_STRIDE_UV * IMGU_MAX_OUTPUT_BLOCK_WIDTH \ + / IMGU_VMEM3_ELEMS_PER_VEC) * IMGU_VER_CNTX_WORDS) / 2) +#define IMGU_VMEM3_VER_V_SIZE \ + (((IMGU_CNTX_STRIDE_UV * IMGU_MAX_FRAME_WIDTH \ + / IMGU_VMEM3_ELEMS_PER_VEC) * IMGU_VER_CNTX_WORDS) / 2) + +#define IMGU_ISP_VEC_NELEMS 64 +#define IMGU_LUMA_TO_CHROMA_RATIO 2 +#define IMGU_INPUT_BLOCK_WIDTH (128) +#define IMGU_FIFO_ADDR_SCALER_TO_FMT \ + (IMGU_SCALER_MS_TO_OUTFORMACC_SL_ADDR >> 2) +#define IMGU_FIFO_ADDR_SCALER_TO_SP (IMGU_SCALER_TO_OF_ACK_FA_ADDR >> 2) +#define IMGU_VMEM1_INP_BUF_ADDR 0 +#define IMGU_VMEM1_Y_STRIDE \ + (IMGU_OSYS_BLOCK_WIDTH / IMGU_VMEM1_ELEMS_PER_VEC) +#define IMGU_VMEM1_BUF_SIZE (IMGU_VMEM1_V_OFFSET + IMGU_VMEM1_UV_SIZE) + +#define IMGU_VMEM1_U_OFFSET (IMGU_VMEM1_Y_SIZE) +#define IMGU_VMEM1_V_OFFSET (IMGU_VMEM1_U_OFFSET + IMGU_VMEM1_UV_SIZE) +#define IMGU_VMEM1_UV_STRIDE (IMGU_VMEM1_Y_STRIDE / 2) +#define IMGU_VMEM1_INT_BUF_ADDR (IMGU_VMEM1_OUT_BUF_ADDR + \ + (IMGU_OSYS_NUM_OUTPUT_BUFFERS * IMGU_VMEM1_BUF_SIZE)) + +#define IMGU_VMEM1_ELEMS_PER_VEC (IMGU_HIVE_OF_SYS_OF_SYSTEM_NWAYS) +#define IMGU_VMEM2_BUF_Y_ADDR 0 +#define IMGU_VMEM2_BUF_Y_STRIDE (IMGU_VMEM2_VECS_PER_LINE) +#define IMGU_VMEM2_BUF_U_ADDR \ + (IMGU_VMEM2_BUF_Y_ADDR + IMGU_VMEM2_BUF_SIZE) +#define IMGU_VMEM2_BUF_V_ADDR \ + (IMGU_VMEM2_BUF_U_ADDR + IMGU_VMEM2_BUF_SIZE / 4) +#define IMGU_VMEM2_BUF_UV_STRIDE (IMGU_VMEM2_VECS_PER_LINE / 2) +/* 1.5 x depth of intermediate buffer */ +#define IMGU_VMEM2_LINES_PER_BLOCK 192 +#define IMGU_VMEM3_HOR_Y_ADDR \ + (IMGU_VMEM3_VER_Y_ADDR + IMGU_VMEM3_VER_Y_SIZE) +#define IMGU_VMEM3_HOR_U_ADDR \ + (IMGU_VMEM3_VER_U_ADDR + IMGU_VMEM3_VER_U_SIZE) +#define IMGU_VMEM3_HOR_V_ADDR \ + (IMGU_VMEM3_VER_V_ADDR + IMGU_VMEM3_VER_V_SIZE) +#define IMGU_VMEM3_VER_Y_ADDR 0 +#define IMGU_VMEM3_VER_U_ADDR \ + (IMGU_VMEM3_VER_Y_ADDR + IMGU_VMEM3_VER_Y_SIZE + \ + max(IMGU_VMEM3_HOR_Y_SIZE, IMGU_VMEM3_VER_Y_EXTRA)) +#define IMGU_VMEM3_VER_V_ADDR \ + (IMGU_VMEM3_VER_U_ADDR + IMGU_VMEM3_VER_U_SIZE + \ + max(IMGU_VMEM3_HOR_U_SIZE, IMGU_VMEM3_VER_U_EXTRA)) +#define IMGU_FIFO_ADDR_FMT_TO_SP (IMGU_OF_TO_ACK_FA_ADDR >> 2) +#define IMGU_FIFO_ADDR_FMT_TO_SCALER (IMGU_OUTFORMACC_MS_TO_SCALER_SL_ADDR >> 2) +#define IMGU_VMEM1_HST_BUF_ADDR (IMGU_VMEM1_INT_BUF_ADDR + \ + (IMGU_OSYS_NUM_INTERM_BUFFERS * IMGU_VMEM1_BUF_SIZE)) +#define IMGU_VMEM1_HST_BUF_STRIDE 120 +#define IMGU_VMEM1_HST_BUF_NLINES 3 + +enum imgu_abi_frame_format { + IMGU_ABI_FRAME_FORMAT_NV11, /* 12 bit YUV 411, Y, UV plane */ + IMGU_ABI_FRAME_FORMAT_NV12, /* 12 bit YUV 420, Y, UV plane */ + IMGU_ABI_FRAME_FORMAT_NV12_16, /* 16 bit YUV 420, Y, UV plane */ + IMGU_ABI_FRAME_FORMAT_NV12_TILEY,/* 12 bit YUV 420,Intel tiled format */ + IMGU_ABI_FRAME_FORMAT_NV16, /* 16 bit YUV 422, Y, UV plane */ + IMGU_ABI_FRAME_FORMAT_NV21, /* 12 bit YUV 420, Y, VU plane */ + IMGU_ABI_FRAME_FORMAT_NV61, /* 16 bit YUV 422, Y, VU plane */ + IMGU_ABI_FRAME_FORMAT_YV12, /* 12 bit YUV 420, Y, V, U plane */ + IMGU_ABI_FRAME_FORMAT_YV16, /* 16 bit YUV 422, Y, V, U plane */ + IMGU_ABI_FRAME_FORMAT_YUV420, /* 12 bit YUV 420, Y, U, V plane */ + IMGU_ABI_FRAME_FORMAT_YUV420_16,/* yuv420, 16 bits per subpixel */ + IMGU_ABI_FRAME_FORMAT_YUV422, /* 16 bit YUV 422, Y, U, V plane */ + IMGU_ABI_FRAME_FORMAT_YUV422_16,/* yuv422, 16 bits per subpixel */ + IMGU_ABI_FRAME_FORMAT_UYVY, /* 16 bit YUV 422, UYVY interleaved */ + IMGU_ABI_FRAME_FORMAT_YUYV, /* 16 bit YUV 422, YUYV interleaved */ + IMGU_ABI_FRAME_FORMAT_YUV444, /* 24 bit YUV 444, Y, U, V plane */ + IMGU_ABI_FRAME_FORMAT_YUV_LINE, /* Internal format, 2 y lines */ + /* followed by a uv-interleaved line */ + IMGU_ABI_FRAME_FORMAT_RAW, /* RAW, 1 plane */ + IMGU_ABI_FRAME_FORMAT_RGB565, /* 16 bit RGB, 1 plane. Each 3 sub + * pixels are packed into one 16 bit + * value, 5 bits for R, 6 bits for G + * and 5 bits for B. + */ + IMGU_ABI_FRAME_FORMAT_PLANAR_RGB888, /* 24 bit RGB, 3 planes */ + IMGU_ABI_FRAME_FORMAT_RGBA888, /* 32 bit RGBA, 1 plane, A=Alpha + * (alpha is unused) + */ + IMGU_ABI_FRAME_FORMAT_QPLANE6, /* Internal, for advanced ISP */ + IMGU_ABI_FRAME_FORMAT_BINARY_8, /* byte stream, used for jpeg. For + * frames of this type, we set the + * height to 1 and the width to the + * number of allocated bytes. + */ + IMGU_ABI_FRAME_FORMAT_MIPI, /* MIPI frame, 1 plane */ + IMGU_ABI_FRAME_FORMAT_RAW_PACKED, /* RAW, 1 plane, packed */ + IMGU_ABI_FRAME_FORMAT_CSI_MIPI_YUV420_8, /* 8 bit per Y/U/V. Y odd line + * UYVY interleaved even line + */ + IMGU_ABI_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8, /* Legacy YUV420. + * UY odd line; + * VY even line + */ + IMGU_ABI_FRAME_FORMAT_CSI_MIPI_YUV420_10,/* 10 bit per Y/U/V. Y odd + * line; UYVY interleaved + * even line + */ + IMGU_ABI_FRAME_FORMAT_YCGCO444_16, /* Internal format for ISP2.7, + * 16 bits per plane YUV 444, + * Y, U, V plane + */ + IMGU_ABI_FRAME_FORMAT_NUM +}; + +enum imgu_abi_bayer_order { + IMGU_ABI_BAYER_ORDER_GRBG, + IMGU_ABI_BAYER_ORDER_RGGB, + IMGU_ABI_BAYER_ORDER_BGGR, + IMGU_ABI_BAYER_ORDER_GBRG +}; + +enum imgu_abi_osys_format { + IMGU_ABI_OSYS_FORMAT_YUV420, + IMGU_ABI_OSYS_FORMAT_YV12, + IMGU_ABI_OSYS_FORMAT_NV12, + IMGU_ABI_OSYS_FORMAT_NV21, + IMGU_ABI_OSYS_FORMAT_YUV_LINE, + IMGU_ABI_OSYS_FORMAT_YUY2, /* = IMGU_ABI_OSYS_FORMAT_YUYV */ + IMGU_ABI_OSYS_FORMAT_NV16, + IMGU_ABI_OSYS_FORMAT_RGBA, + IMGU_ABI_OSYS_FORMAT_BGRA +}; + +enum imgu_abi_osys_tiling { + IMGU_ABI_OSYS_TILING_NONE, + IMGU_ABI_OSYS_TILING_Y, + IMGU_ABI_OSYS_TILING_YF, +}; + +enum imgu_abi_osys_procmode { + IMGU_ABI_OSYS_PROCMODE_BYPASS, + IMGU_ABI_OSYS_PROCMODE_UPSCALE, + IMGU_ABI_OSYS_PROCMODE_DOWNSCALE, +}; + +enum imgu_abi_queue_id { + IMGU_ABI_QUEUE_EVENT_ID = -1, + IMGU_ABI_QUEUE_A_ID = 0, + IMGU_ABI_QUEUE_B_ID, + IMGU_ABI_QUEUE_C_ID, + IMGU_ABI_QUEUE_D_ID, + IMGU_ABI_QUEUE_E_ID, + IMGU_ABI_QUEUE_F_ID, + IMGU_ABI_QUEUE_G_ID, + IMGU_ABI_QUEUE_H_ID, /* input frame queue for skycam */ + IMGU_ABI_QUEUE_NUM +}; + +enum imgu_abi_buffer_type { + IMGU_ABI_BUFFER_TYPE_INVALID = -1, + IMGU_ABI_BUFFER_TYPE_3A_STATISTICS = 0, + IMGU_ABI_BUFFER_TYPE_DIS_STATISTICS, + IMGU_ABI_BUFFER_TYPE_LACE_STATISTICS, + IMGU_ABI_BUFFER_TYPE_INPUT_FRAME, + IMGU_ABI_BUFFER_TYPE_OUTPUT_FRAME, + IMGU_ABI_BUFFER_TYPE_SEC_OUTPUT_FRAME, + IMGU_ABI_BUFFER_TYPE_VF_OUTPUT_FRAME, + IMGU_ABI_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME, + IMGU_ABI_BUFFER_TYPE_RAW_OUTPUT_FRAME, + IMGU_ABI_BUFFER_TYPE_CUSTOM_INPUT, + IMGU_ABI_BUFFER_TYPE_CUSTOM_OUTPUT, + IMGU_ABI_BUFFER_TYPE_METADATA, + IMGU_ABI_BUFFER_TYPE_PARAMETER_SET, + IMGU_ABI_BUFFER_TYPE_PER_FRAME_PARAMETER_SET, + IMGU_ABI_NUM_DYNAMIC_BUFFER_TYPE, + IMGU_ABI_NUM_BUFFER_TYPE +}; + +enum imgu_abi_raw_type { + IMGU_ABI_RAW_TYPE_BAYER, + IMGU_ABI_RAW_TYPE_IR_ON_GR, + IMGU_ABI_RAW_TYPE_IR_ON_GB +}; + +enum imgu_abi_memories { + IMGU_ABI_MEM_ISP_PMEM0 = 0, + IMGU_ABI_MEM_ISP_DMEM0, + IMGU_ABI_MEM_ISP_VMEM0, + IMGU_ABI_MEM_ISP_VAMEM0, + IMGU_ABI_MEM_ISP_VAMEM1, + IMGU_ABI_MEM_ISP_VAMEM2, + IMGU_ABI_MEM_ISP_HMEM0, + IMGU_ABI_MEM_SP0_DMEM0, + IMGU_ABI_MEM_SP1_DMEM0, + IMGU_ABI_MEM_DDR, + IMGU_ABI_NUM_MEMORIES +}; + +enum imgu_abi_param_class { + IMGU_ABI_PARAM_CLASS_PARAM, /* Late binding parameters, like 3A */ + IMGU_ABI_PARAM_CLASS_CONFIG, /* Pipe config time parameters */ + IMGU_ABI_PARAM_CLASS_STATE, /* State parameters, eg. buffer index */ + IMGU_ABI_PARAM_CLASS_NUM +}; + +enum imgu_abi_bin_input_src { + IMGU_ABI_BINARY_INPUT_SOURCE_SENSOR, + IMGU_ABI_BINARY_INPUT_SOURCE_MEMORY, + IMGU_ABI_BINARY_INPUT_SOURCE_VARIABLE, +}; + +enum imgu_abi_sp_swstate { + IMGU_ABI_SP_SWSTATE_TERMINATED, + IMGU_ABI_SP_SWSTATE_INITIALIZED, + IMGU_ABI_SP_SWSTATE_CONNECTED, + IMGU_ABI_SP_SWSTATE_RUNNING, +}; + +enum imgu_abi_bl_swstate { + IMGU_ABI_BL_SWSTATE_OK = 0x100, + IMGU_ABI_BL_SWSTATE_BUSY, + IMGU_ABI_BL_SWSTATE_ERR, +}; + +/* The type of pipe stage */ +enum imgu_abi_stage_type { + IMGU_ABI_STAGE_TYPE_SP, + IMGU_ABI_STAGE_TYPE_ISP, +}; + +struct imgu_abi_acc_operation { + /* + * zero means on init, + * others mean upon receiving an ack signal from the BC acc. + */ + u8 op_indicator; + u8 op_type; +} __packed; + +struct imgu_abi_acc_process_lines_cmd_data { + u16 lines; + u8 cfg_set; + u8 reserved; /* Align to 4 bytes */ +} __packed; + +/* Bayer shading definitions */ + +struct imgu_abi_shd_transfer_luts_set_data { + u8 set_number; + u8 padding[3]; + imgu_addr_t rg_lut_ddr_addr; + imgu_addr_t bg_lut_ddr_addr; + u32 align_dummy; +} __packed; + +struct imgu_abi_shd_grid_config { + /* reg 0 */ + u32 grid_width:8; + u32 grid_height:8; + u32 block_width:3; + u32 reserved0:1; + u32 block_height:3; + u32 reserved1:1; + u32 grid_height_per_slice:8; + /* reg 1 */ + s32 x_start:13; + s32 reserved2:3; + s32 y_start:13; + s32 reserved3:3; +} __packed; + +struct imgu_abi_shd_general_config { + u32 init_set_vrt_offst_ul:8; + u32 shd_enable:1; + /* aka 'gf' */ + u32 gain_factor:2; + u32 reserved:21; +} __packed; + +struct imgu_abi_shd_black_level_config { + /* reg 0 */ + s32 bl_r:12; + s32 reserved0:4; + s32 bl_gr:12; + u32 reserved1:1; + /* aka 'nf' */ + u32 normalization_shift:3; + /* reg 1 */ + s32 bl_gb:12; + s32 reserved2:4; + s32 bl_b:12; + s32 reserved3:4; +} __packed; + +struct imgu_abi_shd_intra_frame_operations_data { + struct imgu_abi_acc_operation + operation_list[IMGU_ABI_SHD_MAX_OPERATIONS] __aligned(32); + struct imgu_abi_acc_process_lines_cmd_data + process_lines_data[IMGU_ABI_SHD_MAX_PROCESS_LINES] __aligned(32); + struct imgu_abi_shd_transfer_luts_set_data + transfer_data[IMGU_ABI_SHD_MAX_TRANSFERS] __aligned(32); +} __packed; + +struct imgu_abi_shd_config { + struct ipu3_uapi_shd_config_static shd __aligned(32); + struct imgu_abi_shd_intra_frame_operations_data shd_ops __aligned(32); + struct ipu3_uapi_shd_lut shd_lut __aligned(32); +} __packed; + +struct imgu_abi_stripe_input_frame_resolution { + u16 width; + u16 height; + u32 bayer_order; /* enum ipu3_uapi_bayer_order */ + u32 raw_bit_depth; +} __packed; + +/* Stripe-based processing */ + +struct imgu_abi_stripes { + /* offset from start of frame - measured in pixels */ + u16 offset; + /* stripe width - measured in pixels */ + u16 width; + /* stripe width - measured in pixels */ + u16 height; +} __packed; + +struct imgu_abi_stripe_data { + /* + * number of stripes for current processing source + * - VLIW binary parameter we currently support 1 or 2 stripes + */ + u16 num_of_stripes; + + u8 padding[2]; + + /* + * the following data is derived from resolution-related + * pipe config and from num_of_stripes + */ + + /* + *'input-stripes' - before input cropping + * used by input feeder + */ + struct imgu_abi_stripe_input_frame_resolution input_frame; + + /*'effective-stripes' - after input cropping used dpc, bds */ + struct imgu_abi_stripes effective_stripes[IPU3_UAPI_MAX_STRIPES]; + + /* 'down-scaled-stripes' - after down-scaling ONLY. used by BDS */ + struct imgu_abi_stripes down_scaled_stripes[IPU3_UAPI_MAX_STRIPES]; + + /* + *'bds-out-stripes' - after bayer down-scaling and padding. + * used by all algos starting with norm up to the ref-frame for GDC + * (currently up to the output kernel) + */ + struct imgu_abi_stripes bds_out_stripes[IPU3_UAPI_MAX_STRIPES]; + + /* 'bds-out-stripes (no overlap)' - used for ref kernel */ + struct imgu_abi_stripes + bds_out_stripes_no_overlap[IPU3_UAPI_MAX_STRIPES]; + + /* + * input resolution for output system (equal to bds_out - envelope) + * output-system input frame width as configured by user + */ + u16 output_system_in_frame_width; + /* output-system input frame height as configured by user */ + u16 output_system_in_frame_height; + + /* + * 'output-stripes' - accounts for stiching on the output (no overlap) + * used by the output kernel + */ + struct imgu_abi_stripes output_stripes[IPU3_UAPI_MAX_STRIPES]; + + /* + * 'block-stripes' - accounts for stiching by the output system + * (1 or more blocks overlap) + * used by DVS, TNR and the output system kernel + */ + struct imgu_abi_stripes block_stripes[IPU3_UAPI_MAX_STRIPES]; + + u16 effective_frame_width; /* Needed for vertical cropping */ + u16 bds_frame_width; + u16 out_frame_width; /* Output frame width as configured by user */ + u16 out_frame_height; /* Output frame height as configured by user */ + + /* GDC in buffer (A.K.A delay frame,ref buffer) info */ + u16 gdc_in_buffer_width; /* GDC in buffer width */ + u16 gdc_in_buffer_height; /* GDC in buffer height */ + /* GDC in buffer first valid pixel x offset */ + u16 gdc_in_buffer_offset_x; + /* GDC in buffer first valid pixel y offset */ + u16 gdc_in_buffer_offset_y; + + /* Display frame width as configured by user */ + u16 display_frame_width; + /* Display frame height as configured by user */ + u16 display_frame_height; + u16 bds_aligned_frame_width; + /* Number of vectors to left-crop when writing stripes (not stripe 0) */ + u16 half_overlap_vectors; + /* Decimate ISP and fixed func resolutions after BDS (ir_extraction) */ + u16 ir_ext_decimation; + u8 padding1[2]; +} __packed; + +/* Input feeder related structs */ + +struct imgu_abi_input_feeder_data { + u32 row_stride; /* row stride */ + u32 start_row_address; /* start row address */ + u32 start_pixel; /* start pixel */ +} __packed; + +struct imgu_abi_input_feeder_data_aligned { + struct imgu_abi_input_feeder_data data __aligned(32); +} __packed; + +struct imgu_abi_input_feeder_data_per_stripe { + struct imgu_abi_input_feeder_data_aligned + input_feeder_data[IPU3_UAPI_MAX_STRIPES]; +} __packed; + +struct imgu_abi_input_feeder_config { + struct imgu_abi_input_feeder_data data; + struct imgu_abi_input_feeder_data_per_stripe data_per_stripe + __aligned(32); +} __packed; + +/* DVS related definitions */ + +struct imgu_abi_dvs_stat_grd_config { + u8 grid_width; + u8 grid_height; + u8 block_width; + u8 block_height; + u16 x_start; + u16 y_start; + u16 enable; + u16 x_end; + u16 y_end; +} __packed; + +struct imgu_abi_dvs_stat_cfg { + u8 reserved0[4]; + struct imgu_abi_dvs_stat_grd_config + grd_config[IMGU_ABI_DVS_STAT_LEVELS]; + u8 reserved1[18]; +} __packed; + +struct imgu_abi_dvs_stat_transfer_op_data { + u8 set_number; +} __packed; + +struct imgu_abi_dvs_stat_intra_frame_operations_data { + struct imgu_abi_acc_operation + ops[IMGU_ABI_DVS_STAT_MAX_OPERATIONS] __aligned(32); + struct imgu_abi_acc_process_lines_cmd_data + process_lines_data[IMGU_ABI_DVS_STAT_MAX_PROCESS_LINES] + __aligned(32); + struct imgu_abi_dvs_stat_transfer_op_data + transfer_data[IMGU_ABI_DVS_STAT_MAX_TRANSFERS] __aligned(32); +} __packed; + +struct imgu_abi_dvs_stat_config { + struct imgu_abi_dvs_stat_cfg cfg __aligned(32); + u8 reserved0[128]; + struct imgu_abi_dvs_stat_intra_frame_operations_data operations_data; + u8 reserved1[64]; +} __packed; + +/* Y-tone Mapping */ + +struct imgu_abi_yuvp2_y_tm_lut_static_config { + u16 entries[IMGU_ABI_YUVP2_YTM_LUT_ENTRIES]; + u32 enable; +} __packed; + +/* Output formatter related structs */ + +struct imgu_abi_osys_formatter_params { + u32 format; + u32 flip; + u32 mirror; + u32 tiling; + u32 reduce_range; + u32 alpha_blending; + u32 release_inp_addr; + u32 release_inp_en; + u32 process_out_buf_addr; + u32 image_width_vecs; + u32 image_height_lines; + u32 inp_buff_y_st_addr; + u32 inp_buff_y_line_stride; + u32 inp_buff_y_buffer_stride; + u32 int_buff_u_st_addr; + u32 int_buff_v_st_addr; + u32 inp_buff_uv_line_stride; + u32 inp_buff_uv_buffer_stride; + u32 out_buff_level; + u32 out_buff_nr_y_lines; + u32 out_buff_u_st_offset; + u32 out_buff_v_st_offset; + u32 out_buff_y_line_stride; + u32 out_buff_uv_line_stride; + u32 hist_buff_st_addr; + u32 hist_buff_line_stride; + u32 hist_buff_nr_lines; +} __packed; + +struct imgu_abi_osys_formatter { + struct imgu_abi_osys_formatter_params param __aligned(32); +} __packed; + +struct imgu_abi_osys_scaler_params { + u32 inp_buf_y_st_addr; + u32 inp_buf_y_line_stride; + u32 inp_buf_y_buffer_stride; + u32 inp_buf_u_st_addr; + u32 inp_buf_v_st_addr; + u32 inp_buf_uv_line_stride; + u32 inp_buf_uv_buffer_stride; + u32 inp_buf_chunk_width; + u32 inp_buf_nr_buffers; + /* Output buffers */ + u32 out_buf_y_st_addr; + u32 out_buf_y_line_stride; + u32 out_buf_y_buffer_stride; + u32 out_buf_u_st_addr; + u32 out_buf_v_st_addr; + u32 out_buf_uv_line_stride; + u32 out_buf_uv_buffer_stride; + u32 out_buf_nr_buffers; + /* Intermediate buffers */ + u32 int_buf_y_st_addr; + u32 int_buf_y_line_stride; + u32 int_buf_u_st_addr; + u32 int_buf_v_st_addr; + u32 int_buf_uv_line_stride; + u32 int_buf_height; + u32 int_buf_chunk_width; + u32 int_buf_chunk_height; + /* Context buffers */ + u32 ctx_buf_hor_y_st_addr; + u32 ctx_buf_hor_u_st_addr; + u32 ctx_buf_hor_v_st_addr; + u32 ctx_buf_ver_y_st_addr; + u32 ctx_buf_ver_u_st_addr; + u32 ctx_buf_ver_v_st_addr; + /* Addresses for release-input and process-output tokens */ + u32 release_inp_buf_addr; + u32 release_inp_buf_en; + u32 release_out_buf_en; + u32 process_out_buf_addr; + /* Settings dimensions, padding, cropping */ + u32 input_image_y_width; + u32 input_image_y_height; + u32 input_image_y_start_column; + u32 input_image_uv_start_column; + u32 input_image_y_left_pad; + u32 input_image_uv_left_pad; + u32 input_image_y_right_pad; + u32 input_image_uv_right_pad; + u32 input_image_y_top_pad; + u32 input_image_uv_top_pad; + u32 input_image_y_bottom_pad; + u32 input_image_uv_bottom_pad; + u32 processing_mode; /* enum imgu_abi_osys_procmode */ + u32 scaling_ratio; + u32 y_left_phase_init; + u32 uv_left_phase_init; + u32 y_top_phase_init; + u32 uv_top_phase_init; + u32 coeffs_exp_shift; + u32 out_y_left_crop; + u32 out_uv_left_crop; + u32 out_y_top_crop; + u32 out_uv_top_crop; +} __packed; + +struct imgu_abi_osys_scaler { + struct imgu_abi_osys_scaler_params param __aligned(32); +} __packed; + +struct imgu_abi_osys_frame_params { + /* Output pins */ + u32 enable; + u32 format; /* enum imgu_abi_osys_format */ + u32 flip; + u32 mirror; + u32 tiling; /* enum imgu_abi_osys_tiling */ + u32 width; + u32 height; + u32 stride; + u32 scaled; +} __packed; + +struct imgu_abi_osys_frame { + struct imgu_abi_osys_frame_params param __aligned(32); +} __packed; + +struct imgu_abi_osys_stripe { + /* Input resolution */ + u32 input_width; + u32 input_height; + /* Output Stripe */ + u32 output_width[IMGU_ABI_OSYS_PINS]; + u32 output_height[IMGU_ABI_OSYS_PINS]; + u32 output_offset[IMGU_ABI_OSYS_PINS]; + u32 buf_stride[IMGU_ABI_OSYS_PINS]; + /* Scaler params */ + u32 block_width; + u32 block_height; + /* Output Crop factor */ + u32 crop_top[IMGU_ABI_OSYS_PINS]; + u32 crop_left[IMGU_ABI_OSYS_PINS]; +} __packed; + +struct imgu_abi_osys_config { + struct imgu_abi_osys_formatter + formatter[IPU3_UAPI_MAX_STRIPES][IMGU_ABI_OSYS_PINS]; + struct imgu_abi_osys_scaler scaler[IPU3_UAPI_MAX_STRIPES]; + struct imgu_abi_osys_frame frame[IMGU_ABI_OSYS_PINS]; + struct imgu_abi_osys_stripe stripe[IPU3_UAPI_MAX_STRIPES]; + /* 32 packed coefficients for luma and chroma */ + s8 scaler_coeffs_chroma[128]; + s8 scaler_coeffs_luma[128]; +} __packed; + +/* BDS */ + +struct imgu_abi_bds_hor_ctrl0 { + u32 sample_patrn_length:9; + u32 reserved0:3; + u32 hor_ds_en:1; + u32 min_clip_val:1; + u32 max_clip_val:2; + u32 out_frame_width:13; + u32 reserved1:3; +} __packed; + +struct imgu_abi_bds_ptrn_arr { + u32 elems[IMGU_ABI_BDS_SAMPLE_PATTERN_ARRAY_SIZE]; +} __packed; + +struct imgu_abi_bds_phase_entry { + s8 coeff_min2; + s8 coeff_min1; + s8 coeff_0; + s8 nf; + s8 coeff_pls1; + s8 coeff_pls2; + s8 coeff_pls3; + u8 reserved; +} __packed; + +struct imgu_abi_bds_phase_arr { + struct imgu_abi_bds_phase_entry + even[IMGU_ABI_BDS_PHASE_COEFFS_ARRAY_SIZE]; + struct imgu_abi_bds_phase_entry + odd[IMGU_ABI_BDS_PHASE_COEFFS_ARRAY_SIZE]; +} __packed; + +struct imgu_abi_bds_hor_ctrl1 { + u32 hor_crop_start:13; + u32 reserved0:3; + u32 hor_crop_end:13; + u32 reserved1:1; + u32 hor_crop_en:1; + u32 reserved2:1; +} __packed; + +struct imgu_abi_bds_hor_ctrl2 { + u32 input_frame_height:13; + u32 reserved0:19; +} __packed; + +struct imgu_abi_bds_hor { + struct imgu_abi_bds_hor_ctrl0 hor_ctrl0; + struct imgu_abi_bds_ptrn_arr hor_ptrn_arr; + struct imgu_abi_bds_phase_arr hor_phase_arr; + struct imgu_abi_bds_hor_ctrl1 hor_ctrl1; + struct imgu_abi_bds_hor_ctrl2 hor_ctrl2; +} __packed; + +struct imgu_abi_bds_ver_ctrl0 { + u32 sample_patrn_length:9; + u32 reserved0:3; + u32 ver_ds_en:1; + u32 min_clip_val:1; + u32 max_clip_val:2; + u32 reserved1:16; +} __packed; + +struct imgu_abi_bds_ver_ctrl1 { + u32 out_frame_width:13; + u32 reserved0:3; + u32 out_frame_height:13; + u32 reserved1:3; +} __packed; + +struct imgu_abi_bds_ver { + struct imgu_abi_bds_ver_ctrl0 ver_ctrl0; + struct imgu_abi_bds_ptrn_arr ver_ptrn_arr; + struct imgu_abi_bds_phase_arr ver_phase_arr; + struct imgu_abi_bds_ver_ctrl1 ver_ctrl1; +} __packed; + +struct imgu_abi_bds_per_stripe_data { + struct imgu_abi_bds_hor_ctrl0 hor_ctrl0; + struct imgu_abi_bds_ver_ctrl1 ver_ctrl1; + struct imgu_abi_bds_hor_ctrl1 crop; +} __packed; + +struct imgu_abi_bds_per_stripe_data_aligned { + struct imgu_abi_bds_per_stripe_data data __aligned(32); +} __packed; + +struct imgu_abi_bds_per_stripe { + struct imgu_abi_bds_per_stripe_data_aligned + aligned_data[IPU3_UAPI_MAX_STRIPES]; +} __packed; + +struct imgu_abi_bds_config { + struct imgu_abi_bds_hor hor __aligned(32); + struct imgu_abi_bds_ver ver __aligned(32); + struct imgu_abi_bds_per_stripe per_stripe __aligned(32); + u32 enabled; +} __packed; + +/* ANR */ + +struct imgu_abi_anr_search_config { + u32 enable; + u16 frame_width; + u16 frame_height; +} __packed; + +struct imgu_abi_anr_stitch_config { + u32 anr_stitch_en; + u16 frame_width; + u16 frame_height; + u8 reserved[40]; + struct ipu3_uapi_anr_stitch_pyramid pyramid[IPU3_UAPI_ANR_PYRAMID_SIZE]; +} __packed; + +struct imgu_abi_anr_tile2strm_config { + u32 enable; + u16 frame_width; + u16 frame_height; +} __packed; + +struct imgu_abi_anr_config { + struct imgu_abi_anr_search_config search __aligned(32); + struct ipu3_uapi_anr_transform_config transform __aligned(32); + struct imgu_abi_anr_stitch_config stitch __aligned(32); + struct imgu_abi_anr_tile2strm_config tile2strm __aligned(32); +} __packed; + +/* AF */ + +struct imgu_abi_af_frame_size { + u16 width; + u16 height; +} __packed; + +struct imgu_abi_af_config_s { + struct ipu3_uapi_af_filter_config filter_config __aligned(32); + struct imgu_abi_af_frame_size frame_size; + struct ipu3_uapi_grid_config grid_cfg __aligned(32); +} __packed; + +struct imgu_abi_af_intra_frame_operations_data { + struct imgu_abi_acc_operation ops[IMGU_ABI_AF_MAX_OPERATIONS] + __aligned(32); + struct imgu_abi_acc_process_lines_cmd_data + process_lines_data[IMGU_ABI_AF_MAX_PROCESS_LINES] __aligned(32); +} __packed; + +struct imgu_abi_af_stripe_config { + struct imgu_abi_af_frame_size frame_size __aligned(32); + struct ipu3_uapi_grid_config grid_cfg __aligned(32); +} __packed; + +struct imgu_abi_af_config { + struct imgu_abi_af_config_s config; + struct imgu_abi_af_intra_frame_operations_data operations_data; + struct imgu_abi_af_stripe_config stripes[IPU3_UAPI_MAX_STRIPES]; +} __packed; + +/* AE */ + +struct imgu_abi_ae_config { + struct ipu3_uapi_ae_grid_config grid_cfg __aligned(32); + struct ipu3_uapi_ae_weight_elem weights[IPU3_UAPI_AE_WEIGHTS] + __aligned(32); + struct ipu3_uapi_ae_ccm ae_ccm __aligned(32); + struct { + struct ipu3_uapi_ae_grid_config grid __aligned(32); + } stripes[IPU3_UAPI_MAX_STRIPES]; +} __packed; + +/* AWB_FR */ + +struct imgu_abi_awb_fr_intra_frame_operations_data { + struct imgu_abi_acc_operation ops[IMGU_ABI_AWB_FR_MAX_OPERATIONS] + __aligned(32); + struct imgu_abi_acc_process_lines_cmd_data + process_lines_data[IMGU_ABI_AWB_FR_MAX_PROCESS_LINES] __aligned(32); +} __packed; + +struct imgu_abi_awb_fr_config { + struct ipu3_uapi_awb_fr_config_s config; + struct imgu_abi_awb_fr_intra_frame_operations_data operations_data; + struct ipu3_uapi_awb_fr_config_s stripes[IPU3_UAPI_MAX_STRIPES]; +} __packed; + +struct imgu_abi_acc_transfer_op_data { + u8 set_number; +} __packed; + +struct imgu_abi_awb_intra_frame_operations_data { + struct imgu_abi_acc_operation ops[IMGU_ABI_AWB_MAX_OPERATIONS] + __aligned(32); + struct imgu_abi_acc_process_lines_cmd_data + process_lines_data[IMGU_ABI_AWB_MAX_PROCESS_LINES] __aligned(32); + struct imgu_abi_acc_transfer_op_data + transfer_data[IMGU_ABI_AWB_MAX_TRANSFERS] __aligned(32); +} __aligned(32) __packed; + +struct imgu_abi_awb_config { + struct ipu3_uapi_awb_config_s config __aligned(32); + struct imgu_abi_awb_intra_frame_operations_data operations_data; + struct ipu3_uapi_awb_config_s stripes[IPU3_UAPI_MAX_STRIPES]; +} __packed; + +struct imgu_abi_acc_param { + struct imgu_abi_stripe_data stripe; + u8 padding[8]; + struct imgu_abi_input_feeder_config input_feeder; + struct ipu3_uapi_bnr_static_config bnr; + struct ipu3_uapi_bnr_static_config_green_disparity green_disparity + __aligned(32); + struct ipu3_uapi_dm_config dm __aligned(32); + struct ipu3_uapi_ccm_mat_config ccm __aligned(32); + struct ipu3_uapi_gamma_config gamma __aligned(32); + struct ipu3_uapi_csc_mat_config csc __aligned(32); + struct ipu3_uapi_cds_params cds __aligned(32); + struct imgu_abi_shd_config shd __aligned(32); + struct imgu_abi_dvs_stat_config dvs_stat; + u8 padding1[224]; /* reserved for lace_stat */ + struct ipu3_uapi_yuvp1_iefd_config iefd __aligned(32); + struct ipu3_uapi_yuvp1_yds_config yds_c0 __aligned(32); + struct ipu3_uapi_yuvp1_chnr_config chnr_c0 __aligned(32); + struct ipu3_uapi_yuvp1_y_ee_nr_config y_ee_nr __aligned(32); + struct ipu3_uapi_yuvp1_yds_config yds __aligned(32); + struct ipu3_uapi_yuvp1_chnr_config chnr __aligned(32); + struct imgu_abi_yuvp2_y_tm_lut_static_config ytm __aligned(32); + struct ipu3_uapi_yuvp1_yds_config yds2 __aligned(32); + struct ipu3_uapi_yuvp2_tcc_static_config tcc __aligned(32); + /* reserved for defect pixel correction */ + u8 dpc[240832] __aligned(32); + struct imgu_abi_bds_config bds; + struct imgu_abi_anr_config anr; + struct imgu_abi_awb_fr_config awb_fr; + struct imgu_abi_ae_config ae; + struct imgu_abi_af_config af; + struct imgu_abi_awb_config awb; + struct imgu_abi_osys_config osys; +} __packed; + +/***** Morphing table entry *****/ + +struct imgu_abi_gdc_warp_param { + u32 origin_x; + u32 origin_y; + u32 in_addr_offset; + u32 in_block_width; + u32 in_block_height; + u32 p0_x; + u32 p0_y; + u32 p1_x; + u32 p1_y; + u32 p2_x; + u32 p2_y; + u32 p3_x; + u32 p3_y; + u32 in_block_width_a; + u32 in_block_width_b; + u32 padding; /* struct size multiple of DDR word */ +} __packed; + +/******************* Firmware ABI definitions *******************/ + +/***** struct imgu_abi_sp_stage *****/ + +struct imgu_abi_crop_pos { + u16 x; + u16 y; +} __packed; + +struct imgu_abi_sp_resolution { + u16 width; /* Width of valid data in pixels */ + u16 height; /* Height of valid data in lines */ +} __packed; + +/* + * Frame info struct. This describes the contents of an image frame buffer. + */ +struct imgu_abi_frame_sp_info { + struct imgu_abi_sp_resolution res; + u16 padded_width; /* stride of line in memory + * (in pixels) + */ + u8 format; /* format of the frame data */ + u8 raw_bit_depth; /* number of valid bits per pixel, + * only valid for RAW bayer frames + */ + u8 raw_bayer_order; /* bayer order, only valid + * for RAW bayer frames + */ + u8 raw_type; /* To choose the proper raw frame type. for + * Legacy SKC pipes/Default is set to + * IMGU_ABI_RAW_TYPE_BAYER. For RGB IR sensor - + * driver should set it to: + * IronGr case - IMGU_ABI_RAW_TYPE_IR_ON_GR + * IronGb case - IMGU_ABI_RAW_TYPE_IR_ON_GB + */ + u8 padding[2]; /* Extend to 32 bit multiple */ +} __packed; + +struct imgu_abi_buffer_sp { + union { + imgu_addr_t xmem_addr; + s32 queue_id; /* enum imgu_abi_queue_id */ + } buf_src; + s32 buf_type; /* enum imgu_abi_buffer_type */ +} __packed; + +struct imgu_abi_frame_sp_plane { + u32 offset; /* offset in bytes to start of frame data */ + /* offset is wrt data in imgu_abi_sp_sp_frame */ +} __packed; + +struct imgu_abi_frame_sp_rgb_planes { + struct imgu_abi_frame_sp_plane r; + struct imgu_abi_frame_sp_plane g; + struct imgu_abi_frame_sp_plane b; +} __packed; + +struct imgu_abi_frame_sp_yuv_planes { + struct imgu_abi_frame_sp_plane y; + struct imgu_abi_frame_sp_plane u; + struct imgu_abi_frame_sp_plane v; +} __packed; + +struct imgu_abi_frame_sp_nv_planes { + struct imgu_abi_frame_sp_plane y; + struct imgu_abi_frame_sp_plane uv; +} __packed; + +struct imgu_abi_frame_sp_plane6 { + struct imgu_abi_frame_sp_plane r; + struct imgu_abi_frame_sp_plane r_at_b; + struct imgu_abi_frame_sp_plane gr; + struct imgu_abi_frame_sp_plane gb; + struct imgu_abi_frame_sp_plane b; + struct imgu_abi_frame_sp_plane b_at_r; +} __packed; + +struct imgu_abi_frame_sp_binary_plane { + u32 size; + struct imgu_abi_frame_sp_plane data; +} __packed; + +struct imgu_abi_frame_sp { + struct imgu_abi_frame_sp_info info; + struct imgu_abi_buffer_sp buf_attr; + union { + struct imgu_abi_frame_sp_plane raw; + struct imgu_abi_frame_sp_plane rgb; + struct imgu_abi_frame_sp_rgb_planes planar_rgb; + struct imgu_abi_frame_sp_plane yuyv; + struct imgu_abi_frame_sp_yuv_planes yuv; + struct imgu_abi_frame_sp_nv_planes nv; + struct imgu_abi_frame_sp_plane6 plane6; + struct imgu_abi_frame_sp_binary_plane binary; + } planes; +} __packed; + +struct imgu_abi_resolution { + u32 width; + u32 height; +} __packed; + +struct imgu_abi_frames_sp { + struct imgu_abi_frame_sp in; + struct imgu_abi_frame_sp out[IMGU_ABI_BINARY_MAX_OUTPUT_PORTS]; + struct imgu_abi_resolution effective_in_res; + struct imgu_abi_frame_sp out_vf; + struct imgu_abi_frame_sp_info internal_frame_info; + struct imgu_abi_buffer_sp s3a_buf; + struct imgu_abi_buffer_sp dvs_buf; + struct imgu_abi_buffer_sp lace_buf; +} __packed; + +struct imgu_abi_uds_info { + u16 curr_dx; + u16 curr_dy; + u16 xc; + u16 yc; +} __packed; + +/* Information for a single pipeline stage */ +struct imgu_abi_sp_stage { + /* Multiple boolean flags can be stored in an integer */ + u8 num; /* Stage number */ + u8 isp_online; + u8 isp_copy_vf; + u8 isp_copy_output; + u8 sp_enable_xnr; + u8 isp_deci_log_factor; + u8 isp_vf_downscale_bits; + u8 deinterleaved; + /* + * NOTE: Programming the input circuit can only be done at the + * start of a session. It is illegal to program it during execution + * The input circuit defines the connectivity + */ + u8 program_input_circuit; + u8 func; + u8 stage_type; /* enum imgu_abi_stage_type */ + u8 num_stripes; + u8 isp_pipe_version; + struct { + u8 vf_output; + u8 s3a; + u8 sdis; + u8 dvs_stats; + u8 lace_stats; + } enable; + + struct imgu_abi_crop_pos sp_out_crop_pos; + u8 padding[2]; + struct imgu_abi_frames_sp frames; + struct imgu_abi_resolution dvs_envelope; + struct imgu_abi_uds_info uds; + imgu_addr_t isp_stage_addr; + imgu_addr_t xmem_bin_addr; + imgu_addr_t xmem_map_addr; + + u16 top_cropping; + u16 row_stripes_height; + u16 row_stripes_overlap_lines; + u8 if_config_index; /* Which should be applied by this stage. */ + u8 padding2; +} __packed; + +/***** struct imgu_abi_isp_stage *****/ + +struct imgu_abi_isp_param_memory_offsets { + u32 offsets[IMGU_ABI_PARAM_CLASS_NUM]; /* offset wrt hdr in bytes */ +} __packed; + +/* + * Blob descriptor. + * This structure describes an SP or ISP blob. + * It describes the test, data and bss sections as well as position in a + * firmware file. + * For convenience, it contains dynamic data after loading. + */ +struct imgu_abi_blob_info { + /* Static blob data */ + u32 offset; /* Blob offset in fw file */ + struct imgu_abi_isp_param_memory_offsets memory_offsets; + /* offset wrt hdr in bytes */ + u32 prog_name_offset; /* offset wrt hdr in bytes */ + u32 size; /* Size of blob */ + u32 padding_size; /* total cummulative of bytes added + * due to section alignment + */ + u32 icache_source; /* Position of icache in blob */ + u32 icache_size; /* Size of icache section */ + u32 icache_padding; /* added due to icache section alignment */ + u32 text_source; /* Position of text in blob */ + u32 text_size; /* Size of text section */ + u32 text_padding; /* bytes added due to text section alignment */ + u32 data_source; /* Position of data in blob */ + u32 data_target; /* Start of data in SP dmem */ + u32 data_size; /* Size of text section */ + u32 data_padding; /* bytes added due to data section alignment */ + u32 bss_target; /* Start position of bss in SP dmem */ + u32 bss_size; /* Size of bss section + * Dynamic data filled by loader + */ + u64 code __aligned(8); /* Code section absolute pointer */ + /* within fw, code = icache + text */ + u64 data __aligned(8); /* Data section absolute pointer */ + /* within fw, data = data + bss */ +} __packed; + +struct imgu_abi_binary_pipeline_info { + u32 mode; + u32 isp_pipe_version; + u32 pipelining; + u32 c_subsampling; + u32 top_cropping; + u32 left_cropping; + u32 variable_resolution; +} __packed; + +struct imgu_abi_binary_input_info { + u32 min_width; + u32 min_height; + u32 max_width; + u32 max_height; + u32 source; /* enum imgu_abi_bin_input_src */ +} __packed; + +struct imgu_abi_binary_output_info { + u32 min_width; + u32 min_height; + u32 max_width; + u32 max_height; + u32 num_chunks; + u32 variable_format; +} __packed; + +struct imgu_abi_binary_internal_info { + u32 max_width; + u32 max_height; +} __packed; + +struct imgu_abi_binary_bds_info { + u32 supported_bds_factors; +} __packed; + +struct imgu_abi_binary_dvs_info { + u32 max_envelope_width; + u32 max_envelope_height; +} __packed; + +struct imgu_abi_binary_vf_dec_info { + u32 is_variable; + u32 max_log_downscale; +} __packed; + +struct imgu_abi_binary_s3a_info { + u32 s3atbl_use_dmem; + u32 fixed_s3a_deci_log; +} __packed; + +struct imgu_abi_binary_dpc_info { + u32 bnr_lite; /* bnr lite enable flag */ +} __packed; + +struct imgu_abi_binary_iterator_info { + u32 num_stripes; + u32 row_stripes_height; + u32 row_stripes_overlap_lines; +} __packed; + +struct imgu_abi_binary_address_info { + u32 isp_addresses; /* Address in ISP dmem */ + u32 main_entry; /* Address of entry fct */ + u32 in_frame; /* Address in ISP dmem */ + u32 out_frame; /* Address in ISP dmem */ + u32 in_data; /* Address in ISP dmem */ + u32 out_data; /* Address in ISP dmem */ + u32 sh_dma_cmd_ptr; /* In ISP dmem */ +} __packed; + +struct imgu_abi_binary_uds_info { + u16 bpp; + u16 use_bci; + u16 use_str; + u16 woix; + u16 woiy; + u16 extra_out_vecs; + u16 vectors_per_line_in; + u16 vectors_per_line_out; + u16 vectors_c_per_line_in; + u16 vectors_c_per_line_out; + u16 vmem_gdc_in_block_height_y; + u16 vmem_gdc_in_block_height_c; +} __packed; + +struct imgu_abi_binary_block_info { + u32 block_width; + u32 block_height; + u32 output_block_height; +} __packed; + +struct imgu_abi_isp_data { + imgu_addr_t address; /* ISP address */ + u32 size; /* Disabled if 0 */ +} __packed; + +struct imgu_abi_isp_param_segments { + struct imgu_abi_isp_data + params[IMGU_ABI_PARAM_CLASS_NUM][IMGU_ABI_NUM_MEMORIES]; +} __packed; + +struct imgu_abi_binary_info { + u32 id __aligned(8); /* IMGU_ABI_BINARY_ID_* */ + struct imgu_abi_binary_pipeline_info pipeline; + struct imgu_abi_binary_input_info input; + struct imgu_abi_binary_output_info output; + struct imgu_abi_binary_internal_info internal; + struct imgu_abi_binary_bds_info bds; + struct imgu_abi_binary_dvs_info dvs; + struct imgu_abi_binary_vf_dec_info vf_dec; + struct imgu_abi_binary_s3a_info s3a; + struct imgu_abi_binary_dpc_info dpc_bnr; /* DPC related binary info */ + struct imgu_abi_binary_iterator_info iterator; + struct imgu_abi_binary_address_info addresses; + struct imgu_abi_binary_uds_info uds; + struct imgu_abi_binary_block_info block; + struct imgu_abi_isp_param_segments mem_initializers; + struct { + u8 input_feeder; + u8 output_system; + u8 obgrid; + u8 lin; + u8 dpc_acc; + u8 bds_acc; + u8 shd_acc; + u8 shd_ff; + u8 stats_3a_raw_buffer; + u8 acc_bayer_denoise; + u8 bnr_ff; + u8 awb_acc; + u8 awb_fr_acc; + u8 anr_acc; + u8 rgbpp_acc; + u8 rgbpp_ff; + u8 demosaic_acc; + u8 demosaic_ff; + u8 dvs_stats; + u8 lace_stats; + u8 yuvp1_b0_acc; + u8 yuvp1_c0_acc; + u8 yuvp2_acc; + u8 ae; + u8 af; + u8 dergb; + u8 rgb2yuv; + u8 high_quality; + u8 kerneltest; + u8 routing_shd_to_bnr; /* connect SHD with BNR ACCs */ + u8 routing_bnr_to_anr; /* connect BNR with ANR ACCs */ + u8 routing_anr_to_de; /* connect ANR with DE ACCs */ + u8 routing_rgb_to_yuvp1; /* connect RGB with YUVP1 */ + u8 routing_yuvp1_to_yuvp2; /* connect YUVP1 with YUVP2 */ + u8 luma_only; + u8 input_yuv; + u8 input_raw; + u8 reduced_pipe; + u8 vf_veceven; + u8 dis; + u8 dvs_envelope; + u8 uds; + u8 dvs_6axis; + u8 block_output; + u8 streaming_dma; + u8 ds; + u8 bayer_fir_6db; + u8 raw_binning; + u8 continuous; + u8 s3a; + u8 fpnr; + u8 sc; + u8 macc; + u8 output; + u8 ref_frame; + u8 tnr; + u8 xnr; + u8 params; + u8 ca_gdc; + u8 isp_addresses; + u8 in_frame; + u8 out_frame; + u8 high_speed; + u8 dpc; + u8 padding[2]; + u8 rgbir; + } enable; + struct { + u8 ref_y_channel; + u8 ref_c_channel; + u8 tnr_channel; + u8 tnr_out_channel; + u8 dvs_coords_channel; + u8 output_channel; + u8 c_channel; + u8 vfout_channel; + u8 vfout_c_channel; + u8 vfdec_bits_per_pixel; + u8 claimed_by_isp; + u8 padding[2]; + } dma; +} __packed; + +struct imgu_abi_isp_stage { + struct imgu_abi_blob_info blob_info; + struct imgu_abi_binary_info binary_info; + char binary_name[IMGU_ABI_MAX_BINARY_NAME]; + struct imgu_abi_isp_param_segments mem_initializers; +} __packed; + +/***** struct imgu_abi_ddr_address_map and parameter set *****/ + +/* xmem address map allocation */ +struct imgu_abi_ddr_address_map { + imgu_addr_t isp_mem_param[IMGU_ABI_MAX_STAGES][IMGU_ABI_NUM_MEMORIES]; + imgu_addr_t obgrid_tbl[IPU3_UAPI_MAX_STRIPES]; + imgu_addr_t acc_cluster_params_for_sp; + imgu_addr_t dvs_6axis_params_y; +} __packed; + +struct imgu_abi_parameter_set_info { + /* Pointers to Parameters in ISP format IMPT */ + struct imgu_abi_ddr_address_map mem_map; + /* Unique ID to track per-frame configurations */ + u32 isp_parameters_id; + /* Output frame to which this config has to be applied (optional) */ + imgu_addr_t output_frame_ptr; +} __packed; + +/***** struct imgu_abi_sp_group *****/ + +/* SP configuration information */ +struct imgu_abi_sp_config { + u8 no_isp_sync; /* Signal host immediately after start */ + u8 enable_raw_pool_locking; /* Enable Raw Buffer Locking for HALv3 */ + u8 lock_all; + u8 disable_cont_vf; + u8 disable_preview_on_capture; + u8 padding[3]; +} __packed; + +/* Information for a pipeline */ +struct imgu_abi_sp_pipeline { + u32 pipe_id; /* the pipe ID */ + u32 pipe_num; /* the dynamic pipe number */ + u32 thread_id; /* the sp thread ID */ + u32 pipe_config; /* the pipe config */ + u32 pipe_qos_config; /* Bitmap of multiple QOS extension fw + * state, 0xffffffff indicates non + * QOS pipe. + */ + u32 inout_port_config; + u32 required_bds_factor; + u32 dvs_frame_delay; + u32 num_stages; /* the pipe config */ + u32 running; /* needed for pipe termination */ + imgu_addr_t sp_stage_addr[IMGU_ABI_MAX_STAGES]; + imgu_addr_t scaler_pp_lut; /* Early bound LUT */ + u32 stage; /* stage ptr is only used on sp */ + s32 num_execs; /* number of times to run if this is + * an acceleration pipe. + */ + union { + struct { + u32 bytes_available; + } bin; + struct { + u32 height; + u32 width; + u32 padded_width; + u32 max_input_width; + u32 raw_bit_depth; + } raw; + } copy; + + /* Parameters passed to Shading Correction kernel. */ + struct { + /* Origin X (bqs) of internal frame on shading table */ + u32 internal_frame_origin_x_bqs_on_sctbl; + /* Origin Y (bqs) of internal frame on shading table */ + u32 internal_frame_origin_y_bqs_on_sctbl; + } shading; +} __packed; + +struct imgu_abi_sp_debug_command { + /* + * The DMA software-mask, + * Bit 31...24: unused. + * Bit 23...16: unused. + * Bit 15...08: reading-request enabling bits for DMA channel 7..0 + * Bit 07...00: writing-request enabling bits for DMA channel 7..0 + * + * For example, "0...0 0...0 11111011 11111101" indicates that the + * writing request through DMA Channel 1 and the reading request + * through DMA channel 2 are both disabled. The others are enabled. + */ + u32 dma_sw_reg; +} __packed; + +/* + * Group all host initialized SP variables into this struct. + * This is initialized every stage through dma. + * The stage part itself is transferred through imgu_abi_sp_stage. + */ +struct imgu_abi_sp_group { + struct imgu_abi_sp_config config; + struct imgu_abi_sp_pipeline pipe[IMGU_ABI_MAX_SP_THREADS]; + struct imgu_abi_sp_debug_command debug; +} __packed; + +/***** parameter and state class binary configurations *****/ + +struct imgu_abi_isp_iterator_config { + struct imgu_abi_frame_sp_info input_info; + struct imgu_abi_frame_sp_info internal_info; + struct imgu_abi_frame_sp_info output_info; + struct imgu_abi_frame_sp_info vf_info; + struct imgu_abi_sp_resolution dvs_envelope; +} __packed; + +struct imgu_abi_dma_port_config { + u8 crop, elems; + u16 width; + u32 stride; +} __packed; + +struct imgu_abi_isp_ref_config { + u32 width_a_over_b; + struct imgu_abi_dma_port_config port_b; + u32 ref_frame_addr_y[IMGU_ABI_FRAMES_REF]; + u32 ref_frame_addr_c[IMGU_ABI_FRAMES_REF]; + u32 dvs_frame_delay; +} __packed; + +struct imgu_abi_isp_ref_dmem_state { + u32 ref_in_buf_idx; + u32 ref_out_buf_idx; +} __packed; + +struct imgu_abi_isp_dvs_config { + u32 num_horizontal_blocks; + u32 num_vertical_blocks; +} __packed; + +struct imgu_abi_isp_tnr3_config { + u32 width_a_over_b; + u32 frame_height; + struct imgu_abi_dma_port_config port_b; + u32 delay_frame; + u32 frame_addr[IMGU_ABI_FRAMES_TNR]; +} __packed; + +struct imgu_abi_isp_tnr3_dmem_state { + u32 in_bufidx; + u32 out_bufidx; + u32 total_frame_counter; + u32 buffer_frame_counter[IMGU_ABI_BUF_SETS_TNR]; + u32 bypass_filter; +} __packed; + +/***** Queues *****/ + +struct imgu_abi_queue_info { + u8 size; /* the maximum number of elements*/ + u8 step; /* number of bytes per element */ + u8 start; /* index of the oldest element */ + u8 end; /* index at which to write the new element */ +} __packed; + +struct imgu_abi_queues { + /* + * Queues for the dynamic frame information, + * i.e. the "in_frame" buffer, the "out_frame" + * buffer and the "vf_out_frame" buffer. + */ + struct imgu_abi_queue_info host2sp_bufq_info + [IMGU_ABI_MAX_SP_THREADS][IMGU_ABI_QUEUE_NUM]; + u32 host2sp_bufq[IMGU_ABI_MAX_SP_THREADS][IMGU_ABI_QUEUE_NUM] + [IMGU_ABI_HOST2SP_BUFQ_SIZE]; + struct imgu_abi_queue_info sp2host_bufq_info[IMGU_ABI_QUEUE_NUM]; + u32 sp2host_bufq[IMGU_ABI_QUEUE_NUM][IMGU_ABI_SP2HOST_BUFQ_SIZE]; + + /* + * The queues for the events. + */ + struct imgu_abi_queue_info host2sp_evtq_info; + u32 host2sp_evtq[IMGU_ABI_HOST2SP_EVTQ_SIZE]; + struct imgu_abi_queue_info sp2host_evtq_info; + u32 sp2host_evtq[IMGU_ABI_SP2HOST_EVTQ_SIZE]; +} __packed; + +/***** Buffer descriptor *****/ + +struct imgu_abi_metadata_info { + struct imgu_abi_resolution resolution; /* Resolution */ + u32 stride; /* Stride in bytes */ + u32 size; /* Total size in bytes */ +} __packed; + +struct imgu_abi_isp_3a_statistics { + union { + struct { + imgu_addr_t s3a_tbl; + } dmem; + struct { + imgu_addr_t s3a_tbl_hi; + imgu_addr_t s3a_tbl_lo; + } vmem; + } data; + struct { + imgu_addr_t rgby_tbl; + } data_hmem; + u32 exp_id; /* exposure id, to match statistics to a frame, */ + u32 isp_config_id; /* Tracks per-frame configs */ + imgu_addr_t data_ptr; /* pointer to base of all data */ + u32 size; /* total size of all data */ + u32 dmem_size; + u32 vmem_size; /* both lo and hi have this size */ + u32 hmem_size; +} __packed; + +struct imgu_abi_metadata { + struct imgu_abi_metadata_info info; /* Layout info */ + imgu_addr_t address; /* CSS virtual address */ + u32 exp_id; /* Exposure ID */ +} __packed; + +struct imgu_abi_time_meas { + u32 start_timer_value; /* measured time in ticks */ + u32 end_timer_value; /* measured time in ticks */ +} __packed; + +struct imgu_abi_buffer { + union { + struct imgu_abi_isp_3a_statistics s3a; + u8 reserved[28]; + imgu_addr_t skc_dvs_statistics; + imgu_addr_t lace_stat; + struct imgu_abi_metadata metadata; + struct { + imgu_addr_t frame_data; + u32 flashed; + u32 exp_id; + u32 isp_parameters_id; /* Tracks per-frame configs */ + u32 padded_width; + } frame; + imgu_addr_t ddr_ptrs; + } payload; + /* + * kernel_ptr is present for host administration purposes only. + * type is uint64_t in order to be 64-bit host compatible. + * uint64_t does not exist on SP/ISP. + * Size of the struct is checked by sp.hive.c. + */ + u64 cookie_ptr __aligned(8); + u64 kernel_ptr; + struct imgu_abi_time_meas timing_data; + u32 isys_eof_clock_tick; +} __packed; + +struct imgu_abi_bl_dma_cmd_entry { + u32 src_addr; /* virtual DDR address */ + u32 size; /* number of bytes to transferred */ + u32 dst_type; + u32 dst_addr; /* hmm address of xMEM or MMIO */ +} __packed; + +struct imgu_abi_sp_init_dmem_cfg { + u32 ddr_data_addr; /* data segment address in ddr */ + u32 dmem_data_addr; /* data segment address in dmem */ + u32 dmem_bss_addr; /* bss segment address in dmem */ + u32 data_size; /* data segment size */ + u32 bss_size; /* bss segment size */ + u32 sp_id; /* sp id */ +} __packed; + +#endif diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.c b/drivers/staging/media/ipu3/ipu3-css-fw.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/mm.h> +#include <linux/slab.h> + +#include "ipu3-css.h" +#include "ipu3-css-fw.h" +#include "ipu3-dmamap.h" + +static void ipu3_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi, + const char *name) +{ + unsigned int i; + + dev_dbg(dev, "found firmware binary type %i size %i name %s\n", + bi->type, bi->blob.size, name); + if (bi->type != IMGU_FW_ISP_FIRMWARE) + return; + + dev_dbg(dev, " id %i mode %i bds 0x%x veceven %i/%i out_pins %i\n", + bi->info.isp.sp.id, bi->info.isp.sp.pipeline.mode, + bi->info.isp.sp.bds.supported_bds_factors, + bi->info.isp.sp.enable.vf_veceven, + bi->info.isp.sp.vf_dec.is_variable, + bi->info.isp.num_output_pins); + + dev_dbg(dev, " input (%i,%i)-(%i,%i) formats %s%s%s\n", + bi->info.isp.sp.input.min_width, + bi->info.isp.sp.input.min_height, + bi->info.isp.sp.input.max_width, + bi->info.isp.sp.input.max_height, + bi->info.isp.sp.enable.input_yuv ? "yuv420 " : "", + bi->info.isp.sp.enable.input_feeder || + bi->info.isp.sp.enable.input_raw ? "raw8 raw10 " : "", + bi->info.isp.sp.enable.input_raw ? "raw12" : ""); + + dev_dbg(dev, " internal (%i,%i)\n", + bi->info.isp.sp.internal.max_width, + bi->info.isp.sp.internal.max_height); + + dev_dbg(dev, " output (%i,%i)-(%i,%i) formats", + bi->info.isp.sp.output.min_width, + bi->info.isp.sp.output.min_height, + bi->info.isp.sp.output.max_width, + bi->info.isp.sp.output.max_height); + for (i = 0; i < bi->info.isp.num_output_formats; i++) + dev_dbg(dev, " %i", bi->info.isp.output_formats[i]); + dev_dbg(dev, " vf"); + for (i = 0; i < bi->info.isp.num_vf_formats; i++) + dev_dbg(dev, " %i", bi->info.isp.vf_formats[i]); + dev_dbg(dev, "\n"); +} + +unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi) +{ + unsigned int width = DIV_ROUND_UP(bi->info.isp.sp.internal.max_width, + IMGU_OBGRID_TILE_SIZE * 2) + 1; + unsigned int height = DIV_ROUND_UP(bi->info.isp.sp.internal.max_height, + IMGU_OBGRID_TILE_SIZE * 2) + 1; + unsigned int obgrid_size; + + width = ALIGN(width, IPU3_UAPI_ISP_VEC_ELEMS / 4); + obgrid_size = PAGE_ALIGN(width * height * + sizeof(struct ipu3_uapi_obgrid_param)) * + bi->info.isp.sp.iterator.num_stripes; + return obgrid_size; +} + +void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe, + enum imgu_abi_param_class cls, + enum imgu_abi_memories mem, + struct imgu_fw_isp_parameter *par, + size_t par_size, void *binary_params) +{ + struct imgu_fw_info *bi = + &css->fwp->binary_header[css->pipes[pipe].bindex]; + + if (par->offset + par->size > + bi->info.isp.sp.mem_initializers.params[cls][mem].size) + return NULL; + + if (par->size != par_size) + pr_warn("parameter size doesn't match defined size\n"); + + if (par->size < par_size) + return NULL; + + return binary_params + par->offset; +} + +void ipu3_css_fw_cleanup(struct ipu3_css *css) +{ + struct imgu_device *imgu = dev_get_drvdata(css->dev); + + if (css->binary) { + unsigned int i; + + for (i = 0; i < css->fwp->file_header.binary_nr; i++) + ipu3_dmamap_free(imgu, &css->binary[i]); + kfree(css->binary); + } + if (css->fw) + release_firmware(css->fw); + + css->binary = NULL; + css->fw = NULL; +} + +int ipu3_css_fw_init(struct ipu3_css *css) +{ + static const u32 BLOCK_MAX = 65536; + struct imgu_device *imgu = dev_get_drvdata(css->dev); + struct device *dev = css->dev; + unsigned int i, j, binary_nr; + int r; + + r = request_firmware(&css->fw, IMGU_FW_NAME, css->dev); + if (r) + return r; + + /* Check and display fw header info */ + + css->fwp = (struct imgu_fw_header *)css->fw->data; + if (css->fw->size < sizeof(struct imgu_fw_header *) || + css->fwp->file_header.h_size != sizeof(struct imgu_fw_bi_file_h)) + goto bad_fw; + if (sizeof(struct imgu_fw_bi_file_h) + + css->fwp->file_header.binary_nr * sizeof(struct imgu_fw_info) > + css->fw->size) + goto bad_fw; + + dev_info(dev, "loaded firmware version %.64s, %u binaries, %zu bytes\n", + css->fwp->file_header.version, css->fwp->file_header.binary_nr, + css->fw->size); + + /* Validate and display info on fw binaries */ + + binary_nr = css->fwp->file_header.binary_nr; + + css->fw_bl = -1; + css->fw_sp[0] = -1; + css->fw_sp[1] = -1; + + for (i = 0; i < binary_nr; i++) { + struct imgu_fw_info *bi = &css->fwp->binary_header[i]; + const char *name = (void *)css->fwp + bi->blob.prog_name_offset; + size_t len; + + if (bi->blob.prog_name_offset >= css->fw->size) + goto bad_fw; + len = strnlen(name, css->fw->size - bi->blob.prog_name_offset); + if (len + 1 > css->fw->size - bi->blob.prog_name_offset || + len + 1 >= IMGU_ABI_MAX_BINARY_NAME) + goto bad_fw; + + if (bi->blob.size != bi->blob.text_size + bi->blob.icache_size + + bi->blob.data_size + bi->blob.padding_size) + goto bad_fw; + if (bi->blob.offset + bi->blob.size > css->fw->size) + goto bad_fw; + + if (bi->type == IMGU_FW_BOOTLOADER_FIRMWARE) { + css->fw_bl = i; + if (bi->info.bl.sw_state >= css->iomem_length || + bi->info.bl.num_dma_cmds >= css->iomem_length || + bi->info.bl.dma_cmd_list >= css->iomem_length) + goto bad_fw; + } + if (bi->type == IMGU_FW_SP_FIRMWARE || + bi->type == IMGU_FW_SP1_FIRMWARE) { + css->fw_sp[bi->type == IMGU_FW_SP_FIRMWARE ? 0 : 1] = i; + if (bi->info.sp.per_frame_data >= css->iomem_length || + bi->info.sp.init_dmem_data >= css->iomem_length || + bi->info.sp.host_sp_queue >= css->iomem_length || + bi->info.sp.isp_started >= css->iomem_length || + bi->info.sp.sw_state >= css->iomem_length || + bi->info.sp.sleep_mode >= css->iomem_length || + bi->info.sp.invalidate_tlb >= css->iomem_length || + bi->info.sp.host_sp_com >= css->iomem_length || + bi->info.sp.output + 12 >= css->iomem_length || + bi->info.sp.host_sp_queues_initialized >= + css->iomem_length) + goto bad_fw; + } + if (bi->type != IMGU_FW_ISP_FIRMWARE) + continue; + + if (bi->info.isp.sp.pipeline.mode >= IPU3_CSS_PIPE_ID_NUM) + goto bad_fw; + + if (bi->info.isp.sp.iterator.num_stripes > + IPU3_UAPI_MAX_STRIPES) + goto bad_fw; + + if (bi->info.isp.num_vf_formats > IMGU_ABI_FRAME_FORMAT_NUM || + bi->info.isp.num_output_formats > IMGU_ABI_FRAME_FORMAT_NUM) + goto bad_fw; + + for (j = 0; j < bi->info.isp.num_output_formats; j++) + if (bi->info.isp.output_formats[j] < 0 || + bi->info.isp.output_formats[j] >= + IMGU_ABI_FRAME_FORMAT_NUM) + goto bad_fw; + for (j = 0; j < bi->info.isp.num_vf_formats; j++) + if (bi->info.isp.vf_formats[j] < 0 || + bi->info.isp.vf_formats[j] >= + IMGU_ABI_FRAME_FORMAT_NUM) + goto bad_fw; + + if (bi->info.isp.sp.block.block_width <= 0 || + bi->info.isp.sp.block.block_width > BLOCK_MAX || + bi->info.isp.sp.block.output_block_height <= 0 || + bi->info.isp.sp.block.output_block_height > BLOCK_MAX) + goto bad_fw; + + if (bi->blob.memory_offsets.offsets[IMGU_ABI_PARAM_CLASS_PARAM] + + sizeof(struct imgu_fw_param_memory_offsets) > + css->fw->size || + bi->blob.memory_offsets.offsets[IMGU_ABI_PARAM_CLASS_CONFIG] + + sizeof(struct imgu_fw_config_memory_offsets) > + css->fw->size || + bi->blob.memory_offsets.offsets[IMGU_ABI_PARAM_CLASS_STATE] + + sizeof(struct imgu_fw_state_memory_offsets) > + css->fw->size) + goto bad_fw; + + ipu3_css_fw_show_binary(dev, bi, name); + } + + if (css->fw_bl == -1 || css->fw_sp[0] == -1 || css->fw_sp[1] == -1) + goto bad_fw; + + /* Allocate and map fw binaries into IMGU */ + + css->binary = kcalloc(binary_nr, sizeof(*css->binary), GFP_KERNEL); + if (!css->binary) { + r = -ENOMEM; + goto error_out; + } + + for (i = 0; i < css->fwp->file_header.binary_nr; i++) { + struct imgu_fw_info *bi = &css->fwp->binary_header[i]; + void *blob = (void *)css->fwp + bi->blob.offset; + size_t size = bi->blob.size; + + if (!ipu3_dmamap_alloc(imgu, &css->binary[i], size)) { + r = -ENOMEM; + goto error_out; + } + memcpy(css->binary[i].vaddr, blob, size); + } + + return 0; + +bad_fw: + dev_err(dev, "invalid firmware binary, size %u\n", (int)css->fw->size); + r = -ENODEV; + +error_out: + ipu3_css_fw_cleanup(css); + return r; +} diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.h b/drivers/staging/media/ipu3/ipu3-css-fw.h @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ + +#ifndef __IPU3_CSS_FW_H +#define __IPU3_CSS_FW_H + +/******************* Firmware file definitions *******************/ + +#define IMGU_FW_NAME "intel/ipu3-fw.bin" + +typedef u32 imgu_fw_ptr; + +enum imgu_fw_type { + IMGU_FW_SP_FIRMWARE, /* Firmware for the SP */ + IMGU_FW_SP1_FIRMWARE, /* Firmware for the SP1 */ + IMGU_FW_ISP_FIRMWARE, /* Firmware for the ISP */ + IMGU_FW_BOOTLOADER_FIRMWARE, /* Firmware for the BootLoader */ + IMGU_FW_ACC_FIRMWARE /* Firmware for accelerations */ +}; + +enum imgu_fw_acc_type { + IMGU_FW_ACC_NONE, /* Normal binary */ + IMGU_FW_ACC_OUTPUT, /* Accelerator stage on output frame */ + IMGU_FW_ACC_VIEWFINDER, /* Accelerator stage on viewfinder frame */ + IMGU_FW_ACC_STANDALONE, /* Stand-alone acceleration */ +}; + +struct imgu_fw_isp_parameter { + u32 offset; /* Offset in isp_<mem> config, params, etc. */ + u32 size; /* Disabled if 0 */ +}; + +struct imgu_fw_param_memory_offsets { + struct { + struct imgu_fw_isp_parameter lin; /* lin_vmem_params */ + struct imgu_fw_isp_parameter tnr3; /* tnr3_vmem_params */ + struct imgu_fw_isp_parameter xnr3; /* xnr3_vmem_params */ + } vmem; + struct { + struct imgu_fw_isp_parameter tnr; + struct imgu_fw_isp_parameter tnr3; /* tnr3_params */ + struct imgu_fw_isp_parameter xnr3; /* xnr3_params */ + struct imgu_fw_isp_parameter plane_io_config; /* 192 bytes */ + struct imgu_fw_isp_parameter rgbir; /* rgbir_params */ + } dmem; +}; + +struct imgu_fw_config_memory_offsets { + struct { + struct imgu_fw_isp_parameter iterator; + struct imgu_fw_isp_parameter dvs; + struct imgu_fw_isp_parameter output; + struct imgu_fw_isp_parameter raw; + struct imgu_fw_isp_parameter input_yuv; + struct imgu_fw_isp_parameter tnr; + struct imgu_fw_isp_parameter tnr3; + struct imgu_fw_isp_parameter ref; + } dmem; +}; + +struct imgu_fw_state_memory_offsets { + struct { + struct imgu_fw_isp_parameter tnr; + struct imgu_fw_isp_parameter tnr3; + struct imgu_fw_isp_parameter ref; + } dmem; +}; + +union imgu_fw_all_memory_offsets { + struct { + u64 imgu_fw_mem_offsets[3]; /* params, config, state */ + } offsets; + struct { + u64 ptr; + } array[IMGU_ABI_PARAM_CLASS_NUM]; +}; + +struct imgu_fw_binary_xinfo { + /* Part that is of interest to the SP. */ + struct imgu_abi_binary_info sp; + + /* Rest of the binary info, only interesting to the host. */ + u32 type; /* enum imgu_fw_acc_type */ + + u32 num_output_formats __aligned(8); + u32 output_formats[IMGU_ABI_FRAME_FORMAT_NUM]; /* enum frame_format */ + + /* number of supported vf formats */ + u32 num_vf_formats __aligned(8); + /* types of supported vf formats */ + u32 vf_formats[IMGU_ABI_FRAME_FORMAT_NUM]; /* enum frame_format */ + u8 num_output_pins; + imgu_fw_ptr xmem_addr; + + u64 imgu_fw_blob_descr_ptr __aligned(8); + u32 blob_index __aligned(8); + union imgu_fw_all_memory_offsets mem_offsets __aligned(8); + struct imgu_fw_binary_xinfo *next __aligned(8); +}; + +struct imgu_fw_sp_info { + u32 init_dmem_data; /* data sect config, stored to dmem */ + u32 per_frame_data; /* Per frame data, stored to dmem */ + u32 group; /* Per pipeline data, loaded by dma */ + u32 output; /* SP output data, loaded by dmem */ + u32 host_sp_queue; /* Host <-> SP queues */ + u32 host_sp_com; /* Host <-> SP commands */ + u32 isp_started; /* P'ed from sensor thread, csim only */ + u32 sw_state; /* Polled from css, enum imgu_abi_sp_swstate */ + u32 host_sp_queues_initialized; /* Polled from the SP */ + u32 sleep_mode; /* different mode to halt SP */ + u32 invalidate_tlb; /* inform SP to invalidate mmu TLB */ + u32 debug_buffer_ddr_address; /* the addr of DDR debug queue */ + + /* input system perf count array */ + u32 perf_counter_input_system_error; + u32 threads_stack; /* sp thread's stack pointers */ + u32 threads_stack_size; /* sp thread's stack sizes */ + u32 curr_binary_id; /* current binary id */ + u32 raw_copy_line_count; /* raw copy line counter */ + u32 ddr_parameter_address; /* acc param ddrptr, sp dmem */ + u32 ddr_parameter_size; /* acc param size, sp dmem */ + /* Entry functions */ + u32 sp_entry; /* The SP entry function */ + u32 tagger_frames_addr; /* Base address of tagger state */ +}; + +struct imgu_fw_bl_info { + u32 num_dma_cmds; /* Number of cmds sent by CSS */ + u32 dma_cmd_list; /* Dma command list sent by CSS */ + u32 sw_state; /* Polled from css, enum imgu_abi_bl_swstate */ + /* Entry functions */ + u32 bl_entry; /* The SP entry function */ +}; + +struct imgu_fw_acc_info { + u32 per_frame_data; /* Dummy for now */ +}; + +union imgu_fw_union { + struct imgu_fw_binary_xinfo isp; /* ISP info */ + struct imgu_fw_sp_info sp; /* SP info */ + struct imgu_fw_sp_info sp1; /* SP1 info */ + struct imgu_fw_bl_info bl; /* Bootloader info */ + struct imgu_fw_acc_info acc; /* Accelerator info */ +}; + +struct imgu_fw_info { + size_t header_size; /* size of fw header */ + u32 type __aligned(8); /* enum imgu_fw_type */ + union imgu_fw_union info; /* Binary info */ + struct imgu_abi_blob_info blob; /* Blob info */ + /* Dynamic part */ + u64 next; + + u32 loaded __aligned(8); /* Firmware has been loaded */ + const u64 isp_code __aligned(8); /* ISP pointer to code */ + /* Firmware handle between user space and kernel */ + u32 handle __aligned(8); + /* Sections to copy from/to ISP */ + struct imgu_abi_isp_param_segments mem_initializers; + /* Initializer for local ISP memories */ +}; + +struct imgu_fw_bi_file_h { + char version[64]; /* branch tag + week day + time */ + int binary_nr; /* Number of binaries */ + unsigned int h_size; /* sizeof(struct imgu_fw_bi_file_h) */ +}; + +struct imgu_fw_header { + struct imgu_fw_bi_file_h file_header; + struct imgu_fw_info binary_header[1]; /* binary_nr items */ +}; + +/******************* Firmware functions *******************/ + +int ipu3_css_fw_init(struct ipu3_css *css); +void ipu3_css_fw_cleanup(struct ipu3_css *css); + +unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi); +void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe, + enum imgu_abi_param_class cls, + enum imgu_abi_memories mem, + struct imgu_fw_isp_parameter *par, + size_t par_size, void *binary_params); + +#endif diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c @@ -0,0 +1,2943 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include <linux/device.h> + +#include "ipu3-css.h" +#include "ipu3-css-fw.h" +#include "ipu3-tables.h" + +#define DIV_ROUND_CLOSEST_DOWN(a, b) (((a) + ((b) / 2) - 1) / (b)) +#define roundclosest_down(a, b) (DIV_ROUND_CLOSEST_DOWN(a, b) * (b)) + +#define IPU3_UAPI_ANR_MAX_RESET ((1 << 12) - 1) +#define IPU3_UAPI_ANR_MIN_RESET (((-1) << 12) + 1) + +struct ipu3_css_scaler_info { + unsigned int phase_step; /* Same for luma/chroma */ + int exp_shift; + + unsigned int phase_init; /* luma/chroma dependent */ + int pad_left; + int pad_right; + int crop_left; + int crop_top; +}; + +static unsigned int ipu3_css_scaler_get_exp(unsigned int counter, + unsigned int divider) +{ + int i = fls(divider) - fls(counter); + + if (i <= 0) + return 0; + + if (divider >> i < counter) + i = i - 1; + + return i; +} + +/* Set up the CSS scaler look up table */ +static void +ipu3_css_scaler_setup_lut(unsigned int taps, unsigned int input_width, + unsigned int output_width, int phase_step_correction, + const int *coeffs, unsigned int coeffs_size, + s8 coeff_lut[], struct ipu3_css_scaler_info *info) +{ + int tap, phase, phase_sum_left, phase_sum_right; + int exponent = ipu3_css_scaler_get_exp(output_width, input_width); + int mantissa = (1 << exponent) * output_width; + unsigned int phase_step; + + if (input_width == output_width) { + for (phase = 0; phase < IMGU_SCALER_PHASES; phase++) { + for (tap = 0; tap < taps; tap++) { + coeff_lut[phase * IMGU_SCALER_FILTER_TAPS + tap] + = 0; + } + } + + info->phase_step = IMGU_SCALER_PHASES * + (1 << IMGU_SCALER_PHASE_COUNTER_PREC_REF); + info->exp_shift = 0; + info->pad_left = 0; + info->pad_right = 0; + info->phase_init = 0; + info->crop_left = 0; + info->crop_top = 0; + return; + } + + for (phase = 0; phase < IMGU_SCALER_PHASES; phase++) { + for (tap = 0; tap < taps; tap++) { + /* flip table to for convolution reverse indexing */ + s64 coeff = coeffs[coeffs_size - + ((tap * (coeffs_size / taps)) + phase) - 1]; + coeff *= mantissa; + coeff = div64_long(coeff, input_width); + + /* Add +"0.5" */ + coeff += 1 << (IMGU_SCALER_COEFF_BITS - 1); + coeff >>= IMGU_SCALER_COEFF_BITS; + + coeff_lut[phase * IMGU_SCALER_FILTER_TAPS + tap] = + coeff; + } + } + + phase_step = IMGU_SCALER_PHASES * + (1 << IMGU_SCALER_PHASE_COUNTER_PREC_REF) * + output_width / input_width; + phase_step += phase_step_correction; + phase_sum_left = (taps / 2 * IMGU_SCALER_PHASES * + (1 << IMGU_SCALER_PHASE_COUNTER_PREC_REF)) - + (1 << (IMGU_SCALER_PHASE_COUNTER_PREC_REF - 1)); + phase_sum_right = (taps / 2 * IMGU_SCALER_PHASES * + (1 << IMGU_SCALER_PHASE_COUNTER_PREC_REF)) + + (1 << (IMGU_SCALER_PHASE_COUNTER_PREC_REF - 1)); + + info->exp_shift = IMGU_SCALER_MAX_EXPONENT_SHIFT - exponent; + info->pad_left = (phase_sum_left % phase_step == 0) ? + phase_sum_left / phase_step - 1 : phase_sum_left / phase_step; + info->pad_right = (phase_sum_right % phase_step == 0) ? + phase_sum_right / phase_step - 1 : phase_sum_right / phase_step; + info->phase_init = phase_sum_left - phase_step * info->pad_left; + info->phase_step = phase_step; + info->crop_left = taps - 1; + info->crop_top = taps - 1; +} + +/* + * Calculates the exact output image width/height, based on phase_step setting + * (must be perfectly aligned with hardware). + */ +static unsigned int +ipu3_css_scaler_calc_scaled_output(unsigned int input, + struct ipu3_css_scaler_info *info) +{ + unsigned int arg1 = input * info->phase_step + + (1 - IMGU_SCALER_TAPS_Y / 2) * IMGU_SCALER_FIR_PHASES - + IMGU_SCALER_FIR_PHASES / (2 * IMGU_SCALER_PHASES); + unsigned int arg2 = ((IMGU_SCALER_TAPS_Y / 2) * IMGU_SCALER_FIR_PHASES + + IMGU_SCALER_FIR_PHASES / (2 * IMGU_SCALER_PHASES)) * + IMGU_SCALER_FIR_PHASES + info->phase_step / 2; + + return ((arg1 + (arg2 - IMGU_SCALER_FIR_PHASES * info->phase_step) / + IMGU_SCALER_FIR_PHASES) / (2 * IMGU_SCALER_FIR_PHASES)) * 2; +} + +/* + * Calculate the output width and height, given the luma + * and chroma details of a scaler + */ +static void +ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, + u32 target_height, struct imgu_abi_osys_config *cfg, + struct ipu3_css_scaler_info *info_luma, + struct ipu3_css_scaler_info *info_chroma, + unsigned int *output_width, unsigned int *output_height, + unsigned int *procmode) +{ + u32 out_width = target_width; + u32 out_height = target_height; + const unsigned int height_alignment = 2; + int phase_step_correction = -1; + + /* + * Calculate scaled output width. If the horizontal and vertical scaling + * factor is different, then choose the biggest and crop off excess + * lines or columns after formatting. + */ + if (target_height * input_width > target_width * input_height) + target_width = DIV_ROUND_UP(target_height * input_width, + input_height); + + if (input_width == target_width) + *procmode = IMGU_ABI_OSYS_PROCMODE_BYPASS; + else + *procmode = IMGU_ABI_OSYS_PROCMODE_DOWNSCALE; + + memset(&cfg->scaler_coeffs_chroma, 0, + sizeof(cfg->scaler_coeffs_chroma)); + memset(&cfg->scaler_coeffs_luma, 0, sizeof(*cfg->scaler_coeffs_luma)); + do { + phase_step_correction++; + + ipu3_css_scaler_setup_lut(IMGU_SCALER_TAPS_Y, + input_width, target_width, + phase_step_correction, + ipu3_css_downscale_4taps, + IMGU_SCALER_DOWNSCALE_4TAPS_LEN, + cfg->scaler_coeffs_luma, info_luma); + + ipu3_css_scaler_setup_lut(IMGU_SCALER_TAPS_UV, + input_width, target_width, + phase_step_correction, + ipu3_css_downscale_2taps, + IMGU_SCALER_DOWNSCALE_2TAPS_LEN, + cfg->scaler_coeffs_chroma, + info_chroma); + + out_width = ipu3_css_scaler_calc_scaled_output(input_width, + info_luma); + out_height = ipu3_css_scaler_calc_scaled_output(input_height, + info_luma); + } while ((out_width < target_width || out_height < target_height || + !IS_ALIGNED(out_height, height_alignment)) && + phase_step_correction <= 5); + + *output_width = out_width; + *output_height = out_height; +} + +/********************** Osys routines for scaler****************************/ + +static void ipu3_css_osys_set_format(enum imgu_abi_frame_format host_format, + unsigned int *osys_format, + unsigned int *osys_tiling) +{ + *osys_format = IMGU_ABI_OSYS_FORMAT_YUV420; + *osys_tiling = IMGU_ABI_OSYS_TILING_NONE; + + switch (host_format) { + case IMGU_ABI_FRAME_FORMAT_YUV420: + *osys_format = IMGU_ABI_OSYS_FORMAT_YUV420; + break; + case IMGU_ABI_FRAME_FORMAT_YV12: + *osys_format = IMGU_ABI_OSYS_FORMAT_YV12; + break; + case IMGU_ABI_FRAME_FORMAT_NV12: + *osys_format = IMGU_ABI_OSYS_FORMAT_NV12; + break; + case IMGU_ABI_FRAME_FORMAT_NV16: + *osys_format = IMGU_ABI_OSYS_FORMAT_NV16; + break; + case IMGU_ABI_FRAME_FORMAT_NV21: + *osys_format = IMGU_ABI_OSYS_FORMAT_NV21; + break; + case IMGU_ABI_FRAME_FORMAT_NV12_TILEY: + *osys_format = IMGU_ABI_OSYS_FORMAT_NV12; + *osys_tiling = IMGU_ABI_OSYS_TILING_Y; + break; + default: + /* For now, assume use default values */ + break; + } +} + +/* + * Function calculates input frame stripe offset, based + * on output frame stripe offset and filter parameters. + */ +static int ipu3_css_osys_calc_stripe_offset(int stripe_offset_out, + int fir_phases, int phase_init, + int phase_step, int pad_left) +{ + int stripe_offset_inp = stripe_offset_out * fir_phases - + pad_left * phase_step; + + return DIV_ROUND_UP(stripe_offset_inp - phase_init, phase_step); +} + +/* + * Calculate input frame phase, given the output frame + * stripe offset and filter parameters + */ +static int ipu3_css_osys_calc_stripe_phase_init(int stripe_offset_out, + int fir_phases, int phase_init, + int phase_step, int pad_left) +{ + int stripe_offset_inp = + ipu3_css_osys_calc_stripe_offset(stripe_offset_out, + fir_phases, phase_init, + phase_step, pad_left); + + return phase_init + ((pad_left + stripe_offset_inp) * phase_step) - + stripe_offset_out * fir_phases; +} + +/* + * This function calculates input frame stripe width, + * based on output frame stripe offset and filter parameters + */ +static int ipu3_css_osys_calc_inp_stripe_width(int stripe_width_out, + int fir_phases, int phase_init, + int phase_step, int fir_taps, + int pad_left, int pad_right) +{ + int stripe_width_inp = (stripe_width_out + fir_taps - 1) * fir_phases; + + stripe_width_inp = DIV_ROUND_UP(stripe_width_inp - phase_init, + phase_step); + + return stripe_width_inp - pad_left - pad_right; +} + +/* + * This function calculates output frame stripe width, basedi + * on output frame stripe offset and filter parameters + */ +static int ipu3_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases, + int phase_init, int phase_step, + int fir_taps, int pad_left, + int pad_right, int column_offset) +{ + int stripe_width_out = (pad_left + stripe_width_inp + + pad_right - column_offset) * phase_step; + + stripe_width_out = (stripe_width_out + phase_init) / fir_phases; + + return stripe_width_out - (fir_taps - 1); +} + +struct ipu3_css_reso { + unsigned int input_width; + unsigned int input_height; + enum imgu_abi_frame_format input_format; + unsigned int pin_width[IMGU_ABI_OSYS_PINS]; + unsigned int pin_height[IMGU_ABI_OSYS_PINS]; + unsigned int pin_stride[IMGU_ABI_OSYS_PINS]; + enum imgu_abi_frame_format pin_format[IMGU_ABI_OSYS_PINS]; + int chunk_width; + int chunk_height; + int block_height; + int block_width; +}; + +struct ipu3_css_frame_params { + /* Output pins */ + unsigned int enable; + unsigned int format; + unsigned int flip; + unsigned int mirror; + unsigned int tiling; + unsigned int reduce_range; + unsigned int width; + unsigned int height; + unsigned int stride; + unsigned int scaled; + unsigned int crop_left; + unsigned int crop_top; +}; + +struct ipu3_css_stripe_params { + unsigned int processing_mode; + unsigned int phase_step; + unsigned int exp_shift; + unsigned int phase_init_left_y; + unsigned int phase_init_left_uv; + unsigned int phase_init_top_y; + unsigned int phase_init_top_uv; + unsigned int pad_left_y; + unsigned int pad_left_uv; + unsigned int pad_right_y; + unsigned int pad_right_uv; + unsigned int pad_top_y; + unsigned int pad_top_uv; + unsigned int pad_bottom_y; + unsigned int pad_bottom_uv; + unsigned int crop_left_y; + unsigned int crop_top_y; + unsigned int crop_left_uv; + unsigned int crop_top_uv; + unsigned int start_column_y; + unsigned int start_column_uv; + unsigned int chunk_width; + unsigned int chunk_height; + unsigned int block_width; + unsigned int block_height; + unsigned int input_width; + unsigned int input_height; + int output_width[IMGU_ABI_OSYS_PINS]; + int output_height[IMGU_ABI_OSYS_PINS]; + int output_offset[IMGU_ABI_OSYS_PINS]; +}; + +/* + * frame_params - size IMGU_ABI_OSYS_PINS + * stripe_params - size IPU3_UAPI_MAX_STRIPES + */ +static int ipu3_css_osys_calc_frame_and_stripe_params( + struct ipu3_css *css, unsigned int stripes, + struct imgu_abi_osys_config *osys, + struct ipu3_css_scaler_info *scaler_luma, + struct ipu3_css_scaler_info *scaler_chroma, + struct ipu3_css_frame_params frame_params[], + struct ipu3_css_stripe_params stripe_params[], + unsigned int pipe) +{ + struct ipu3_css_reso reso; + unsigned int output_width, pin, s; + u32 input_width, input_height, target_width, target_height; + unsigned int procmode = 0; + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + + input_width = css_pipe->rect[IPU3_CSS_RECT_GDC].width; + input_height = css_pipe->rect[IPU3_CSS_RECT_GDC].height; + target_width = css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.width; + target_height = css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.height; + + /* Frame parameters */ + + /* Input width for Output System is output width of DVS (with GDC) */ + reso.input_width = css_pipe->rect[IPU3_CSS_RECT_GDC].width; + + /* Input height for Output System is output height of DVS (with GDC) */ + reso.input_height = css_pipe->rect[IPU3_CSS_RECT_GDC].height; + + reso.input_format = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->frame_format; + + reso.pin_width[IMGU_ABI_OSYS_PIN_OUT] = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.width; + reso.pin_height[IMGU_ABI_OSYS_PIN_OUT] = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.height; + reso.pin_stride[IMGU_ABI_OSYS_PIN_OUT] = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].width_pad; + reso.pin_format[IMGU_ABI_OSYS_PIN_OUT] = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->frame_format; + + reso.pin_width[IMGU_ABI_OSYS_PIN_VF] = + css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.width; + reso.pin_height[IMGU_ABI_OSYS_PIN_VF] = + css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.height; + reso.pin_stride[IMGU_ABI_OSYS_PIN_VF] = + css_pipe->queue[IPU3_CSS_QUEUE_VF].width_pad; + reso.pin_format[IMGU_ABI_OSYS_PIN_VF] = + css_pipe->queue[IPU3_CSS_QUEUE_VF].css_fmt->frame_format; + + /* Configure the frame parameters for all output pins */ + + frame_params[IMGU_ABI_OSYS_PIN_OUT].width = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.width; + frame_params[IMGU_ABI_OSYS_PIN_OUT].height = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.height; + frame_params[IMGU_ABI_OSYS_PIN_VF].width = + css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.width; + frame_params[IMGU_ABI_OSYS_PIN_VF].height = + css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.height; + frame_params[IMGU_ABI_OSYS_PIN_VF].crop_top = 0; + frame_params[IMGU_ABI_OSYS_PIN_VF].crop_left = 0; + + for (pin = 0; pin < IMGU_ABI_OSYS_PINS; pin++) { + int enable = 0; + int scaled = 0; + unsigned int format = 0; + unsigned int tiling = 0; + + frame_params[pin].flip = 0; + frame_params[pin].mirror = 0; + frame_params[pin].reduce_range = 0; + if (reso.pin_width[pin] != 0 && reso.pin_height[pin] != 0) { + enable = 1; + if (pin == IMGU_ABI_OSYS_PIN_OUT) { + if (reso.input_width < reso.pin_width[pin] || + reso.input_height < reso.pin_height[pin]) + return -EINVAL; + /* + * When input and output resolution is + * different instead of scaling, cropping + * should happen. Determine the crop factor + * to do the symmetric cropping + */ + frame_params[pin].crop_left = roundclosest_down( + (reso.input_width - + reso.pin_width[pin]) / 2, + IMGU_OSYS_DMA_CROP_W_LIMIT); + frame_params[pin].crop_top = roundclosest_down( + (reso.input_height - + reso.pin_height[pin]) / 2, + IMGU_OSYS_DMA_CROP_H_LIMIT); + } else { + if (reso.pin_width[pin] != reso.input_width || + reso.pin_height[pin] != reso.input_height) { + /* + * If resolution is different at input + * and output of OSYS, scaling is + * considered except when pin is MAIN. + * Later it will be decide whether + * scaler factor is 1 or other + * and cropping has to be done or not. + */ + scaled = 1; + } + } + ipu3_css_osys_set_format(reso.pin_format[pin], &format, + &tiling); + } else { + enable = 0; + } + frame_params[pin].enable = enable; + frame_params[pin].format = format; + frame_params[pin].tiling = tiling; + frame_params[pin].stride = reso.pin_stride[pin]; + frame_params[pin].scaled = scaled; + } + + ipu3_css_scaler_calc(input_width, input_height, target_width, + target_height, osys, scaler_luma, scaler_chroma, + &reso.pin_width[IMGU_ABI_OSYS_PIN_VF], + &reso.pin_height[IMGU_ABI_OSYS_PIN_VF], &procmode); + dev_dbg(css->dev, "osys scaler procmode is %u", procmode); + output_width = reso.pin_width[IMGU_ABI_OSYS_PIN_VF]; + + if (output_width < reso.input_width / 2) { + /* Scaling factor <= 0.5 */ + reso.chunk_width = IMGU_OSYS_BLOCK_WIDTH; + reso.block_width = IMGU_OSYS_BLOCK_WIDTH; + } else { /* 0.5 <= Scaling factor <= 1.0 */ + reso.chunk_width = IMGU_OSYS_BLOCK_WIDTH / 2; + reso.block_width = IMGU_OSYS_BLOCK_WIDTH; + } + + if (output_width <= reso.input_width * 7 / 8) { + /* Scaling factor <= 0.875 */ + reso.chunk_height = IMGU_OSYS_BLOCK_HEIGHT; + reso.block_height = IMGU_OSYS_BLOCK_HEIGHT; + } else { /* 1.0 <= Scaling factor <= 1.75 */ + reso.chunk_height = IMGU_OSYS_BLOCK_HEIGHT / 2; + reso.block_height = IMGU_OSYS_BLOCK_HEIGHT; + } + + /* + * Calculate scaler configuration parameters based on input and output + * resolution. + */ + + if (frame_params[IMGU_ABI_OSYS_PIN_VF].enable) { + /* + * When aspect ratio is different between target resolution and + * required resolution, determine the crop factor to do + * symmetric cropping + */ + u32 w = reso.pin_width[IMGU_ABI_OSYS_PIN_VF] - + frame_params[IMGU_ABI_OSYS_PIN_VF].width; + u32 h = reso.pin_height[IMGU_ABI_OSYS_PIN_VF] - + frame_params[IMGU_ABI_OSYS_PIN_VF].height; + + frame_params[IMGU_ABI_OSYS_PIN_VF].crop_left = + roundclosest_down(w / 2, IMGU_OSYS_DMA_CROP_W_LIMIT); + frame_params[IMGU_ABI_OSYS_PIN_VF].crop_top = + roundclosest_down(h / 2, IMGU_OSYS_DMA_CROP_H_LIMIT); + + if (reso.input_height % 4 || reso.input_width % 8) { + dev_err(css->dev, "OSYS input width is not multiple of 8 or\n"); + dev_err(css->dev, "height is not multiple of 4\n"); + return -EINVAL; + } + } + + /* Stripe parameters */ + + if (frame_params[IMGU_ABI_OSYS_PIN_VF].enable) { + output_width = reso.pin_width[IMGU_ABI_OSYS_PIN_VF]; + } else { + /* + * in case scaler output is not enabled + * take output width as input width since + * there is no scaling at main pin. + * Due to the fact that main pin can be different + * from input resolution to osys in the case of cropping, + * main pin resolution is not taken. + */ + output_width = reso.input_width; + } + + for (s = 0; s < stripes; s++) { + int stripe_offset_inp_y = 0; + int stripe_offset_inp_uv = 0; + int stripe_offset_out_y = 0; + int stripe_offset_out_uv = 0; + int stripe_phase_init_y = scaler_luma->phase_init; + int stripe_phase_init_uv = scaler_chroma->phase_init; + int stripe_offset_blk_y = 0; + int stripe_offset_blk_uv = 0; + int stripe_offset_col_y = 0; + int stripe_offset_col_uv = 0; + int stripe_pad_left_y = scaler_luma->pad_left; + int stripe_pad_left_uv = scaler_chroma->pad_left; + int stripe_pad_right_y = scaler_luma->pad_right; + int stripe_pad_right_uv = scaler_chroma->pad_right; + int stripe_crop_left_y = scaler_luma->crop_left; + int stripe_crop_left_uv = scaler_chroma->crop_left; + int stripe_input_width_y = reso.input_width; + int stripe_input_width_uv = 0; + int stripe_output_width_y = output_width; + int stripe_output_width_uv = 0; + int chunk_floor_y = 0; + int chunk_floor_uv = 0; + int chunk_ceil_uv = 0; + + if (stripes > 1) { + if (s > 0) { + /* Calculate stripe offsets */ + stripe_offset_out_y = + output_width * s / stripes; + stripe_offset_out_y = + rounddown(stripe_offset_out_y, + IPU3_UAPI_ISP_VEC_ELEMS); + stripe_offset_out_uv = stripe_offset_out_y / + IMGU_LUMA_TO_CHROMA_RATIO; + stripe_offset_inp_y = + ipu3_css_osys_calc_stripe_offset( + stripe_offset_out_y, + IMGU_OSYS_FIR_PHASES, + scaler_luma->phase_init, + scaler_luma->phase_step, + scaler_luma->pad_left); + stripe_offset_inp_uv = + ipu3_css_osys_calc_stripe_offset( + stripe_offset_out_uv, + IMGU_OSYS_FIR_PHASES, + scaler_chroma->phase_init, + scaler_chroma->phase_step, + scaler_chroma->pad_left); + + /* Calculate stripe phase init */ + stripe_phase_init_y = + ipu3_css_osys_calc_stripe_phase_init( + stripe_offset_out_y, + IMGU_OSYS_FIR_PHASES, + scaler_luma->phase_init, + scaler_luma->phase_step, + scaler_luma->pad_left); + stripe_phase_init_uv = + ipu3_css_osys_calc_stripe_phase_init( + stripe_offset_out_uv, + IMGU_OSYS_FIR_PHASES, + scaler_chroma->phase_init, + scaler_chroma->phase_step, + scaler_chroma->pad_left); + + /* + * Chunk boundary corner case - luma and chroma + * start from different input chunks. + */ + chunk_floor_y = rounddown(stripe_offset_inp_y, + reso.chunk_width); + chunk_floor_uv = + rounddown(stripe_offset_inp_uv, + reso.chunk_width / + IMGU_LUMA_TO_CHROMA_RATIO); + + if (chunk_floor_y != chunk_floor_uv * + IMGU_LUMA_TO_CHROMA_RATIO) { + /* + * Match starting luma/chroma chunks. + * Decrease offset for UV and add output + * cropping. + */ + stripe_offset_inp_uv -= 1; + stripe_crop_left_uv += 1; + stripe_phase_init_uv -= + scaler_luma->phase_step; + if (stripe_phase_init_uv < 0) + stripe_phase_init_uv = + stripe_phase_init_uv + + IMGU_OSYS_FIR_PHASES; + } + /* + * FW workaround for a HW bug: if the first + * chroma pixel is generated exactly at the end + * of chunck scaler HW may not output the pixel + * for downscale factors smaller than 1.5 + * (timing issue). + */ + chunk_ceil_uv = + roundup(stripe_offset_inp_uv, + reso.chunk_width / + IMGU_LUMA_TO_CHROMA_RATIO); + + if (stripe_offset_inp_uv == + chunk_ceil_uv - IMGU_OSYS_TAPS_UV) { + /* + * Decrease input offset and add + * output cropping + */ + stripe_offset_inp_uv -= 1; + stripe_phase_init_uv -= + scaler_luma->phase_step; + if (stripe_phase_init_uv < 0) { + stripe_phase_init_uv += + IMGU_OSYS_FIR_PHASES; + stripe_crop_left_uv += 1; + } + } + + /* + * Calculate block and column offsets for the + * input stripe + */ + stripe_offset_blk_y = + rounddown(stripe_offset_inp_y, + IMGU_INPUT_BLOCK_WIDTH); + stripe_offset_blk_uv = + rounddown(stripe_offset_inp_uv, + IMGU_INPUT_BLOCK_WIDTH / + IMGU_LUMA_TO_CHROMA_RATIO); + stripe_offset_col_y = stripe_offset_inp_y - + stripe_offset_blk_y; + stripe_offset_col_uv = stripe_offset_inp_uv - + stripe_offset_blk_uv; + + /* Left padding is only for the first stripe */ + stripe_pad_left_y = 0; + stripe_pad_left_uv = 0; + } + + /* Right padding is only for the last stripe */ + if (s < stripes - 1) { + int next_offset; + + stripe_pad_right_y = 0; + stripe_pad_right_uv = 0; + + next_offset = output_width * (s + 1) / stripes; + next_offset = rounddown(next_offset, 64); + stripe_output_width_y = next_offset - + stripe_offset_out_y; + } else { + stripe_output_width_y = output_width - + stripe_offset_out_y; + } + + /* Calculate target output stripe width */ + stripe_output_width_uv = stripe_output_width_y / + IMGU_LUMA_TO_CHROMA_RATIO; + /* Calculate input stripe width */ + stripe_input_width_y = stripe_offset_col_y + + ipu3_css_osys_calc_inp_stripe_width( + stripe_output_width_y, + IMGU_OSYS_FIR_PHASES, + stripe_phase_init_y, + scaler_luma->phase_step, + IMGU_OSYS_TAPS_Y, + stripe_pad_left_y, + stripe_pad_right_y); + + stripe_input_width_uv = stripe_offset_col_uv + + ipu3_css_osys_calc_inp_stripe_width( + stripe_output_width_uv, + IMGU_OSYS_FIR_PHASES, + stripe_phase_init_uv, + scaler_chroma->phase_step, + IMGU_OSYS_TAPS_UV, + stripe_pad_left_uv, + stripe_pad_right_uv); + + stripe_input_width_uv = max(DIV_ROUND_UP( + stripe_input_width_y, + IMGU_LUMA_TO_CHROMA_RATIO), + stripe_input_width_uv); + + stripe_input_width_y = stripe_input_width_uv * + IMGU_LUMA_TO_CHROMA_RATIO; + + if (s >= stripes - 1) { + stripe_input_width_y = reso.input_width - + stripe_offset_blk_y; + /* + * The scaler requires that the last stripe + * spans at least two input blocks. + */ + } + + /* + * Spec: input stripe width must be a multiple of 8. + * Increase the input width and recalculate the output + * width. This may produce an extra column of junk + * blocks which will be overwritten by the + * next stripe. + */ + stripe_input_width_y = ALIGN(stripe_input_width_y, 8); + stripe_output_width_y = + ipu3_css_osys_out_stripe_width( + stripe_input_width_y, + IMGU_OSYS_FIR_PHASES, + stripe_phase_init_y, + scaler_luma->phase_step, + IMGU_OSYS_TAPS_Y, + stripe_pad_left_y, + stripe_pad_right_y, + stripe_offset_col_y); + + stripe_output_width_y = + rounddown(stripe_output_width_y, + IMGU_LUMA_TO_CHROMA_RATIO); + } + /* + * Following section executes and process parameters + * for both cases - Striping or No Striping. + */ + { + unsigned int i; + int pin_scale = 0; + /*Input resolution */ + + stripe_params[s].input_width = stripe_input_width_y; + stripe_params[s].input_height = reso.input_height; + + for (i = 0; i < IMGU_ABI_OSYS_PINS; i++) { + if (frame_params[i].scaled) { + /* + * Output stripe resolution and offset + * as produced by the scaler; actual + * output resolution may be slightly + * smaller. + */ + stripe_params[s].output_width[i] = + stripe_output_width_y; + stripe_params[s].output_height[i] = + reso.pin_height[i]; + stripe_params[s].output_offset[i] = + stripe_offset_out_y; + + pin_scale += frame_params[i].scaled; + } else { + /* Unscaled pin */ + stripe_params[s].output_width[i] = + stripe_params[s].input_width; + stripe_params[s].output_height[i] = + stripe_params[s].input_height; + stripe_params[s].output_offset[i] = + stripe_offset_blk_y; + } + } + + /* If no pin use scale, we use BYPASS mode */ + stripe_params[s].processing_mode = procmode; + stripe_params[s].phase_step = scaler_luma->phase_step; + stripe_params[s].exp_shift = scaler_luma->exp_shift; + stripe_params[s].phase_init_left_y = + stripe_phase_init_y; + stripe_params[s].phase_init_left_uv = + stripe_phase_init_uv; + stripe_params[s].phase_init_top_y = + scaler_luma->phase_init; + stripe_params[s].phase_init_top_uv = + scaler_chroma->phase_init; + stripe_params[s].pad_left_y = stripe_pad_left_y; + stripe_params[s].pad_left_uv = stripe_pad_left_uv; + stripe_params[s].pad_right_y = stripe_pad_right_y; + stripe_params[s].pad_right_uv = stripe_pad_right_uv; + stripe_params[s].pad_top_y = scaler_luma->pad_left; + stripe_params[s].pad_top_uv = scaler_chroma->pad_left; + stripe_params[s].pad_bottom_y = scaler_luma->pad_right; + stripe_params[s].pad_bottom_uv = + scaler_chroma->pad_right; + stripe_params[s].crop_left_y = stripe_crop_left_y; + stripe_params[s].crop_top_y = scaler_luma->crop_top; + stripe_params[s].crop_left_uv = stripe_crop_left_uv; + stripe_params[s].crop_top_uv = scaler_chroma->crop_top; + stripe_params[s].start_column_y = stripe_offset_col_y; + stripe_params[s].start_column_uv = stripe_offset_col_uv; + stripe_params[s].chunk_width = reso.chunk_width; + stripe_params[s].chunk_height = reso.chunk_height; + stripe_params[s].block_width = reso.block_width; + stripe_params[s].block_height = reso.block_height; + } + } + + return 0; +} + +/* + * This function configures the Output Formatter System, given the number of + * stripes, scaler luma and chrome parameters + */ +static int ipu3_css_osys_calc(struct ipu3_css *css, unsigned int pipe, + unsigned int stripes, + struct imgu_abi_osys_config *osys, + struct ipu3_css_scaler_info *scaler_luma, + struct ipu3_css_scaler_info *scaler_chroma, + struct imgu_abi_stripes block_stripes[]) +{ + struct ipu3_css_frame_params frame_params[IMGU_ABI_OSYS_PINS]; + struct ipu3_css_stripe_params stripe_params[IPU3_UAPI_MAX_STRIPES]; + struct imgu_abi_osys_formatter_params *param; + unsigned int pin, s; + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + + memset(osys, 0, sizeof(*osys)); + + /* Compute the frame and stripe params */ + if (ipu3_css_osys_calc_frame_and_stripe_params(css, stripes, osys, + scaler_luma, + scaler_chroma, + frame_params, + stripe_params, pipe)) + return -EINVAL; + + /* Output formatter system parameters */ + + for (s = 0; s < stripes; s++) { + struct imgu_abi_osys_scaler_params *scaler = + &osys->scaler[s].param; + int fifo_addr_fmt = IMGU_FIFO_ADDR_SCALER_TO_FMT; + int fifo_addr_ack = IMGU_FIFO_ADDR_SCALER_TO_SP; + + /* OUTPUT 0 / PIN 0 is only Scaler output */ + scaler->inp_buf_y_st_addr = IMGU_VMEM1_INP_BUF_ADDR; + + /* + * = (IMGU_OSYS_BLOCK_WIDTH / IMGU_VMEM1_ELEMS_PER_VEC) + * = (2 * IPU3_UAPI_ISP_VEC_ELEMS) / + * (IMGU_HIVE_OF_SYS_OF_SYSTEM_NWAYS) + * = 2 * 64 / 32 = 4 + */ + scaler->inp_buf_y_line_stride = IMGU_VMEM1_Y_STRIDE; + /* + * = (IMGU_VMEM1_V_OFFSET + VMEM1_uv_size) + * = (IMGU_VMEM1_U_OFFSET + VMEM1_uv_size) + + * (VMEM1_y_size / 4) + * = (VMEM1_y_size) + (VMEM1_y_size / 4) + + * (IMGU_OSYS_BLOCK_HEIGHT * IMGU_VMEM1_Y_STRIDE)/4 + * = (IMGU_OSYS_BLOCK_HEIGHT * IMGU_VMEM1_Y_STRIDE) + */ + scaler->inp_buf_y_buffer_stride = IMGU_VMEM1_BUF_SIZE; + scaler->inp_buf_u_st_addr = IMGU_VMEM1_INP_BUF_ADDR + + IMGU_VMEM1_U_OFFSET; + scaler->inp_buf_v_st_addr = IMGU_VMEM1_INP_BUF_ADDR + + IMGU_VMEM1_V_OFFSET; + scaler->inp_buf_uv_line_stride = IMGU_VMEM1_UV_STRIDE; + scaler->inp_buf_uv_buffer_stride = IMGU_VMEM1_BUF_SIZE; + scaler->inp_buf_chunk_width = stripe_params[s].chunk_width; + scaler->inp_buf_nr_buffers = IMGU_OSYS_NUM_INPUT_BUFFERS; + + /* Output buffers */ + scaler->out_buf_y_st_addr = IMGU_VMEM1_INT_BUF_ADDR; + scaler->out_buf_y_line_stride = stripe_params[s].block_width / + IMGU_VMEM1_ELEMS_PER_VEC; + scaler->out_buf_y_buffer_stride = IMGU_VMEM1_BUF_SIZE; + scaler->out_buf_u_st_addr = IMGU_VMEM1_INT_BUF_ADDR + + IMGU_VMEM1_U_OFFSET; + scaler->out_buf_v_st_addr = IMGU_VMEM1_INT_BUF_ADDR + + IMGU_VMEM1_V_OFFSET; + scaler->out_buf_uv_line_stride = stripe_params[s].block_width / + IMGU_VMEM1_ELEMS_PER_VEC / 2; + scaler->out_buf_uv_buffer_stride = IMGU_VMEM1_BUF_SIZE; + scaler->out_buf_nr_buffers = IMGU_OSYS_NUM_INTERM_BUFFERS; + + /* Intermediate buffers */ + scaler->int_buf_y_st_addr = IMGU_VMEM2_BUF_Y_ADDR; + scaler->int_buf_y_line_stride = IMGU_VMEM2_BUF_Y_STRIDE; + scaler->int_buf_u_st_addr = IMGU_VMEM2_BUF_U_ADDR; + scaler->int_buf_v_st_addr = IMGU_VMEM2_BUF_V_ADDR; + scaler->int_buf_uv_line_stride = IMGU_VMEM2_BUF_UV_STRIDE; + scaler->int_buf_height = IMGU_VMEM2_LINES_PER_BLOCK; + scaler->int_buf_chunk_width = stripe_params[s].chunk_height; + scaler->int_buf_chunk_height = stripe_params[s].block_width; + + /* Context buffers */ + scaler->ctx_buf_hor_y_st_addr = IMGU_VMEM3_HOR_Y_ADDR; + scaler->ctx_buf_hor_u_st_addr = IMGU_VMEM3_HOR_U_ADDR; + scaler->ctx_buf_hor_v_st_addr = IMGU_VMEM3_HOR_V_ADDR; + scaler->ctx_buf_ver_y_st_addr = IMGU_VMEM3_VER_Y_ADDR; + scaler->ctx_buf_ver_u_st_addr = IMGU_VMEM3_VER_U_ADDR; + scaler->ctx_buf_ver_v_st_addr = IMGU_VMEM3_VER_V_ADDR; + + /* Addresses for release-input and process-output tokens */ + scaler->release_inp_buf_addr = fifo_addr_ack; + scaler->release_inp_buf_en = 1; + scaler->release_out_buf_en = 1; + scaler->process_out_buf_addr = fifo_addr_fmt; + + /* Settings dimensions, padding, cropping */ + scaler->input_image_y_width = stripe_params[s].input_width; + scaler->input_image_y_height = stripe_params[s].input_height; + scaler->input_image_y_start_column = + stripe_params[s].start_column_y; + scaler->input_image_uv_start_column = + stripe_params[s].start_column_uv; + scaler->input_image_y_left_pad = stripe_params[s].pad_left_y; + scaler->input_image_uv_left_pad = stripe_params[s].pad_left_uv; + scaler->input_image_y_right_pad = stripe_params[s].pad_right_y; + scaler->input_image_uv_right_pad = + stripe_params[s].pad_right_uv; + scaler->input_image_y_top_pad = stripe_params[s].pad_top_y; + scaler->input_image_uv_top_pad = stripe_params[s].pad_top_uv; + scaler->input_image_y_bottom_pad = + stripe_params[s].pad_bottom_y; + scaler->input_image_uv_bottom_pad = + stripe_params[s].pad_bottom_uv; + scaler->processing_mode = stripe_params[s].processing_mode; + scaler->scaling_ratio = stripe_params[s].phase_step; + scaler->y_left_phase_init = stripe_params[s].phase_init_left_y; + scaler->uv_left_phase_init = + stripe_params[s].phase_init_left_uv; + scaler->y_top_phase_init = stripe_params[s].phase_init_top_y; + scaler->uv_top_phase_init = stripe_params[s].phase_init_top_uv; + scaler->coeffs_exp_shift = stripe_params[s].exp_shift; + scaler->out_y_left_crop = stripe_params[s].crop_left_y; + scaler->out_uv_left_crop = stripe_params[s].crop_left_uv; + scaler->out_y_top_crop = stripe_params[s].crop_top_y; + scaler->out_uv_top_crop = stripe_params[s].crop_top_uv; + + for (pin = 0; pin < IMGU_ABI_OSYS_PINS; pin++) { + int in_fifo_addr; + int out_fifo_addr; + int block_width_vecs; + int input_width_s; + int input_width_vecs; + int input_buf_y_st_addr; + int input_buf_u_st_addr; + int input_buf_v_st_addr; + int input_buf_y_line_stride; + int input_buf_uv_line_stride; + int output_buf_y_line_stride; + int output_buf_uv_line_stride; + int output_buf_nr_y_lines; + int block_height; + int block_width; + struct imgu_abi_osys_frame_params *fr_pr; + + fr_pr = &osys->frame[pin].param; + + /* Frame parameters */ + fr_pr->enable = frame_params[pin].enable; + fr_pr->format = frame_params[pin].format; + fr_pr->mirror = frame_params[pin].mirror; + fr_pr->flip = frame_params[pin].flip; + fr_pr->tiling = frame_params[pin].tiling; + fr_pr->width = frame_params[pin].width; + fr_pr->height = frame_params[pin].height; + fr_pr->stride = frame_params[pin].stride; + fr_pr->scaled = frame_params[pin].scaled; + + /* Stripe parameters */ + osys->stripe[s].crop_top[pin] = + frame_params[pin].crop_top; + osys->stripe[s].input_width = + stripe_params[s].input_width; + osys->stripe[s].input_height = + stripe_params[s].input_height; + osys->stripe[s].block_height = + stripe_params[s].block_height; + osys->stripe[s].block_width = + stripe_params[s].block_width; + osys->stripe[s].output_width[pin] = + stripe_params[s].output_width[pin]; + osys->stripe[s].output_height[pin] = + stripe_params[s].output_height[pin]; + + if (s == 0) { + /* Only first stripe should do left cropping */ + osys->stripe[s].crop_left[pin] = + frame_params[pin].crop_left; + osys->stripe[s].output_offset[pin] = + stripe_params[s].output_offset[pin]; + } else { + /* + * Stripe offset for other strips should be + * adjusted according to the cropping done + * at the first strip + */ + osys->stripe[s].crop_left[pin] = 0; + osys->stripe[s].output_offset[pin] = + (stripe_params[s].output_offset[pin] - + osys->stripe[0].crop_left[pin]); + } + + if (!frame_params[pin].enable) + continue; + + /* Formatter: configurations */ + + /* + * Get the dimensions of the input blocks of the + * formatter, which is the same as the output + * blocks of the scaler. + */ + if (frame_params[pin].scaled) { + block_height = stripe_params[s].block_height; + block_width = stripe_params[s].block_width; + } else { + block_height = IMGU_OSYS_BLOCK_HEIGHT; + block_width = IMGU_OSYS_BLOCK_WIDTH; + } + block_width_vecs = + block_width / IMGU_VMEM1_ELEMS_PER_VEC; + /* + * The input/output line stride depends on the + * block size. + */ + input_buf_y_line_stride = block_width_vecs; + input_buf_uv_line_stride = block_width_vecs / 2; + output_buf_y_line_stride = block_width_vecs; + output_buf_uv_line_stride = block_width_vecs / 2; + output_buf_nr_y_lines = block_height; + if (frame_params[pin].format == + IMGU_ABI_OSYS_FORMAT_NV12 || + frame_params[pin].format == + IMGU_ABI_OSYS_FORMAT_NV21) + output_buf_uv_line_stride = + output_buf_y_line_stride; + + /* + * Tiled outputs use a different output buffer + * configuration. The input (= scaler output) block + * width translates to a tile height, and the block + * height to the tile width. The default block size of + * 128x32 maps exactly onto a 4kB tile (512x8) for Y. + * For UV, the tile width is always half. + */ + if (frame_params[pin].tiling) { + output_buf_nr_y_lines = 8; + output_buf_y_line_stride = 512 / + IMGU_VMEM1_ELEMS_PER_VEC; + output_buf_uv_line_stride = 256 / + IMGU_VMEM1_ELEMS_PER_VEC; + } + + /* + * Store the output buffer line stride. Will be + * used to compute buffer offsets in boundary + * conditions when output blocks are partially + * outside the image. + */ + osys->stripe[s].buf_stride[pin] = + output_buf_y_line_stride * + IMGU_HIVE_OF_SYS_OF_SYSTEM_NWAYS; + if (frame_params[pin].scaled) { + /* + * The input buffs are the intermediate + * buffers (scalers' output) + */ + input_buf_y_st_addr = IMGU_VMEM1_INT_BUF_ADDR; + input_buf_u_st_addr = IMGU_VMEM1_INT_BUF_ADDR + + IMGU_VMEM1_U_OFFSET; + input_buf_v_st_addr = IMGU_VMEM1_INT_BUF_ADDR + + IMGU_VMEM1_V_OFFSET; + } else { + /* + * The input bufferss are the buffers + * filled by the SP + */ + input_buf_y_st_addr = IMGU_VMEM1_INP_BUF_ADDR; + input_buf_u_st_addr = IMGU_VMEM1_INP_BUF_ADDR + + IMGU_VMEM1_U_OFFSET; + input_buf_v_st_addr = IMGU_VMEM1_INP_BUF_ADDR + + IMGU_VMEM1_V_OFFSET; + } + + /* + * The formatter input width must be rounded to + * the block width. Otherwise the formatter will + * not recognize the end of the line, resulting + * in incorrect tiling (system may hang!) and + * possibly other problems. + */ + input_width_s = + roundup(stripe_params[s].output_width[pin], + block_width); + input_width_vecs = input_width_s / + IMGU_VMEM1_ELEMS_PER_VEC; + out_fifo_addr = IMGU_FIFO_ADDR_FMT_TO_SP; + /* + * Process-output tokens must be sent to the SP. + * When scaling, the release-input tokens can be + * sent directly to the scaler, otherwise the + * formatter should send them to the SP. + */ + if (frame_params[pin].scaled) + in_fifo_addr = IMGU_FIFO_ADDR_FMT_TO_SCALER; + else + in_fifo_addr = IMGU_FIFO_ADDR_FMT_TO_SP; + + /* Formatter */ + param = &osys->formatter[s][pin].param; + + param->format = frame_params[pin].format; + param->flip = frame_params[pin].flip; + param->mirror = frame_params[pin].mirror; + param->tiling = frame_params[pin].tiling; + param->reduce_range = frame_params[pin].reduce_range; + param->alpha_blending = 0; + param->release_inp_addr = in_fifo_addr; + param->release_inp_en = 1; + param->process_out_buf_addr = out_fifo_addr; + param->image_width_vecs = input_width_vecs; + param->image_height_lines = + stripe_params[s].output_height[pin]; + param->inp_buff_y_st_addr = input_buf_y_st_addr; + param->inp_buff_y_line_stride = input_buf_y_line_stride; + param->inp_buff_y_buffer_stride = IMGU_VMEM1_BUF_SIZE; + param->int_buff_u_st_addr = input_buf_u_st_addr; + param->int_buff_v_st_addr = input_buf_v_st_addr; + param->inp_buff_uv_line_stride = + input_buf_uv_line_stride; + param->inp_buff_uv_buffer_stride = IMGU_VMEM1_BUF_SIZE; + param->out_buff_level = 0; + param->out_buff_nr_y_lines = output_buf_nr_y_lines; + param->out_buff_u_st_offset = IMGU_VMEM1_U_OFFSET; + param->out_buff_v_st_offset = IMGU_VMEM1_V_OFFSET; + param->out_buff_y_line_stride = + output_buf_y_line_stride; + param->out_buff_uv_line_stride = + output_buf_uv_line_stride; + param->hist_buff_st_addr = IMGU_VMEM1_HST_BUF_ADDR; + param->hist_buff_line_stride = + IMGU_VMEM1_HST_BUF_STRIDE; + param->hist_buff_nr_lines = IMGU_VMEM1_HST_BUF_NLINES; + } + } + + block_stripes[0].offset = 0; + if (stripes <= 1) { + block_stripes[0].width = stripe_params[0].input_width; + block_stripes[0].height = stripe_params[0].input_height; + } else { + struct imgu_fw_info *bi = + &css->fwp->binary_header[css_pipe->bindex]; + unsigned int sp_block_width = + bi->info.isp.sp.block.block_width * + IPU3_UAPI_ISP_VEC_ELEMS; + + block_stripes[0].width = roundup(stripe_params[0].input_width, + sp_block_width); + block_stripes[1].offset = + rounddown(css_pipe->rect[IPU3_CSS_RECT_GDC].width - + stripe_params[1].input_width, sp_block_width); + block_stripes[1].width = + roundup(css_pipe->rect[IPU3_CSS_RECT_GDC].width - + block_stripes[1].offset, sp_block_width); + block_stripes[0].height = css_pipe->rect[IPU3_CSS_RECT_GDC].height; + block_stripes[1].height = block_stripes[0].height; + } + + return 0; +} + +/*********************** Mostly 3A operations ******************************/ + +/* + * This function creates a "TO-DO list" (operations) for the sp code. + * + * There are 2 types of operations: + * 1. Transfer: Issue DMA transfer request for copying grid cells from DDR to + * accelerator space (NOTE that this space is limited) associated data: + * DDR address + accelerator's config set index(acc's address). + * + * 2. Issue "Process Lines Command" to shd accelerator + * associated data: #lines + which config set to use (actually, accelerator + * will use x AND (x+1)%num_of_sets - NOTE that this implies the restriction + * of not touching config sets x & (x+1)%num_of_sets when process_lines(x) + * is active). + * + * Basically there are 2 types of operations "chunks": + * 1. "initial chunk": Initially, we do as much transfers as we can (and need) + * [0 - max sets(3) ] followed by 1 or 2 "process lines" operations. + * + * 2. "regular chunk" - 1 transfer followed by 1 process line operation. + * (in some cases we might need additional transfer ate the last chunk). + * + * for some case: + * --> init + * tr (0) + * tr (1) + * tr (2) + * pl (0) + * pl (1) + * --> ack (0) + * tr (3) + * pl (2) + * --> ack (1) + * pl (3) + * --> ack (2) + * do nothing + * --> ack (3) + * do nothing + */ + +static int +ipu3_css_shd_ops_calc(struct imgu_abi_shd_intra_frame_operations_data *ops, + const struct ipu3_uapi_shd_grid_config *grid, + unsigned int image_height) +{ + unsigned int block_height = 1 << grid->block_height_log2; + unsigned int grid_height_per_slice = grid->grid_height_per_slice; + unsigned int set_height = grid_height_per_slice * block_height; + + /* We currently support only abs(y_start) > grid_height_per_slice */ + unsigned int positive_y_start = (unsigned int)-grid->y_start; + unsigned int first_process_lines = + set_height - (positive_y_start % set_height); + unsigned int last_set_height; + unsigned int num_of_sets; + + struct imgu_abi_acc_operation *p_op; + struct imgu_abi_acc_process_lines_cmd_data *p_pl; + struct imgu_abi_shd_transfer_luts_set_data *p_tr; + + unsigned int op_idx, pl_idx, tr_idx; + unsigned char tr_set_num, pl_cfg_set; + + /* + * When the number of lines for the last process lines command + * is equal to a set height, we need another line of grid cell - + * additional transfer is required. + */ + unsigned char last_tr = 0; + + /* Add "process lines" command to the list of operations */ + bool add_pl; + /* Add DMA xfer (config set) command to the list of ops */ + bool add_tr; + + /* + * Available partial grid (the part that fits into #IMGU_SHD_SETS sets) + * doesn't cover whole frame - need to process in chunks + */ + if (image_height > first_process_lines) { + last_set_height = + (image_height - first_process_lines) % set_height; + num_of_sets = last_set_height > 0 ? + (image_height - first_process_lines) / set_height + 2 : + (image_height - first_process_lines) / set_height + 1; + last_tr = (set_height - last_set_height <= block_height || + last_set_height == 0) ? 1 : 0; + } else { /* partial grid covers whole frame */ + last_set_height = 0; + num_of_sets = 1; + first_process_lines = image_height; + last_tr = set_height - image_height <= block_height ? 1 : 0; + } + + /* Init operations lists and counters */ + p_op = ops->operation_list; + op_idx = 0; + p_pl = ops->process_lines_data; + pl_idx = 0; + p_tr = ops->transfer_data; + tr_idx = 0; + + memset(ops, 0, sizeof(*ops)); + + /* Cyclic counters that holds config set number [0,IMGU_SHD_SETS) */ + tr_set_num = 0; + pl_cfg_set = 0; + + /* + * Always start with a transfer - process lines command must be + * initiated only after appropriate config sets are in place + * (2 configuration sets per process line command, except for last one). + */ + add_pl = false; + add_tr = true; + + while (add_pl || add_tr) { + /* Transfer ops */ + if (add_tr) { + if (op_idx >= IMGU_ABI_SHD_MAX_OPERATIONS || + tr_idx >= IMGU_ABI_SHD_MAX_TRANSFERS) + return -EINVAL; + p_op[op_idx].op_type = + IMGU_ABI_ACC_OPTYPE_TRANSFER_DATA; + p_op[op_idx].op_indicator = IMGU_ABI_ACC_OP_IDLE; + op_idx++; + p_tr[tr_idx].set_number = tr_set_num; + tr_idx++; + tr_set_num = (tr_set_num + 1) % IMGU_SHD_SETS; + } + + /* Process-lines ops */ + if (add_pl) { + if (op_idx >= IMGU_ABI_SHD_MAX_OPERATIONS || + pl_idx >= IMGU_ABI_SHD_MAX_PROCESS_LINES) + return -EINVAL; + p_op[op_idx].op_type = + IMGU_ABI_ACC_OPTYPE_PROCESS_LINES; + + /* + * In case we have 2 process lines commands - + * don't stop after the first one + */ + if (pl_idx == 0 && num_of_sets != 1) + p_op[op_idx].op_indicator = + IMGU_ABI_ACC_OP_IDLE; + /* + * Initiate last process lines command - + * end of operation list. + */ + else if (pl_idx == num_of_sets - 1) + p_op[op_idx].op_indicator = + IMGU_ABI_ACC_OP_END_OF_OPS; + /* + * Intermediate process line command - end of operation + * "chunk" (meaning few "transfers" followed by few + * "process lines" commands). + */ + else + p_op[op_idx].op_indicator = + IMGU_ABI_ACC_OP_END_OF_ACK; + + op_idx++; + + /* first process line operation */ + if (pl_idx == 0) + p_pl[pl_idx].lines = first_process_lines; + /* Last process line operation */ + else if (pl_idx == num_of_sets - 1 && + last_set_height > 0) + p_pl[pl_idx].lines = last_set_height; + else /* "regular" process lines operation */ + p_pl[pl_idx].lines = set_height; + + p_pl[pl_idx].cfg_set = pl_cfg_set; + pl_idx++; + pl_cfg_set = (pl_cfg_set + 1) % IMGU_SHD_SETS; + } + + /* + * Initially, we always transfer + * min(IMGU_SHD_SETS, num_of_sets) - after that we fill in the + * corresponding process lines commands. + */ + if (tr_idx == IMGU_SHD_SETS || + tr_idx == num_of_sets + last_tr) { + add_tr = false; + add_pl = true; + } + + /* + * We have finished the "initial" operations chunk - + * be ready to get more chunks. + */ + if (pl_idx == 2) { + add_tr = true; + add_pl = true; + } + + /* Stop conditions for each operation type */ + if (tr_idx == num_of_sets + last_tr) + add_tr = false; + if (pl_idx == num_of_sets) + add_pl = false; + } + + return 0; +} + +/* + * The follow handshake procotol is the same for AF, AWB and AWB FR. + * + * for n sets of meta-data, the flow is: + * --> init + * process-lines (0) + * process-lines (1) eoc + * --> ack (0) + * read-meta-data (0) + * process-lines (2) eoc + * --> ack (1) + * read-meta-data (1) + * process-lines (3) eoc + * ... + * + * --> ack (n-3) + * read-meta-data (n-3) + * process-lines (n-1) eoc + * --> ack (n-2) + * read-meta-data (n-2) eoc + * --> ack (n-1) + * read-meta-data (n-1) eof + * + * for 2 sets we get: + * --> init + * pl (0) + * pl (1) eoc + * --> ack (0) + * pl (2) - rest of image, if applicable) + * rmd (0) eoc + * --> ack (1) + * rmd (1) eof + * --> (ack (2)) + * do nothing + * + * for only one set: + * + * --> init + * pl(0) eoc + * --> ack (0) + * rmd (0) eof + * + * grid smaller than image case + * for example 128x128 grid (block size 8x8, 16x16 num of blocks) + * start at (0,0) + * 1st set holds 160 cells - 10 blocks vertical, 16 horizontal + * => 1st process lines = 80 + * we're left with 128-80=48 lines (6 blocks vertical) + * => 2nd process lines = 48 + * last process lines to cover the image - image_height - 128 + * + * --> init + * pl (0) first + * pl (1) last-in-grid + * --> ack (0) + * rmd (0) + * pl (2) after-grid + * --> ack (1) + * rmd (1) eof + * --> ack (2) + * do nothing + */ +struct process_lines { + unsigned int image_height; + unsigned short grid_height; + unsigned short block_height; + unsigned short y_start; + unsigned char grid_height_per_slice; + + unsigned short max_op; /* max operation */ + unsigned short max_tr; /* max transaction */ + unsigned char acc_enable; +}; + +/* Helper to config intra_frame_operations_data. */ +static int +ipu3_css_acc_process_lines(const struct process_lines *pl, + struct imgu_abi_acc_operation *p_op, + struct imgu_abi_acc_process_lines_cmd_data *p_pl, + struct imgu_abi_acc_transfer_op_data *p_tr) +{ + unsigned short op_idx = 0, pl_idx = 0, tr_idx = 0; + unsigned char tr_set_num = 0, pl_cfg_set = 0; + const unsigned short grid_last_line = + pl->y_start + pl->grid_height * pl->block_height; + const unsigned short process_lines = + pl->grid_height_per_slice * pl->block_height; + + unsigned int process_lines_after_grid; + unsigned short first_process_lines; + unsigned short last_process_lines_in_grid; + + unsigned short num_of_process_lines; + unsigned short num_of_sets; + + if (pl->grid_height_per_slice == 0) + return -EINVAL; + + if (pl->acc_enable && grid_last_line > pl->image_height) + return -EINVAL; + + num_of_sets = pl->grid_height / pl->grid_height_per_slice; + if (num_of_sets * pl->grid_height_per_slice < pl->grid_height) + num_of_sets++; + + /* Account for two line delay inside the FF */ + if (pl->max_op == IMGU_ABI_AF_MAX_OPERATIONS) { + first_process_lines = process_lines + pl->y_start + 2; + last_process_lines_in_grid = + (grid_last_line - first_process_lines) - + ((num_of_sets - 2) * process_lines) + 4; + process_lines_after_grid = + pl->image_height - grid_last_line - 4; + } else { + first_process_lines = process_lines + pl->y_start; + last_process_lines_in_grid = + (grid_last_line - first_process_lines) - + ((num_of_sets - 2) * process_lines); + process_lines_after_grid = pl->image_height - grid_last_line; + } + + num_of_process_lines = num_of_sets; + if (process_lines_after_grid > 0) + num_of_process_lines++; + + while (tr_idx < num_of_sets || pl_idx < num_of_process_lines) { + /* Read meta-data */ + if (pl_idx >= 2 || (pl_idx == 1 && num_of_sets == 1)) { + if (op_idx >= pl->max_op || tr_idx >= pl->max_tr) + return -EINVAL; + + p_op[op_idx].op_type = + IMGU_ABI_ACC_OPTYPE_TRANSFER_DATA; + + if (tr_idx == num_of_sets - 1) + /* The last operation is always a tr */ + p_op[op_idx].op_indicator = + IMGU_ABI_ACC_OP_END_OF_OPS; + else if (tr_idx == num_of_sets - 2) + if (process_lines_after_grid == 0) + /* + * No additional pl op left - + * this op is left as lats of cycle + */ + p_op[op_idx].op_indicator = + IMGU_ABI_ACC_OP_END_OF_ACK; + else + /* + * We still have to process-lines after + * the grid so have one more pl op + */ + p_op[op_idx].op_indicator = + IMGU_ABI_ACC_OP_IDLE; + else + /* Default - usually there's a pl after a tr */ + p_op[op_idx].op_indicator = + IMGU_ABI_ACC_OP_IDLE; + + op_idx++; + if (p_tr) { + p_tr[tr_idx].set_number = tr_set_num; + tr_set_num = 1 - tr_set_num; + } + tr_idx++; + } + + /* process_lines */ + if (pl_idx < num_of_process_lines) { + if (op_idx >= pl->max_op || pl_idx >= pl->max_tr) + return -EINVAL; + + p_op[op_idx].op_type = + IMGU_ABI_ACC_OPTYPE_PROCESS_LINES; + if (pl_idx == 0) + if (num_of_process_lines == 1) + /* Only one pl op */ + p_op[op_idx].op_indicator = + IMGU_ABI_ACC_OP_END_OF_ACK; + else + /* On init - do two pl ops */ + p_op[op_idx].op_indicator = + IMGU_ABI_ACC_OP_IDLE; + else + /* Usually pl is the end of the ack cycle */ + p_op[op_idx].op_indicator = + IMGU_ABI_ACC_OP_END_OF_ACK; + + op_idx++; + + if (pl_idx == 0) + /* First process line */ + p_pl[pl_idx].lines = first_process_lines; + else if (pl_idx == num_of_sets - 1) + /* Last in grid */ + p_pl[pl_idx].lines = last_process_lines_in_grid; + else if (pl_idx == num_of_process_lines - 1) + /* After the grid */ + p_pl[pl_idx].lines = process_lines_after_grid; + else + /* Inside the grid */ + p_pl[pl_idx].lines = process_lines; + + if (p_tr) { + p_pl[pl_idx].cfg_set = pl_cfg_set; + pl_cfg_set = 1 - pl_cfg_set; + } + pl_idx++; + } + } + + return 0; +} + +static int ipu3_css_af_ops_calc(struct ipu3_css *css, unsigned int pipe, + struct imgu_abi_af_config *af_config) +{ + struct imgu_abi_af_intra_frame_operations_data *to = + &af_config->operations_data; + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_fw_info *bi = + &css->fwp->binary_header[css_pipe->bindex]; + + struct process_lines pl = { + .image_height = css_pipe->rect[IPU3_CSS_RECT_BDS].height, + .grid_height = af_config->config.grid_cfg.height, + .block_height = + 1 << af_config->config.grid_cfg.block_height_log2, + .y_start = af_config->config.grid_cfg.y_start & + IPU3_UAPI_GRID_START_MASK, + .grid_height_per_slice = + af_config->stripes[0].grid_cfg.height_per_slice, + .max_op = IMGU_ABI_AF_MAX_OPERATIONS, + .max_tr = IMGU_ABI_AF_MAX_TRANSFERS, + .acc_enable = bi->info.isp.sp.enable.af, + }; + + return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data, + NULL); +} + +static int +ipu3_css_awb_fr_ops_calc(struct ipu3_css *css, unsigned int pipe, + struct imgu_abi_awb_fr_config *awb_fr_config) +{ + struct imgu_abi_awb_fr_intra_frame_operations_data *to = + &awb_fr_config->operations_data; + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_fw_info *bi = + &css->fwp->binary_header[css_pipe->bindex]; + struct process_lines pl = { + .image_height = css_pipe->rect[IPU3_CSS_RECT_BDS].height, + .grid_height = awb_fr_config->config.grid_cfg.height, + .block_height = + 1 << awb_fr_config->config.grid_cfg.block_height_log2, + .y_start = awb_fr_config->config.grid_cfg.y_start & + IPU3_UAPI_GRID_START_MASK, + .grid_height_per_slice = + awb_fr_config->stripes[0].grid_cfg.height_per_slice, + .max_op = IMGU_ABI_AWB_FR_MAX_OPERATIONS, + .max_tr = IMGU_ABI_AWB_FR_MAX_PROCESS_LINES, + .acc_enable = bi->info.isp.sp.enable.awb_fr_acc, + }; + + return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data, + NULL); +} + +static int ipu3_css_awb_ops_calc(struct ipu3_css *css, unsigned int pipe, + struct imgu_abi_awb_config *awb_config) +{ + struct imgu_abi_awb_intra_frame_operations_data *to = + &awb_config->operations_data; + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_fw_info *bi = + &css->fwp->binary_header[css_pipe->bindex]; + + struct process_lines pl = { + .image_height = css_pipe->rect[IPU3_CSS_RECT_BDS].height, + .grid_height = awb_config->config.grid.height, + .block_height = + 1 << awb_config->config.grid.block_height_log2, + .y_start = awb_config->config.grid.y_start, + .grid_height_per_slice = + awb_config->stripes[0].grid.height_per_slice, + .max_op = IMGU_ABI_AWB_MAX_OPERATIONS, + .max_tr = IMGU_ABI_AWB_MAX_TRANSFERS, + .acc_enable = bi->info.isp.sp.enable.awb_acc, + }; + + return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data, + to->transfer_data); +} + +static u16 ipu3_css_grid_end(u16 start, u8 width, u8 block_width_log2) +{ + return (start & IPU3_UAPI_GRID_START_MASK) + + (width << block_width_log2) - 1; +} + +static void ipu3_css_grid_end_calc(struct ipu3_uapi_grid_config *grid_cfg) +{ + grid_cfg->x_end = ipu3_css_grid_end(grid_cfg->x_start, grid_cfg->width, + grid_cfg->block_width_log2); + grid_cfg->y_end = ipu3_css_grid_end(grid_cfg->y_start, grid_cfg->height, + grid_cfg->block_height_log2); +} + +/****************** config computation *****************************/ + +static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe, + struct imgu_abi_acc_param *acc) +{ + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + const struct imgu_fw_info *bi = + &css->fwp->binary_header[css_pipe->bindex]; + struct ipu3_css_scaler_info scaler_luma, scaler_chroma; + const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes; + const unsigned int f = IPU3_UAPI_ISP_VEC_ELEMS * 2; + unsigned int bds_ds, i; + + memset(acc, 0, sizeof(*acc)); + + /* acc_param: osys_config */ + + if (ipu3_css_osys_calc(css, pipe, stripes, &acc->osys, &scaler_luma, + &scaler_chroma, acc->stripe.block_stripes)) + return -EINVAL; + + /* acc_param: stripe data */ + + /* + * For the striped case the approach is as follows: + * 1. down-scaled stripes are calculated - with 128 overlap + * (this is the main limiter therefore it's first) + * 2. input stripes are derived by up-scaling the down-scaled stripes + * (there are no alignment requirements on input stripes) + * 3. output stripes are derived from down-scaled stripes too + */ + + acc->stripe.num_of_stripes = stripes; + acc->stripe.input_frame.width = + css_pipe->queue[IPU3_CSS_QUEUE_IN].fmt.mpix.width; + acc->stripe.input_frame.height = + css_pipe->queue[IPU3_CSS_QUEUE_IN].fmt.mpix.height; + acc->stripe.input_frame.bayer_order = + css_pipe->queue[IPU3_CSS_QUEUE_IN].css_fmt->bayer_order; + + for (i = 0; i < stripes; i++) + acc->stripe.bds_out_stripes[i].height = + css_pipe->rect[IPU3_CSS_RECT_BDS].height; + acc->stripe.bds_out_stripes[0].offset = 0; + if (stripes <= 1) { + acc->stripe.bds_out_stripes[0].width = + ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, f); + } else { + /* Image processing is divided into two stripes */ + acc->stripe.bds_out_stripes[0].width = + acc->stripe.bds_out_stripes[1].width = + (css_pipe->rect[IPU3_CSS_RECT_BDS].width / 2 & ~(f - 1)) + f; + /* + * Sum of width of the two stripes should not be smaller + * than output width and must be even times of overlapping + * unit f. + */ + if ((css_pipe->rect[IPU3_CSS_RECT_BDS].width / f & 1) != + !!(css_pipe->rect[IPU3_CSS_RECT_BDS].width & (f - 1))) + acc->stripe.bds_out_stripes[0].width += f; + if ((css_pipe->rect[IPU3_CSS_RECT_BDS].width / f & 1) && + (css_pipe->rect[IPU3_CSS_RECT_BDS].width & (f - 1))) { + acc->stripe.bds_out_stripes[0].width += f; + acc->stripe.bds_out_stripes[1].width += f; + } + /* Overlap between stripes is IPU3_UAPI_ISP_VEC_ELEMS * 4 */ + acc->stripe.bds_out_stripes[1].offset = + acc->stripe.bds_out_stripes[0].width - 2 * f; + } + + acc->stripe.effective_stripes[0].height = + css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].height; + acc->stripe.effective_stripes[0].offset = 0; + acc->stripe.bds_out_stripes_no_overlap[0].height = + css_pipe->rect[IPU3_CSS_RECT_BDS].height; + acc->stripe.bds_out_stripes_no_overlap[0].offset = 0; + acc->stripe.output_stripes[0].height = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.height; + acc->stripe.output_stripes[0].offset = 0; + if (stripes <= 1) { + acc->stripe.down_scaled_stripes[0].width = + css_pipe->rect[IPU3_CSS_RECT_BDS].width; + acc->stripe.down_scaled_stripes[0].height = + css_pipe->rect[IPU3_CSS_RECT_BDS].height; + acc->stripe.down_scaled_stripes[0].offset = 0; + + acc->stripe.effective_stripes[0].width = + css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].width; + acc->stripe.bds_out_stripes_no_overlap[0].width = + ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, f); + + acc->stripe.output_stripes[0].width = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.width; + } else { /* Two stripes */ + bds_ds = css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].width * + IMGU_BDS_GRANULARITY / + css_pipe->rect[IPU3_CSS_RECT_BDS].width; + + acc->stripe.down_scaled_stripes[0] = + acc->stripe.bds_out_stripes[0]; + acc->stripe.down_scaled_stripes[1] = + acc->stripe.bds_out_stripes[1]; + if (!IS_ALIGNED(css_pipe->rect[IPU3_CSS_RECT_BDS].width, f)) + acc->stripe.down_scaled_stripes[1].width += + (css_pipe->rect[IPU3_CSS_RECT_BDS].width + & (f - 1)) - f; + + acc->stripe.effective_stripes[0].width = bds_ds * + acc->stripe.down_scaled_stripes[0].width / + IMGU_BDS_GRANULARITY; + acc->stripe.effective_stripes[1].width = bds_ds * + acc->stripe.down_scaled_stripes[1].width / + IMGU_BDS_GRANULARITY; + acc->stripe.effective_stripes[1].height = + css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].height; + acc->stripe.effective_stripes[1].offset = bds_ds * + acc->stripe.down_scaled_stripes[1].offset / + IMGU_BDS_GRANULARITY; + + acc->stripe.bds_out_stripes_no_overlap[0].width = + acc->stripe.bds_out_stripes_no_overlap[1].offset = + ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, 2 * f) / 2; + acc->stripe.bds_out_stripes_no_overlap[1].width = + DIV_ROUND_UP(css_pipe->rect[IPU3_CSS_RECT_BDS].width, f) + / 2 * f; + acc->stripe.bds_out_stripes_no_overlap[1].height = + css_pipe->rect[IPU3_CSS_RECT_BDS].height; + + acc->stripe.output_stripes[0].width = + acc->stripe.down_scaled_stripes[0].width - f; + acc->stripe.output_stripes[1].width = + acc->stripe.down_scaled_stripes[1].width - f; + acc->stripe.output_stripes[1].height = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.height; + acc->stripe.output_stripes[1].offset = + acc->stripe.output_stripes[0].width; + } + + acc->stripe.output_system_in_frame_width = + css_pipe->rect[IPU3_CSS_RECT_GDC].width; + acc->stripe.output_system_in_frame_height = + css_pipe->rect[IPU3_CSS_RECT_GDC].height; + + acc->stripe.effective_frame_width = + css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].width; + acc->stripe.bds_frame_width = css_pipe->rect[IPU3_CSS_RECT_BDS].width; + acc->stripe.out_frame_width = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.width; + acc->stripe.out_frame_height = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.height; + acc->stripe.gdc_in_buffer_width = + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperline / + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperpixel; + acc->stripe.gdc_in_buffer_height = + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].height; + acc->stripe.gdc_in_buffer_offset_x = IMGU_GDC_BUF_X; + acc->stripe.gdc_in_buffer_offset_y = IMGU_GDC_BUF_Y; + acc->stripe.display_frame_width = + css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.width; + acc->stripe.display_frame_height = + css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.height; + acc->stripe.bds_aligned_frame_width = + roundup(css_pipe->rect[IPU3_CSS_RECT_BDS].width, + 2 * IPU3_UAPI_ISP_VEC_ELEMS); + + if (stripes > 1) + acc->stripe.half_overlap_vectors = + IMGU_STRIPE_FIXED_HALF_OVERLAP; + else + acc->stripe.half_overlap_vectors = 0; + + return 0; +} + +static void ipu3_css_cfg_acc_dvs(struct ipu3_css *css, + struct imgu_abi_acc_param *acc, + unsigned int pipe) +{ + unsigned int i; + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + + /* Disable DVS statistics */ + acc->dvs_stat.operations_data.process_lines_data[0].lines = + css_pipe->rect[IPU3_CSS_RECT_BDS].height; + acc->dvs_stat.operations_data.process_lines_data[0].cfg_set = 0; + acc->dvs_stat.operations_data.ops[0].op_type = + IMGU_ABI_ACC_OPTYPE_PROCESS_LINES; + acc->dvs_stat.operations_data.ops[0].op_indicator = + IMGU_ABI_ACC_OP_NO_OPS; + for (i = 0; i < IMGU_ABI_DVS_STAT_LEVELS; i++) + acc->dvs_stat.cfg.grd_config[i].enable = 0; +} + +static void acc_bds_per_stripe_data(struct ipu3_css *css, + struct imgu_abi_acc_param *acc, + const int i, unsigned int pipe) +{ + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + + acc->bds.per_stripe.aligned_data[i].data.crop.hor_crop_en = 0; + acc->bds.per_stripe.aligned_data[i].data.crop.hor_crop_start = 0; + acc->bds.per_stripe.aligned_data[i].data.crop.hor_crop_end = 0; + acc->bds.per_stripe.aligned_data[i].data.hor_ctrl0 = + acc->bds.hor.hor_ctrl0; + acc->bds.per_stripe.aligned_data[i].data.hor_ctrl0.out_frame_width = + acc->stripe.down_scaled_stripes[i].width; + acc->bds.per_stripe.aligned_data[i].data.ver_ctrl1.out_frame_width = + acc->stripe.down_scaled_stripes[i].width; + acc->bds.per_stripe.aligned_data[i].data.ver_ctrl1.out_frame_height = + css_pipe->rect[IPU3_CSS_RECT_BDS].height; +} + +/* + * Configure `acc' parameters. `acc_old' contains the old values (or is NULL) + * and `acc_user' contains new prospective values. `use' contains flags + * telling which fields to take from the old values (or generate if it is NULL) + * and which to take from the new user values. + */ +int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, + struct ipu3_uapi_flags *use, + struct imgu_abi_acc_param *acc, + struct imgu_abi_acc_param *acc_old, + struct ipu3_uapi_acc_param *acc_user) +{ + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + const struct imgu_fw_info *bi = + &css->fwp->binary_header[css_pipe->bindex]; + const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes; + const unsigned int tnr_frame_width = + acc->stripe.bds_aligned_frame_width; + const unsigned int min_overlap = 10; + const struct v4l2_pix_format_mplane *pixm = + &css_pipe->queue[IPU3_CSS_QUEUE_IN].fmt.mpix; + const struct ipu3_css_bds_config *cfg_bds; + struct imgu_abi_input_feeder_data *feeder_data; + + unsigned int bds_ds, ofs_x, ofs_y, i, width, height; + u8 b_w_log2; /* Block width log2 */ + + /* Update stripe using chroma and luma */ + + if (ipu3_css_cfg_acc_stripe(css, pipe, acc)) + return -EINVAL; + + /* acc_param: input_feeder_config */ + + ofs_x = ((pixm->width - + css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].width) >> 1) & ~1; + ofs_x += css_pipe->queue[IPU3_CSS_QUEUE_IN].css_fmt->bayer_order == + IMGU_ABI_BAYER_ORDER_RGGB || + css_pipe->queue[IPU3_CSS_QUEUE_IN].css_fmt->bayer_order == + IMGU_ABI_BAYER_ORDER_GBRG ? 1 : 0; + ofs_y = ((pixm->height - + css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].height) >> 1) & ~1; + ofs_y += css_pipe->queue[IPU3_CSS_QUEUE_IN].css_fmt->bayer_order == + IMGU_ABI_BAYER_ORDER_BGGR || + css_pipe->queue[IPU3_CSS_QUEUE_IN].css_fmt->bayer_order == + IMGU_ABI_BAYER_ORDER_GBRG ? 1 : 0; + acc->input_feeder.data.row_stride = pixm->plane_fmt[0].bytesperline; + acc->input_feeder.data.start_row_address = + ofs_x / IMGU_PIXELS_PER_WORD * IMGU_BYTES_PER_WORD + + ofs_y * acc->input_feeder.data.row_stride; + acc->input_feeder.data.start_pixel = ofs_x % IMGU_PIXELS_PER_WORD; + + acc->input_feeder.data_per_stripe.input_feeder_data[0].data = + acc->input_feeder.data; + + ofs_x += acc->stripe.effective_stripes[1].offset; + + feeder_data = + &acc->input_feeder.data_per_stripe.input_feeder_data[1].data; + feeder_data->row_stride = acc->input_feeder.data.row_stride; + feeder_data->start_row_address = + ofs_x / IMGU_PIXELS_PER_WORD * IMGU_BYTES_PER_WORD + + ofs_y * acc->input_feeder.data.row_stride; + feeder_data->start_pixel = ofs_x % IMGU_PIXELS_PER_WORD; + + /* acc_param: bnr_static_config */ + + /* + * Originate from user or be the original default values if user has + * never set them before, when user gives a new set of parameters, + * for each chunk in the parameter structure there is a flag use->xxx + * whether to use the user-provided parameter or not. If not, the + * parameter remains unchanged in the driver: + * it's value is taken from acc_old. + */ + if (use && use->acc_bnr) { + /* Take values from user */ + acc->bnr = acc_user->bnr; + } else if (acc_old) { + /* Use old value */ + acc->bnr = acc_old->bnr; + } else { + /* Calculate from scratch */ + acc->bnr = ipu3_css_bnr_defaults; + } + + acc->bnr.column_size = tnr_frame_width; + + /* acc_param: bnr_static_config_green_disparity */ + + if (use && use->acc_green_disparity) { + /* Take values from user */ + acc->green_disparity = acc_user->green_disparity; + } else if (acc_old) { + /* Use old value */ + acc->green_disparity = acc_old->green_disparity; + } else { + /* Calculate from scratch */ + memset(&acc->green_disparity, 0, sizeof(acc->green_disparity)); + } + + /* acc_param: dm_config */ + + if (use && use->acc_dm) { + /* Take values from user */ + acc->dm = acc_user->dm; + } else if (acc_old) { + /* Use old value */ + acc->dm = acc_old->dm; + } else { + /* Calculate from scratch */ + acc->dm = ipu3_css_dm_defaults; + } + + acc->dm.frame_width = tnr_frame_width; + + /* acc_param: ccm_mat_config */ + + if (use && use->acc_ccm) { + /* Take values from user */ + acc->ccm = acc_user->ccm; + } else if (acc_old) { + /* Use old value */ + acc->ccm = acc_old->ccm; + } else { + /* Calculate from scratch */ + acc->ccm = ipu3_css_ccm_defaults; + } + + /* acc_param: gamma_config */ + + if (use && use->acc_gamma) { + /* Take values from user */ + acc->gamma = acc_user->gamma; + } else if (acc_old) { + /* Use old value */ + acc->gamma = acc_old->gamma; + } else { + /* Calculate from scratch */ + acc->gamma.gc_ctrl.enable = 1; + acc->gamma.gc_lut = ipu3_css_gamma_lut; + } + + /* acc_param: csc_mat_config */ + + if (use && use->acc_csc) { + /* Take values from user */ + acc->csc = acc_user->csc; + } else if (acc_old) { + /* Use old value */ + acc->csc = acc_old->csc; + } else { + /* Calculate from scratch */ + acc->csc = ipu3_css_csc_defaults; + } + + /* acc_param: cds_params */ + + if (use && use->acc_cds) { + /* Take values from user */ + acc->cds = acc_user->cds; + } else if (acc_old) { + /* Use old value */ + acc->cds = acc_old->cds; + } else { + /* Calculate from scratch */ + acc->cds = ipu3_css_cds_defaults; + } + + /* acc_param: shd_config */ + + if (use && use->acc_shd) { + /* Take values from user */ + acc->shd.shd = acc_user->shd.shd; + acc->shd.shd_lut = acc_user->shd.shd_lut; + } else if (acc_old) { + /* Use old value */ + acc->shd.shd = acc_old->shd.shd; + acc->shd.shd_lut = acc_old->shd.shd_lut; + } else { + /* Calculate from scratch */ + acc->shd.shd = ipu3_css_shd_defaults; + memset(&acc->shd.shd_lut, 0, sizeof(acc->shd.shd_lut)); + } + + if (acc->shd.shd.grid.width <= 0) + return -EINVAL; + + acc->shd.shd.grid.grid_height_per_slice = + IMGU_ABI_SHD_MAX_CELLS_PER_SET / acc->shd.shd.grid.width; + + if (acc->shd.shd.grid.grid_height_per_slice <= 0) + return -EINVAL; + + acc->shd.shd.general.init_set_vrt_offst_ul = + (-acc->shd.shd.grid.y_start >> + acc->shd.shd.grid.block_height_log2) % + acc->shd.shd.grid.grid_height_per_slice; + + if (ipu3_css_shd_ops_calc(&acc->shd.shd_ops, &acc->shd.shd.grid, + css_pipe->rect[IPU3_CSS_RECT_BDS].height)) + return -EINVAL; + + /* acc_param: dvs_stat_config */ + ipu3_css_cfg_acc_dvs(css, acc, pipe); + + /* acc_param: yuvp1_iefd_config */ + + if (use && use->acc_iefd) { + /* Take values from user */ + acc->iefd = acc_user->iefd; + } else if (acc_old) { + /* Use old value */ + acc->iefd = acc_old->iefd; + } else { + /* Calculate from scratch */ + acc->iefd = ipu3_css_iefd_defaults; + } + + /* acc_param: yuvp1_yds_config yds_c0 */ + + if (use && use->acc_yds_c0) { + /* Take values from user */ + acc->yds_c0 = acc_user->yds_c0; + } else if (acc_old) { + /* Use old value */ + acc->yds_c0 = acc_old->yds_c0; + } else { + /* Calculate from scratch */ + acc->yds_c0 = ipu3_css_yds_defaults; + } + + /* acc_param: yuvp1_chnr_config chnr_c0 */ + + if (use && use->acc_chnr_c0) { + /* Take values from user */ + acc->chnr_c0 = acc_user->chnr_c0; + } else if (acc_old) { + /* Use old value */ + acc->chnr_c0 = acc_old->chnr_c0; + } else { + /* Calculate from scratch */ + acc->chnr_c0 = ipu3_css_chnr_defaults; + } + + /* acc_param: yuvp1_y_ee_nr_config */ + + if (use && use->acc_y_ee_nr) { + /* Take values from user */ + acc->y_ee_nr = acc_user->y_ee_nr; + } else if (acc_old) { + /* Use old value */ + acc->y_ee_nr = acc_old->y_ee_nr; + } else { + /* Calculate from scratch */ + acc->y_ee_nr = ipu3_css_y_ee_nr_defaults; + } + + /* acc_param: yuvp1_yds_config yds */ + + if (use && use->acc_yds) { + /* Take values from user */ + acc->yds = acc_user->yds; + } else if (acc_old) { + /* Use old value */ + acc->yds = acc_old->yds; + } else { + /* Calculate from scratch */ + acc->yds = ipu3_css_yds_defaults; + } + + /* acc_param: yuvp1_chnr_config chnr */ + + if (use && use->acc_chnr) { + /* Take values from user */ + acc->chnr = acc_user->chnr; + } else if (acc_old) { + /* Use old value */ + acc->chnr = acc_old->chnr; + } else { + /* Calculate from scratch */ + acc->chnr = ipu3_css_chnr_defaults; + } + + /* acc_param: yuvp2_y_tm_lut_static_config */ + + for (i = 0; i < IMGU_ABI_YUVP2_YTM_LUT_ENTRIES; i++) + acc->ytm.entries[i] = i * 32; + acc->ytm.enable = 0; /* Always disabled on IPU3 */ + + /* acc_param: yuvp1_yds_config yds2 */ + + if (use && use->acc_yds2) { + /* Take values from user */ + acc->yds2 = acc_user->yds2; + } else if (acc_old) { + /* Use old value */ + acc->yds2 = acc_old->yds2; + } else { + /* Calculate from scratch */ + acc->yds2 = ipu3_css_yds_defaults; + } + + /* acc_param: yuvp2_tcc_static_config */ + + if (use && use->acc_tcc) { + /* Take values from user */ + acc->tcc = acc_user->tcc; + } else if (acc_old) { + /* Use old value */ + acc->tcc = acc_old->tcc; + } else { + /* Calculate from scratch */ + memset(&acc->tcc, 0, sizeof(acc->tcc)); + + acc->tcc.gen_control.en = 1; + acc->tcc.gen_control.blend_shift = 3; + acc->tcc.gen_control.gain_according_to_y_only = 1; + acc->tcc.gen_control.gamma = 8; + acc->tcc.gen_control.delta = 0; + + for (i = 0; i < IPU3_UAPI_YUVP2_TCC_MACC_TABLE_ELEMENTS; i++) { + acc->tcc.macc_table.entries[i].a = 1024; + acc->tcc.macc_table.entries[i].b = 0; + acc->tcc.macc_table.entries[i].c = 0; + acc->tcc.macc_table.entries[i].d = 1024; + } + + acc->tcc.inv_y_lut.entries[6] = 1023; + for (i = 7; i < IPU3_UAPI_YUVP2_TCC_INV_Y_LUT_ELEMENTS; i++) + acc->tcc.inv_y_lut.entries[i] = 1024 >> (i - 6); + + acc->tcc.gain_pcwl = ipu3_css_tcc_gain_pcwl_lut; + acc->tcc.r_sqr_lut = ipu3_css_tcc_r_sqr_lut; + } + + /* acc_param: dpc_config */ + + if (use && use->acc_dpc) + return -EINVAL; /* Not supported yet */ + + /* Just disable by default */ + memset(&acc->dpc, 0, sizeof(acc->dpc)); + + /* acc_param: bds_config */ + + bds_ds = (css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].height * + IMGU_BDS_GRANULARITY) / css_pipe->rect[IPU3_CSS_RECT_BDS].height; + if (bds_ds < IMGU_BDS_MIN_SF_INV || + bds_ds - IMGU_BDS_MIN_SF_INV >= ARRAY_SIZE(ipu3_css_bds_configs)) + return -EINVAL; + + cfg_bds = &ipu3_css_bds_configs[bds_ds - IMGU_BDS_MIN_SF_INV]; + acc->bds.hor.hor_ctrl1.hor_crop_en = 0; + acc->bds.hor.hor_ctrl1.hor_crop_start = 0; + acc->bds.hor.hor_ctrl1.hor_crop_end = 0; + acc->bds.hor.hor_ctrl0.sample_patrn_length = + cfg_bds->sample_patrn_length; + acc->bds.hor.hor_ctrl0.hor_ds_en = cfg_bds->hor_ds_en; + acc->bds.hor.hor_ctrl0.min_clip_val = IMGU_BDS_MIN_CLIP_VAL; + acc->bds.hor.hor_ctrl0.max_clip_val = IMGU_BDS_MAX_CLIP_VAL; + acc->bds.hor.hor_ctrl0.out_frame_width = + css_pipe->rect[IPU3_CSS_RECT_BDS].width; + acc->bds.hor.hor_ptrn_arr = cfg_bds->ptrn_arr; + acc->bds.hor.hor_phase_arr = cfg_bds->hor_phase_arr; + acc->bds.hor.hor_ctrl2.input_frame_height = + css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].height; + acc->bds.ver.ver_ctrl0.min_clip_val = IMGU_BDS_MIN_CLIP_VAL; + acc->bds.ver.ver_ctrl0.max_clip_val = IMGU_BDS_MAX_CLIP_VAL; + acc->bds.ver.ver_ctrl0.sample_patrn_length = + cfg_bds->sample_patrn_length; + acc->bds.ver.ver_ctrl0.ver_ds_en = cfg_bds->ver_ds_en; + acc->bds.ver.ver_ptrn_arr = cfg_bds->ptrn_arr; + acc->bds.ver.ver_phase_arr = cfg_bds->ver_phase_arr; + acc->bds.ver.ver_ctrl1.out_frame_width = + css_pipe->rect[IPU3_CSS_RECT_BDS].width; + acc->bds.ver.ver_ctrl1.out_frame_height = + css_pipe->rect[IPU3_CSS_RECT_BDS].height; + for (i = 0; i < stripes; i++) + acc_bds_per_stripe_data(css, acc, i, pipe); + + acc->bds.enabled = cfg_bds->hor_ds_en || cfg_bds->ver_ds_en; + + /* acc_param: anr_config */ + + if (use && use->acc_anr) { + /* Take values from user */ + acc->anr.transform = acc_user->anr.transform; + acc->anr.stitch.anr_stitch_en = + acc_user->anr.stitch.anr_stitch_en; + memcpy(acc->anr.stitch.pyramid, acc_user->anr.stitch.pyramid, + sizeof(acc->anr.stitch.pyramid)); + } else if (acc_old) { + /* Use old value */ + acc->anr.transform = acc_old->anr.transform; + acc->anr.stitch.anr_stitch_en = + acc_old->anr.stitch.anr_stitch_en; + memcpy(acc->anr.stitch.pyramid, acc_old->anr.stitch.pyramid, + sizeof(acc->anr.stitch.pyramid)); + } else { + /* Calculate from scratch */ + acc->anr = ipu3_css_anr_defaults; + } + + /* Always enabled */ + acc->anr.search.enable = 1; + acc->anr.transform.enable = 1; + acc->anr.tile2strm.enable = 1; + acc->anr.tile2strm.frame_width = + ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, IMGU_ISP_VMEM_ALIGN); + acc->anr.search.frame_width = acc->anr.tile2strm.frame_width; + acc->anr.stitch.frame_width = acc->anr.tile2strm.frame_width; + acc->anr.tile2strm.frame_height = css_pipe->rect[IPU3_CSS_RECT_BDS].height; + acc->anr.search.frame_height = acc->anr.tile2strm.frame_height; + acc->anr.stitch.frame_height = acc->anr.tile2strm.frame_height; + + width = ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, IMGU_ISP_VMEM_ALIGN); + height = css_pipe->rect[IPU3_CSS_RECT_BDS].height; + + if (acc->anr.transform.xreset + width > IPU3_UAPI_ANR_MAX_RESET) + acc->anr.transform.xreset = IPU3_UAPI_ANR_MAX_RESET - width; + if (acc->anr.transform.xreset < IPU3_UAPI_ANR_MIN_RESET) + acc->anr.transform.xreset = IPU3_UAPI_ANR_MIN_RESET; + + if (acc->anr.transform.yreset + height > IPU3_UAPI_ANR_MAX_RESET) + acc->anr.transform.yreset = IPU3_UAPI_ANR_MAX_RESET - height; + if (acc->anr.transform.yreset < IPU3_UAPI_ANR_MIN_RESET) + acc->anr.transform.yreset = IPU3_UAPI_ANR_MIN_RESET; + + /* acc_param: awb_fr_config */ + + if (use && use->acc_awb_fr) { + /* Take values from user */ + acc->awb_fr.config = acc_user->awb_fr; + } else if (acc_old) { + /* Use old value */ + acc->awb_fr.config = acc_old->awb_fr.config; + } else { + /* Set from scratch */ + acc->awb_fr.config = ipu3_css_awb_fr_defaults; + } + + ipu3_css_grid_end_calc(&acc->awb_fr.config.grid_cfg); + + if (acc->awb_fr.config.grid_cfg.width <= 0) + return -EINVAL; + + acc->awb_fr.config.grid_cfg.height_per_slice = + IMGU_ABI_AWB_FR_MAX_CELLS_PER_SET / + acc->awb_fr.config.grid_cfg.width; + + for (i = 0; i < stripes; i++) + acc->awb_fr.stripes[i] = acc->awb_fr.config; + + if (acc->awb_fr.config.grid_cfg.x_start >= + acc->stripe.down_scaled_stripes[1].offset + min_overlap) { + /* Enable only for rightmost stripe, disable left */ + acc->awb_fr.stripes[0].grid_cfg.y_start &= + ~IPU3_UAPI_GRID_Y_START_EN; + } else if (acc->awb_fr.config.grid_cfg.x_end <= + acc->stripe.bds_out_stripes[0].width - min_overlap) { + /* Enable only for leftmost stripe, disable right */ + acc->awb_fr.stripes[1].grid_cfg.y_start &= + ~IPU3_UAPI_GRID_Y_START_EN; + } else { + /* Enable for both stripes */ + u16 end; /* width for grid end */ + + acc->awb_fr.stripes[0].grid_cfg.width = + (acc->stripe.bds_out_stripes[0].width - min_overlap - + acc->awb_fr.config.grid_cfg.x_start + 1) >> + acc->awb_fr.config.grid_cfg.block_width_log2; + acc->awb_fr.stripes[1].grid_cfg.width = + acc->awb_fr.config.grid_cfg.width - + acc->awb_fr.stripes[0].grid_cfg.width; + + b_w_log2 = acc->awb_fr.stripes[0].grid_cfg.block_width_log2; + end = ipu3_css_grid_end(acc->awb_fr.stripes[0].grid_cfg.x_start, + acc->awb_fr.stripes[0].grid_cfg.width, + b_w_log2); + acc->awb_fr.stripes[0].grid_cfg.x_end = end; + + acc->awb_fr.stripes[1].grid_cfg.x_start = + (acc->awb_fr.stripes[0].grid_cfg.x_end + 1 - + acc->stripe.down_scaled_stripes[1].offset) & + IPU3_UAPI_GRID_START_MASK; + b_w_log2 = acc->awb_fr.stripes[1].grid_cfg.block_width_log2; + end = ipu3_css_grid_end(acc->awb_fr.stripes[1].grid_cfg.x_start, + acc->awb_fr.stripes[1].grid_cfg.width, + b_w_log2); + acc->awb_fr.stripes[1].grid_cfg.x_end = end; + + /* + * To reduce complexity of debubbling and loading + * statistics fix grid_height_per_slice to 1 for both + * stripes. + */ + for (i = 0; i < stripes; i++) + acc->awb_fr.stripes[i].grid_cfg.height_per_slice = 1; + } + + if (ipu3_css_awb_fr_ops_calc(css, pipe, &acc->awb_fr)) + return -EINVAL; + + /* acc_param: ae_config */ + + if (use && use->acc_ae) { + /* Take values from user */ + acc->ae.grid_cfg = acc_user->ae.grid_cfg; + acc->ae.ae_ccm = acc_user->ae.ae_ccm; + for (i = 0; i < IPU3_UAPI_AE_WEIGHTS; i++) + acc->ae.weights[i] = acc_user->ae.weights[i]; + } else if (acc_old) { + /* Use old value */ + acc->ae.grid_cfg = acc_old->ae.grid_cfg; + acc->ae.ae_ccm = acc_old->ae.ae_ccm; + for (i = 0; i < IPU3_UAPI_AE_WEIGHTS; i++) + acc->ae.weights[i] = acc_old->ae.weights[i]; + } else { + /* Set from scratch */ + static const struct ipu3_uapi_ae_weight_elem + weight_def = { 1, 1, 1, 1, 1, 1, 1, 1 }; + + acc->ae.grid_cfg = ipu3_css_ae_grid_defaults; + acc->ae.ae_ccm = ipu3_css_ae_ccm_defaults; + for (i = 0; i < IPU3_UAPI_AE_WEIGHTS; i++) + acc->ae.weights[i] = weight_def; + } + + b_w_log2 = acc->ae.grid_cfg.block_width_log2; + acc->ae.grid_cfg.x_end = ipu3_css_grid_end(acc->ae.grid_cfg.x_start, + acc->ae.grid_cfg.width, + b_w_log2); + b_w_log2 = acc->ae.grid_cfg.block_height_log2; + acc->ae.grid_cfg.y_end = ipu3_css_grid_end(acc->ae.grid_cfg.y_start, + acc->ae.grid_cfg.height, + b_w_log2); + + for (i = 0; i < stripes; i++) + acc->ae.stripes[i].grid = acc->ae.grid_cfg; + + if (acc->ae.grid_cfg.x_start >= + acc->stripe.down_scaled_stripes[1].offset) { + /* Enable only for rightmost stripe, disable left */ + acc->ae.stripes[0].grid.ae_en = 0; + } else if (acc->ae.grid_cfg.x_end <= + acc->stripe.bds_out_stripes[0].width) { + /* Enable only for leftmost stripe, disable right */ + acc->ae.stripes[1].grid.ae_en = 0; + } else { + /* Enable for both stripes */ + u8 b_w_log2; + + acc->ae.stripes[0].grid.width = + (acc->stripe.bds_out_stripes[0].width - + acc->ae.grid_cfg.x_start + 1) >> + acc->ae.grid_cfg.block_width_log2; + + acc->ae.stripes[1].grid.width = + acc->ae.grid_cfg.width - acc->ae.stripes[0].grid.width; + + b_w_log2 = acc->ae.stripes[0].grid.block_width_log2; + acc->ae.stripes[0].grid.x_end = + ipu3_css_grid_end(acc->ae.stripes[0].grid.x_start, + acc->ae.stripes[0].grid.width, + b_w_log2); + + acc->ae.stripes[1].grid.x_start = + (acc->ae.stripes[0].grid.x_end + 1 - + acc->stripe.down_scaled_stripes[1].offset) & + IPU3_UAPI_GRID_START_MASK; + b_w_log2 = acc->ae.stripes[1].grid.block_width_log2; + acc->ae.stripes[1].grid.x_end = + ipu3_css_grid_end(acc->ae.stripes[1].grid.x_start, + acc->ae.stripes[1].grid.width, + b_w_log2); + } + + /* acc_param: af_config */ + + if (use && use->acc_af) { + /* Take values from user */ + acc->af.config.filter_config = acc_user->af.filter_config; + acc->af.config.grid_cfg = acc_user->af.grid_cfg; + } else if (acc_old) { + /* Use old value */ + acc->af.config = acc_old->af.config; + } else { + /* Set from scratch */ + acc->af.config.filter_config = + ipu3_css_af_defaults.filter_config; + acc->af.config.grid_cfg = ipu3_css_af_defaults.grid_cfg; + } + + ipu3_css_grid_end_calc(&acc->af.config.grid_cfg); + + if (acc->af.config.grid_cfg.width <= 0) + return -EINVAL; + + acc->af.config.grid_cfg.height_per_slice = + IMGU_ABI_AF_MAX_CELLS_PER_SET / acc->af.config.grid_cfg.width; + acc->af.config.frame_size.width = + ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, IMGU_ISP_VMEM_ALIGN); + acc->af.config.frame_size.height = + css_pipe->rect[IPU3_CSS_RECT_BDS].height; + + if (acc->stripe.bds_out_stripes[0].width <= min_overlap) + return -EINVAL; + + for (i = 0; i < stripes; i++) { + acc->af.stripes[i].grid_cfg = acc->af.config.grid_cfg; + acc->af.stripes[i].frame_size.height = + css_pipe->rect[IPU3_CSS_RECT_BDS].height; + acc->af.stripes[i].frame_size.width = + acc->stripe.bds_out_stripes[i].width; + } + + if (acc->af.config.grid_cfg.x_start >= + acc->stripe.down_scaled_stripes[1].offset + min_overlap) { + /* Enable only for rightmost stripe, disable left */ + acc->af.stripes[0].grid_cfg.y_start &= + ~IPU3_UAPI_GRID_Y_START_EN; + } else if (acc->af.config.grid_cfg.x_end <= + acc->stripe.bds_out_stripes[0].width - min_overlap) { + /* Enable only for leftmost stripe, disable right */ + acc->af.stripes[1].grid_cfg.y_start &= + ~IPU3_UAPI_GRID_Y_START_EN; + } else { + /* Enable for both stripes */ + + acc->af.stripes[0].grid_cfg.width = + (acc->stripe.bds_out_stripes[0].width - min_overlap - + acc->af.config.grid_cfg.x_start + 1) >> + acc->af.config.grid_cfg.block_width_log2; + acc->af.stripes[1].grid_cfg.width = + acc->af.config.grid_cfg.width - + acc->af.stripes[0].grid_cfg.width; + + b_w_log2 = acc->af.stripes[0].grid_cfg.block_width_log2; + acc->af.stripes[0].grid_cfg.x_end = + ipu3_css_grid_end(acc->af.stripes[0].grid_cfg.x_start, + acc->af.stripes[0].grid_cfg.width, + b_w_log2); + + acc->af.stripes[1].grid_cfg.x_start = + (acc->af.stripes[0].grid_cfg.x_end + 1 - + acc->stripe.down_scaled_stripes[1].offset) & + IPU3_UAPI_GRID_START_MASK; + + b_w_log2 = acc->af.stripes[1].grid_cfg.block_width_log2; + acc->af.stripes[1].grid_cfg.x_end = + ipu3_css_grid_end(acc->af.stripes[1].grid_cfg.x_start, + acc->af.stripes[1].grid_cfg.width, + b_w_log2); + + /* + * To reduce complexity of debubbling and loading statistics + * fix grid_height_per_slice to 1 for both stripes + */ + for (i = 0; i < stripes; i++) + acc->af.stripes[i].grid_cfg.height_per_slice = 1; + } + + if (ipu3_css_af_ops_calc(css, pipe, &acc->af)) + return -EINVAL; + + /* acc_param: awb_config */ + + if (use && use->acc_awb) { + /* Take values from user */ + acc->awb.config = acc_user->awb.config; + } else if (acc_old) { + /* Use old value */ + acc->awb.config = acc_old->awb.config; + } else { + /* Set from scratch */ + acc->awb.config = ipu3_css_awb_defaults; + } + + if (acc->awb.config.grid.width <= 0) + return -EINVAL; + + acc->awb.config.grid.height_per_slice = + IMGU_ABI_AWB_MAX_CELLS_PER_SET / acc->awb.config.grid.width, + ipu3_css_grid_end_calc(&acc->awb.config.grid); + + for (i = 0; i < stripes; i++) + acc->awb.stripes[i] = acc->awb.config; + + if (acc->awb.config.grid.x_start >= + acc->stripe.down_scaled_stripes[1].offset + min_overlap) { + /* Enable only for rightmost stripe, disable left */ + acc->awb.stripes[0].rgbs_thr_b &= ~IPU3_UAPI_AWB_RGBS_THR_B_EN; + } else if (acc->awb.config.grid.x_end <= + acc->stripe.bds_out_stripes[0].width - min_overlap) { + /* Enable only for leftmost stripe, disable right */ + acc->awb.stripes[1].rgbs_thr_b &= ~IPU3_UAPI_AWB_RGBS_THR_B_EN; + } else { + /* Enable for both stripes */ + + acc->awb.stripes[0].grid.width = + (acc->stripe.bds_out_stripes[0].width - + acc->awb.config.grid.x_start + 1) >> + acc->awb.config.grid.block_width_log2; + acc->awb.stripes[1].grid.width = acc->awb.config.grid.width - + acc->awb.stripes[0].grid.width; + + b_w_log2 = acc->awb.stripes[0].grid.block_width_log2; + acc->awb.stripes[0].grid.x_end = + ipu3_css_grid_end(acc->awb.stripes[0].grid.x_start, + acc->awb.stripes[0].grid.width, + b_w_log2); + + acc->awb.stripes[1].grid.x_start = + (acc->awb.stripes[0].grid.x_end + 1 - + acc->stripe.down_scaled_stripes[1].offset) & + IPU3_UAPI_GRID_START_MASK; + + b_w_log2 = acc->awb.stripes[1].grid.block_width_log2; + acc->awb.stripes[1].grid.x_end = + ipu3_css_grid_end(acc->awb.stripes[1].grid.x_start, + acc->awb.stripes[1].grid.width, + b_w_log2); + + /* + * To reduce complexity of debubbling and loading statistics + * fix grid_height_per_slice to 1 for both stripes + */ + for (i = 0; i < stripes; i++) + acc->awb.stripes[i].grid.height_per_slice = 1; + } + + if (ipu3_css_awb_ops_calc(css, pipe, &acc->awb)) + return -EINVAL; + + return 0; +} + +/* + * Fill the indicated structure in `new_binary_params' from the possible + * sources based on `use_user' flag: if the flag is false, copy from + * `old_binary_params', or if the flag is true, copy from `user_setting' + * and return NULL (or error pointer on error). + * If the flag is false and `old_binary_params' is NULL, return pointer + * to the structure inside `new_binary_params'. In that case the caller + * should calculate and fill the structure from scratch. + */ +static void *ipu3_css_cfg_copy(struct ipu3_css *css, + unsigned int pipe, bool use_user, + void *user_setting, void *old_binary_params, + void *new_binary_params, + enum imgu_abi_memories m, + struct imgu_fw_isp_parameter *par, + size_t par_size) +{ + const enum imgu_abi_param_class c = IMGU_ABI_PARAM_CLASS_PARAM; + void *new_setting, *old_setting; + + new_setting = ipu3_css_fw_pipeline_params(css, pipe, c, m, par, + par_size, new_binary_params); + if (!new_setting) + return ERR_PTR(-EPROTO); /* Corrupted firmware */ + + if (use_user) { + /* Take new user parameters */ + memcpy(new_setting, user_setting, par_size); + } else if (old_binary_params) { + /* Take previous value */ + old_setting = ipu3_css_fw_pipeline_params(css, pipe, c, m, par, + par_size, + old_binary_params); + if (!old_setting) + return ERR_PTR(-EPROTO); + memcpy(new_setting, old_setting, par_size); + } else { + return new_setting; /* Need to calculate */ + } + + return NULL; /* Copied from other value */ +} + +/* + * Configure VMEM0 parameters (late binding parameters). + */ +int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, + struct ipu3_uapi_flags *use, + void *vmem0, void *vmem0_old, + struct ipu3_uapi_params *user) +{ + const struct imgu_fw_info *bi = + &css->fwp->binary_header[css->pipes[pipe].bindex]; + struct imgu_fw_param_memory_offsets *pofs = (void *)css->fwp + + bi->blob.memory_offsets.offsets[IMGU_ABI_PARAM_CLASS_PARAM]; + struct ipu3_uapi_isp_lin_vmem_params *lin_vmem = NULL; + struct ipu3_uapi_isp_tnr3_vmem_params *tnr_vmem = NULL; + struct ipu3_uapi_isp_xnr3_vmem_params *xnr_vmem = NULL; + const enum imgu_abi_param_class c = IMGU_ABI_PARAM_CLASS_PARAM; + const enum imgu_abi_memories m = IMGU_ABI_MEM_ISP_VMEM0; + unsigned int i; + + /* Configure VMEM0 */ + + memset(vmem0, 0, bi->info.isp.sp.mem_initializers.params[c][m].size); + + /* Configure Linearization VMEM0 parameters */ + + lin_vmem = ipu3_css_cfg_copy(css, pipe, use && use->lin_vmem_params, + &user->lin_vmem_params, vmem0_old, vmem0, + m, &pofs->vmem.lin, sizeof(*lin_vmem)); + if (!IS_ERR_OR_NULL(lin_vmem)) { + /* Generate parameter from scratch */ + for (i = 0; i < IPU3_UAPI_LIN_LUT_SIZE; i++) { + lin_vmem->lin_lutlow_gr[i] = 32 * i; + lin_vmem->lin_lutlow_r[i] = 32 * i; + lin_vmem->lin_lutlow_b[i] = 32 * i; + lin_vmem->lin_lutlow_gb[i] = 32 * i; + + lin_vmem->lin_lutdif_gr[i] = 32; + lin_vmem->lin_lutdif_r[i] = 32; + lin_vmem->lin_lutdif_b[i] = 32; + lin_vmem->lin_lutdif_gb[i] = 32; + } + } + + /* Configure TNR3 VMEM parameters */ + if (css->pipes[pipe].pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { + tnr_vmem = ipu3_css_cfg_copy(css, pipe, + use && use->tnr3_vmem_params, + &user->tnr3_vmem_params, + vmem0_old, vmem0, m, + &pofs->vmem.tnr3, + sizeof(*tnr_vmem)); + if (!IS_ERR_OR_NULL(tnr_vmem)) { + /* Generate parameter from scratch */ + for (i = 0; i < IPU3_UAPI_ISP_TNR3_VMEM_LEN; i++) + tnr_vmem->sigma[i] = 256; + } + } + i = IPU3_UAPI_ISP_TNR3_VMEM_LEN; + + /* Configure XNR3 VMEM parameters */ + + xnr_vmem = ipu3_css_cfg_copy(css, pipe, use && use->xnr3_vmem_params, + &user->xnr3_vmem_params, vmem0_old, vmem0, + m, &pofs->vmem.xnr3, sizeof(*xnr_vmem)); + if (!IS_ERR_OR_NULL(xnr_vmem)) { + xnr_vmem->x[i] = ipu3_css_xnr3_vmem_defaults.x + [i % IMGU_XNR3_VMEM_LUT_LEN]; + xnr_vmem->a[i] = ipu3_css_xnr3_vmem_defaults.a + [i % IMGU_XNR3_VMEM_LUT_LEN]; + xnr_vmem->b[i] = ipu3_css_xnr3_vmem_defaults.b + [i % IMGU_XNR3_VMEM_LUT_LEN]; + xnr_vmem->c[i] = ipu3_css_xnr3_vmem_defaults.c + [i % IMGU_XNR3_VMEM_LUT_LEN]; + } + + return IS_ERR(lin_vmem) || IS_ERR(tnr_vmem) || IS_ERR(xnr_vmem) ? + -EPROTO : 0; +} + +/* + * Configure DMEM0 parameters (late binding parameters). + */ +int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, + struct ipu3_uapi_flags *use, + void *dmem0, void *dmem0_old, + struct ipu3_uapi_params *user) +{ + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + const struct imgu_fw_info *bi = + &css->fwp->binary_header[css_pipe->bindex]; + struct imgu_fw_param_memory_offsets *pofs = (void *)css->fwp + + bi->blob.memory_offsets.offsets[IMGU_ABI_PARAM_CLASS_PARAM]; + + struct ipu3_uapi_isp_tnr3_params *tnr_dmem = NULL; + struct ipu3_uapi_isp_xnr3_params *xnr_dmem; + + const enum imgu_abi_param_class c = IMGU_ABI_PARAM_CLASS_PARAM; + const enum imgu_abi_memories m = IMGU_ABI_MEM_ISP_DMEM0; + + /* Configure DMEM0 */ + + memset(dmem0, 0, bi->info.isp.sp.mem_initializers.params[c][m].size); + + /* Configure TNR3 DMEM0 parameters */ + if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { + tnr_dmem = ipu3_css_cfg_copy(css, pipe, + use && use->tnr3_dmem_params, + &user->tnr3_dmem_params, + dmem0_old, dmem0, m, + &pofs->dmem.tnr3, + sizeof(*tnr_dmem)); + if (!IS_ERR_OR_NULL(tnr_dmem)) { + /* Generate parameter from scratch */ + tnr_dmem->knee_y1 = 768; + tnr_dmem->knee_y2 = 1280; + } + } + + /* Configure XNR3 DMEM0 parameters */ + + xnr_dmem = ipu3_css_cfg_copy(css, pipe, use && use->xnr3_dmem_params, + &user->xnr3_dmem_params, dmem0_old, dmem0, + m, &pofs->dmem.xnr3, sizeof(*xnr_dmem)); + if (!IS_ERR_OR_NULL(xnr_dmem)) { + /* Generate parameter from scratch */ + xnr_dmem->alpha.y0 = 2047; + xnr_dmem->alpha.u0 = 2047; + xnr_dmem->alpha.v0 = 2047; + } + + return IS_ERR(tnr_dmem) || IS_ERR(xnr_dmem) ? -EPROTO : 0; +} + +/* Generate unity morphing table without morphing effect */ +void ipu3_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, + int frame_in_x, int frame_in_y, + int frame_out_x, int frame_out_y, + int env_w, int env_h) +{ + static const unsigned int FRAC_BITS = IMGU_ABI_GDC_FRAC_BITS; + static const unsigned int XMEM_ALIGN = 1 << 4; + const unsigned int XMEM_ALIGN_MASK = ~(XMEM_ALIGN - 1); + static const unsigned int BCI_ENV = 4; + static const unsigned int BYP = 2; /* Bytes per pixel */ + const unsigned int OFFSET_X = 2 * IMGU_DVS_BLOCK_W + env_w + 1; + const unsigned int OFFSET_Y = IMGU_DVS_BLOCK_H + env_h + 1; + + struct imgu_abi_gdc_warp_param gdc_luma, gdc_chroma; + + unsigned int blocks_x = ALIGN(DIV_ROUND_UP(frame_out_x, + IMGU_DVS_BLOCK_W), 2); + unsigned int blocks_y = DIV_ROUND_UP(frame_out_y, IMGU_DVS_BLOCK_H); + unsigned int y0, x0, x1, x, y; + + /* Global luma settings */ + gdc_luma.origin_x = 0; + gdc_luma.origin_y = 0; + gdc_luma.p0_x = (OFFSET_X - (OFFSET_X & XMEM_ALIGN_MASK)) << FRAC_BITS; + gdc_luma.p0_y = 0; + gdc_luma.p1_x = gdc_luma.p0_x + (IMGU_DVS_BLOCK_W << FRAC_BITS); + gdc_luma.p1_y = gdc_luma.p0_y; + gdc_luma.p2_x = gdc_luma.p0_x; + gdc_luma.p2_y = gdc_luma.p0_y + (IMGU_DVS_BLOCK_H << FRAC_BITS); + gdc_luma.p3_x = gdc_luma.p1_x; + gdc_luma.p3_y = gdc_luma.p2_y; + + gdc_luma.in_block_width = IMGU_DVS_BLOCK_W + BCI_ENV + + OFFSET_X - (OFFSET_X & XMEM_ALIGN_MASK); + gdc_luma.in_block_width_a = DIV_ROUND_UP(gdc_luma.in_block_width, + IPU3_UAPI_ISP_VEC_ELEMS); + gdc_luma.in_block_width_b = DIV_ROUND_UP(gdc_luma.in_block_width, + IMGU_ABI_ISP_DDR_WORD_BYTES / + BYP); + gdc_luma.in_block_height = IMGU_DVS_BLOCK_H + BCI_ENV; + gdc_luma.padding = 0; + + /* Global chroma settings */ + gdc_chroma.origin_x = 0; + gdc_chroma.origin_y = 0; + gdc_chroma.p0_x = (OFFSET_X / 2 - (OFFSET_X / 2 & XMEM_ALIGN_MASK)) << + FRAC_BITS; + gdc_chroma.p0_y = 0; + gdc_chroma.p1_x = gdc_chroma.p0_x + (IMGU_DVS_BLOCK_W << FRAC_BITS); + gdc_chroma.p1_y = gdc_chroma.p0_y; + gdc_chroma.p2_x = gdc_chroma.p0_x; + gdc_chroma.p2_y = gdc_chroma.p0_y + (IMGU_DVS_BLOCK_H / 2 << FRAC_BITS); + gdc_chroma.p3_x = gdc_chroma.p1_x; + gdc_chroma.p3_y = gdc_chroma.p2_y; + + gdc_chroma.in_block_width = IMGU_DVS_BLOCK_W + BCI_ENV; + gdc_chroma.in_block_width_a = DIV_ROUND_UP(gdc_chroma.in_block_width, + IPU3_UAPI_ISP_VEC_ELEMS); + gdc_chroma.in_block_width_b = DIV_ROUND_UP(gdc_chroma.in_block_width, + IMGU_ABI_ISP_DDR_WORD_BYTES / + BYP); + gdc_chroma.in_block_height = IMGU_DVS_BLOCK_H / 2 + BCI_ENV; + gdc_chroma.padding = 0; + + /* Calculate block offsets for luma and chroma */ + for (y0 = 0; y0 < blocks_y; y0++) { + for (x0 = 0; x0 < blocks_x / 2; x0++) { + for (x1 = 0; x1 < 2; x1++) { + /* Luma blocks */ + x = (x0 * 2 + x1) * IMGU_DVS_BLOCK_W + OFFSET_X; + x &= XMEM_ALIGN_MASK; + y = y0 * IMGU_DVS_BLOCK_H + OFFSET_Y; + *gdc = gdc_luma; + gdc->in_addr_offset = + (y * frame_in_x + x) * BYP; + gdc++; + } + + /* Chroma block */ + x = x0 * IMGU_DVS_BLOCK_W + OFFSET_X / 2; + x &= XMEM_ALIGN_MASK; + y = y0 * (IMGU_DVS_BLOCK_H / 2) + OFFSET_Y / 2; + *gdc = gdc_chroma; + gdc->in_addr_offset = (y * frame_in_x + x) * BYP; + gdc++; + } + } +} diff --git a/drivers/staging/media/ipu3/ipu3-css-params.h b/drivers/staging/media/ipu3/ipu3-css-params.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ + +#ifndef __IPU3_PARAMS_H +#define __IPU3_PARAMS_H + +int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, + struct ipu3_uapi_flags *use, + struct imgu_abi_acc_param *acc, + struct imgu_abi_acc_param *acc_old, + struct ipu3_uapi_acc_param *acc_user); + +int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, + struct ipu3_uapi_flags *use, + void *vmem0, void *vmem0_old, + struct ipu3_uapi_params *user); + +int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, + struct ipu3_uapi_flags *use, + void *dmem0, void *dmem0_old, + struct ipu3_uapi_params *user); + +void ipu3_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, + int frame_in_x, int frame_in_y, + int frame_out_x, int frame_out_y, + int env_w, int env_h); + +#endif /*__IPU3_PARAMS_H */ diff --git a/drivers/staging/media/ipu3/ipu3-css-pool.c b/drivers/staging/media/ipu3/ipu3-css-pool.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include <linux/device.h> + +#include "ipu3.h" +#include "ipu3-css-pool.h" +#include "ipu3-dmamap.h" + +int ipu3_css_dma_buffer_resize(struct imgu_device *imgu, + struct ipu3_css_map *map, size_t size) +{ + if (map->size < size && map->vaddr) { + dev_warn(&imgu->pci_dev->dev, "dma buf resized from %zu to %zu", + map->size, size); + + ipu3_dmamap_free(imgu, map); + if (!ipu3_dmamap_alloc(imgu, map, size)) + return -ENOMEM; + } + + return 0; +} + +void ipu3_css_pool_cleanup(struct imgu_device *imgu, struct ipu3_css_pool *pool) +{ + unsigned int i; + + for (i = 0; i < IPU3_CSS_POOL_SIZE; i++) + ipu3_dmamap_free(imgu, &pool->entry[i].param); +} + +int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, + size_t size) +{ + unsigned int i; + + for (i = 0; i < IPU3_CSS_POOL_SIZE; i++) { + pool->entry[i].valid = false; + if (size == 0) { + pool->entry[i].param.vaddr = NULL; + continue; + } + + if (!ipu3_dmamap_alloc(imgu, &pool->entry[i].param, size)) + goto fail; + } + + pool->last = IPU3_CSS_POOL_SIZE; + + return 0; + +fail: + ipu3_css_pool_cleanup(imgu, pool); + return -ENOMEM; +} + +/* + * Allocate a new parameter via recycling the oldest entry in the pool. + */ +void ipu3_css_pool_get(struct ipu3_css_pool *pool) +{ + /* Get the oldest entry */ + u32 n = (pool->last + 1) % IPU3_CSS_POOL_SIZE; + + pool->entry[n].valid = true; + pool->last = n; +} + +/* + * Undo, for all practical purposes, the effect of pool_get(). + */ +void ipu3_css_pool_put(struct ipu3_css_pool *pool) +{ + pool->entry[pool->last].valid = false; + pool->last = (pool->last + IPU3_CSS_POOL_SIZE - 1) % IPU3_CSS_POOL_SIZE; +} + +/** + * ipu3_css_pool_last - Retrieve the nth pool entry from last + * + * @pool: a pointer to &struct ipu3_css_pool. + * @n: the distance to the last index. + * + * Returns: + * The nth entry from last or null map to indicate no frame stored. + */ +const struct ipu3_css_map * +ipu3_css_pool_last(struct ipu3_css_pool *pool, unsigned int n) +{ + static const struct ipu3_css_map null_map = { 0 }; + int i = (pool->last + IPU3_CSS_POOL_SIZE - n) % IPU3_CSS_POOL_SIZE; + + WARN_ON(n >= IPU3_CSS_POOL_SIZE); + + if (!pool->entry[i].valid) + return &null_map; + + return &pool->entry[i].param; +} diff --git a/drivers/staging/media/ipu3/ipu3-css-pool.h b/drivers/staging/media/ipu3/ipu3-css-pool.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ + +#ifndef __IPU3_UTIL_H +#define __IPU3_UTIL_H + +struct device; +struct imgu_device; + +#define IPU3_CSS_POOL_SIZE 4 + +/** + * ipu3_css_map - store DMA mapping info for buffer + * + * @size: size of the buffer in bytes. + * @vaddr: kernel virtual address. + * @daddr: iova dma address to access IPU3. + * @vma: private, a pointer to &struct vm_struct, + * used for ipu3_dmamap_free. + */ +struct ipu3_css_map { + size_t size; + void *vaddr; + dma_addr_t daddr; + struct vm_struct *vma; +}; + +/** + * ipu3_css_pool - circular buffer pool definition + * + * @entry: array with IPU3_CSS_POOL_SIZE elements. + * @entry.param: a &struct ipu3_css_map for storing the mem mapping. + * @entry.valid: used to mark if the entry has valid data. + * @last: write pointer, initialized to IPU3_CSS_POOL_SIZE. + */ +struct ipu3_css_pool { + struct { + struct ipu3_css_map param; + bool valid; + } entry[IPU3_CSS_POOL_SIZE]; + u32 last; +}; + +int ipu3_css_dma_buffer_resize(struct imgu_device *imgu, + struct ipu3_css_map *map, size_t size); +void ipu3_css_pool_cleanup(struct imgu_device *imgu, + struct ipu3_css_pool *pool); +int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, + size_t size); +void ipu3_css_pool_get(struct ipu3_css_pool *pool); +void ipu3_css_pool_put(struct ipu3_css_pool *pool); +const struct ipu3_css_map *ipu3_css_pool_last(struct ipu3_css_pool *pool, + u32 last); + +#endif diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c @@ -0,0 +1,2391 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include <linux/device.h> +#include <linux/iopoll.h> + +#include "ipu3-css.h" +#include "ipu3-css-fw.h" +#include "ipu3-css-params.h" +#include "ipu3-dmamap.h" +#include "ipu3-tables.h" + +/* IRQ configuration */ +#define IMGU_IRQCTRL_IRQ_MASK (IMGU_IRQCTRL_IRQ_SP1 | \ + IMGU_IRQCTRL_IRQ_SP2 | \ + IMGU_IRQCTRL_IRQ_SW_PIN(0) | \ + IMGU_IRQCTRL_IRQ_SW_PIN(1)) + +#define IPU3_CSS_FORMAT_BPP_DEN 50 /* Denominator */ + +/* Some sane limits for resolutions */ +#define IPU3_CSS_MIN_RES 32 +#define IPU3_CSS_MAX_H 3136 +#define IPU3_CSS_MAX_W 4224 + +/* filter size from graph settings is fixed as 4 */ +#define FILTER_SIZE 4 +#define MIN_ENVELOPE 8 + +/* + * pre-allocated buffer size for CSS ABI, auxiliary frames + * after BDS and before GDC. Those values should be tuned + * to big enough to avoid buffer re-allocation when + * streaming to lower streaming latency. + */ +#define CSS_ABI_SIZE 136 +#define CSS_BDS_SIZE (4480 * 3200 * 3) +#define CSS_GDC_SIZE (4224 * 3200 * 12 / 8) + +#define IPU3_CSS_QUEUE_TO_FLAGS(q) (1 << (q)) +#define IPU3_CSS_FORMAT_FL_IN \ + IPU3_CSS_QUEUE_TO_FLAGS(IPU3_CSS_QUEUE_IN) +#define IPU3_CSS_FORMAT_FL_OUT \ + IPU3_CSS_QUEUE_TO_FLAGS(IPU3_CSS_QUEUE_OUT) +#define IPU3_CSS_FORMAT_FL_VF \ + IPU3_CSS_QUEUE_TO_FLAGS(IPU3_CSS_QUEUE_VF) + +/* Formats supported by IPU3 Camera Sub System */ +static const struct ipu3_css_format ipu3_css_formats[] = { + { + .pixelformat = V4L2_PIX_FMT_NV12, + .colorspace = V4L2_COLORSPACE_SRGB, + .frame_format = IMGU_ABI_FRAME_FORMAT_NV12, + .osys_format = IMGU_ABI_OSYS_FORMAT_NV12, + .osys_tiling = IMGU_ABI_OSYS_TILING_NONE, + .bytesperpixel_num = 1 * IPU3_CSS_FORMAT_BPP_DEN, + .chroma_decim = 4, + .width_align = IPU3_UAPI_ISP_VEC_ELEMS, + .flags = IPU3_CSS_FORMAT_FL_OUT | IPU3_CSS_FORMAT_FL_VF, + }, { + /* Each 32 bytes contains 25 10-bit pixels */ + .pixelformat = V4L2_PIX_FMT_IPU3_SBGGR10, + .colorspace = V4L2_COLORSPACE_RAW, + .frame_format = IMGU_ABI_FRAME_FORMAT_RAW_PACKED, + .bayer_order = IMGU_ABI_BAYER_ORDER_BGGR, + .bit_depth = 10, + .bytesperpixel_num = 64, + .width_align = 2 * IPU3_UAPI_ISP_VEC_ELEMS, + .flags = IPU3_CSS_FORMAT_FL_IN, + }, { + .pixelformat = V4L2_PIX_FMT_IPU3_SGBRG10, + .colorspace = V4L2_COLORSPACE_RAW, + .frame_format = IMGU_ABI_FRAME_FORMAT_RAW_PACKED, + .bayer_order = IMGU_ABI_BAYER_ORDER_GBRG, + .bit_depth = 10, + .bytesperpixel_num = 64, + .width_align = 2 * IPU3_UAPI_ISP_VEC_ELEMS, + .flags = IPU3_CSS_FORMAT_FL_IN, + }, { + .pixelformat = V4L2_PIX_FMT_IPU3_SGRBG10, + .colorspace = V4L2_COLORSPACE_RAW, + .frame_format = IMGU_ABI_FRAME_FORMAT_RAW_PACKED, + .bayer_order = IMGU_ABI_BAYER_ORDER_GRBG, + .bit_depth = 10, + .bytesperpixel_num = 64, + .width_align = 2 * IPU3_UAPI_ISP_VEC_ELEMS, + .flags = IPU3_CSS_FORMAT_FL_IN, + }, { + .pixelformat = V4L2_PIX_FMT_IPU3_SRGGB10, + .colorspace = V4L2_COLORSPACE_RAW, + .frame_format = IMGU_ABI_FRAME_FORMAT_RAW_PACKED, + .bayer_order = IMGU_ABI_BAYER_ORDER_RGGB, + .bit_depth = 10, + .bytesperpixel_num = 64, + .width_align = 2 * IPU3_UAPI_ISP_VEC_ELEMS, + .flags = IPU3_CSS_FORMAT_FL_IN, + }, +}; + +static const struct { + enum imgu_abi_queue_id qid; + size_t ptr_ofs; +} ipu3_css_queues[IPU3_CSS_QUEUES] = { + [IPU3_CSS_QUEUE_IN] = { + IMGU_ABI_QUEUE_C_ID, + offsetof(struct imgu_abi_buffer, payload.frame.frame_data) + }, + [IPU3_CSS_QUEUE_OUT] = { + IMGU_ABI_QUEUE_D_ID, + offsetof(struct imgu_abi_buffer, payload.frame.frame_data) + }, + [IPU3_CSS_QUEUE_VF] = { + IMGU_ABI_QUEUE_E_ID, + offsetof(struct imgu_abi_buffer, payload.frame.frame_data) + }, + [IPU3_CSS_QUEUE_STAT_3A] = { + IMGU_ABI_QUEUE_F_ID, + offsetof(struct imgu_abi_buffer, payload.s3a.data_ptr) + }, +}; + +/* Initialize queue based on given format, adjust format as needed */ +static int ipu3_css_queue_init(struct ipu3_css_queue *queue, + struct v4l2_pix_format_mplane *fmt, u32 flags) +{ + struct v4l2_pix_format_mplane *const f = &queue->fmt.mpix; + unsigned int i; + u32 sizeimage; + + INIT_LIST_HEAD(&queue->bufs); + + queue->css_fmt = NULL; /* Disable */ + if (!fmt) + return 0; + + for (i = 0; i < ARRAY_SIZE(ipu3_css_formats); i++) { + if (!(ipu3_css_formats[i].flags & flags)) + continue; + queue->css_fmt = &ipu3_css_formats[i]; + if (ipu3_css_formats[i].pixelformat == fmt->pixelformat) + break; + } + if (!queue->css_fmt) + return -EINVAL; /* Could not find any suitable format */ + + queue->fmt.mpix = *fmt; + + f->width = ALIGN(clamp_t(u32, f->width, + IPU3_CSS_MIN_RES, IPU3_CSS_MAX_W), 2); + f->height = ALIGN(clamp_t(u32, f->height, + IPU3_CSS_MIN_RES, IPU3_CSS_MAX_H), 2); + queue->width_pad = ALIGN(f->width, queue->css_fmt->width_align); + if (queue->css_fmt->frame_format != IMGU_ABI_FRAME_FORMAT_RAW_PACKED) + f->plane_fmt[0].bytesperline = DIV_ROUND_UP(queue->width_pad * + queue->css_fmt->bytesperpixel_num, + IPU3_CSS_FORMAT_BPP_DEN); + else + /* For packed raw, alignment for bpl is by 50 to the width */ + f->plane_fmt[0].bytesperline = + DIV_ROUND_UP(f->width, + IPU3_CSS_FORMAT_BPP_DEN) * + queue->css_fmt->bytesperpixel_num; + + sizeimage = f->height * f->plane_fmt[0].bytesperline; + if (queue->css_fmt->chroma_decim) + sizeimage += 2 * sizeimage / queue->css_fmt->chroma_decim; + + f->plane_fmt[0].sizeimage = sizeimage; + f->field = V4L2_FIELD_NONE; + f->num_planes = 1; + f->colorspace = queue->css_fmt->colorspace; + f->flags = 0; + f->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->quantization = V4L2_QUANTIZATION_DEFAULT; + f->xfer_func = V4L2_XFER_FUNC_DEFAULT; + memset(f->reserved, 0, sizeof(f->reserved)); + + return 0; +} + +static bool ipu3_css_queue_enabled(struct ipu3_css_queue *q) +{ + return q->css_fmt; +} + +/******************* css hw *******************/ + +/* In the style of writesl() defined in include/asm-generic/io.h */ +static inline void writes(const void *mem, ssize_t count, void __iomem *addr) +{ + if (count >= 4) { + const u32 *buf = mem; + + count /= 4; + do { + writel(*buf++, addr); + addr += 4; + } while (--count); + } +} + +/* Wait until register `reg', masked with `mask', becomes `cmp' */ +static int ipu3_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp) +{ + u32 val; + + return readl_poll_timeout(base + reg, val, (val & mask) == cmp, + 1000, 100 * 1000); +} + +/* Initialize the IPU3 CSS hardware and associated h/w blocks */ + +int ipu3_css_set_powerup(struct device *dev, void __iomem *base) +{ + static const unsigned int freq = 450; + u32 pm_ctrl, state, val; + + dev_dbg(dev, "%s\n", __func__); + /* Clear the CSS busy signal */ + readl(base + IMGU_REG_GP_BUSY); + writel(0, base + IMGU_REG_GP_BUSY); + + /* Wait for idle signal */ + if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, + IMGU_STATE_IDLE_STS)) { + dev_err(dev, "failed to set CSS idle\n"); + goto fail; + } + + /* Reset the css */ + writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_FORCE_RESET, + base + IMGU_REG_PM_CTRL); + + usleep_range(200, 300); + + /** Prepare CSS */ + + pm_ctrl = readl(base + IMGU_REG_PM_CTRL); + state = readl(base + IMGU_REG_STATE); + + dev_dbg(dev, "CSS pm_ctrl 0x%x state 0x%x (power %s)\n", + pm_ctrl, state, state & IMGU_STATE_POWER_DOWN ? "down" : "up"); + + /* Power up CSS using wrapper */ + if (state & IMGU_STATE_POWER_DOWN) { + writel(IMGU_PM_CTRL_RACE_TO_HALT | IMGU_PM_CTRL_START, + base + IMGU_REG_PM_CTRL); + if (ipu3_hw_wait(base, IMGU_REG_PM_CTRL, + IMGU_PM_CTRL_START, 0)) { + dev_err(dev, "failed to power up CSS\n"); + goto fail; + } + usleep_range(2000, 3000); + } else { + writel(IMGU_PM_CTRL_RACE_TO_HALT, base + IMGU_REG_PM_CTRL); + } + + /* Set the busy bit */ + writel(readl(base + IMGU_REG_GP_BUSY) | 1, base + IMGU_REG_GP_BUSY); + + /* Set CSS clock frequency */ + pm_ctrl = readl(base + IMGU_REG_PM_CTRL); + val = pm_ctrl & ~(IMGU_PM_CTRL_CSS_PWRDN | IMGU_PM_CTRL_RST_AT_EOF); + writel(val, base + IMGU_REG_PM_CTRL); + writel(0, base + IMGU_REG_GP_BUSY); + if (ipu3_hw_wait(base, IMGU_REG_STATE, + IMGU_STATE_PWRDNM_FSM_MASK, 0)) { + dev_err(dev, "failed to pwrdn CSS\n"); + goto fail; + } + val = (freq / IMGU_SYSTEM_REQ_FREQ_DIVIDER) & IMGU_SYSTEM_REQ_FREQ_MASK; + writel(val, base + IMGU_REG_SYSTEM_REQ); + writel(1, base + IMGU_REG_GP_BUSY); + writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_FORCE_HALT, + base + IMGU_REG_PM_CTRL); + if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, + IMGU_STATE_HALT_STS)) { + dev_err(dev, "failed to halt CSS\n"); + goto fail; + } + + writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_START, + base + IMGU_REG_PM_CTRL); + if (ipu3_hw_wait(base, IMGU_REG_PM_CTRL, IMGU_PM_CTRL_START, 0)) { + dev_err(dev, "failed to start CSS\n"); + goto fail; + } + writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_FORCE_UNHALT, + base + IMGU_REG_PM_CTRL); + + val = readl(base + IMGU_REG_PM_CTRL); /* get pm_ctrl */ + val &= ~(IMGU_PM_CTRL_CSS_PWRDN | IMGU_PM_CTRL_RST_AT_EOF); + val |= pm_ctrl & (IMGU_PM_CTRL_CSS_PWRDN | IMGU_PM_CTRL_RST_AT_EOF); + writel(val, base + IMGU_REG_PM_CTRL); + + return 0; + +fail: + ipu3_css_set_powerdown(dev, base); + return -EIO; +} + +void ipu3_css_set_powerdown(struct device *dev, void __iomem *base) +{ + dev_dbg(dev, "%s\n", __func__); + /* wait for cio idle signal */ + if (ipu3_hw_wait(base, IMGU_REG_CIO_GATE_BURST_STATE, + IMGU_CIO_GATE_BURST_MASK, 0)) + dev_warn(dev, "wait cio gate idle timeout"); + + /* wait for css idle signal */ + if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, + IMGU_STATE_IDLE_STS)) + dev_warn(dev, "wait css idle timeout\n"); + + /* do halt-halted handshake with css */ + writel(1, base + IMGU_REG_GP_HALT); + if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, + IMGU_STATE_HALT_STS)) + dev_warn(dev, "failed to halt css"); + + /* de-assert the busy bit */ + writel(0, base + IMGU_REG_GP_BUSY); +} + +static void ipu3_css_hw_enable_irq(struct ipu3_css *css) +{ + void __iomem *const base = css->base; + u32 val, i; + + /* Set up interrupts */ + + /* + * Enable IRQ on the SP which signals that SP goes to idle + * (aka ready state) and set trigger to pulse + */ + val = readl(base + IMGU_REG_SP_CTRL(0)) | IMGU_CTRL_IRQ_READY; + writel(val, base + IMGU_REG_SP_CTRL(0)); + writel(val | IMGU_CTRL_IRQ_CLEAR, base + IMGU_REG_SP_CTRL(0)); + + /* Enable IRQs from the IMGU wrapper */ + writel(IMGU_REG_INT_CSS_IRQ, base + IMGU_REG_INT_ENABLE); + /* Clear */ + writel(IMGU_REG_INT_CSS_IRQ, base + IMGU_REG_INT_STATUS); + + /* Enable IRQs from main IRQ controller */ + writel(~0, base + IMGU_REG_IRQCTRL_EDGE_NOT_PULSE(IMGU_IRQCTRL_MAIN)); + writel(0, base + IMGU_REG_IRQCTRL_MASK(IMGU_IRQCTRL_MAIN)); + writel(IMGU_IRQCTRL_IRQ_MASK, + base + IMGU_REG_IRQCTRL_EDGE(IMGU_IRQCTRL_MAIN)); + writel(IMGU_IRQCTRL_IRQ_MASK, + base + IMGU_REG_IRQCTRL_ENABLE(IMGU_IRQCTRL_MAIN)); + writel(IMGU_IRQCTRL_IRQ_MASK, + base + IMGU_REG_IRQCTRL_CLEAR(IMGU_IRQCTRL_MAIN)); + writel(IMGU_IRQCTRL_IRQ_MASK, + base + IMGU_REG_IRQCTRL_MASK(IMGU_IRQCTRL_MAIN)); + /* Wait for write complete */ + readl(base + IMGU_REG_IRQCTRL_ENABLE(IMGU_IRQCTRL_MAIN)); + + /* Enable IRQs from SP0 and SP1 controllers */ + for (i = IMGU_IRQCTRL_SP0; i <= IMGU_IRQCTRL_SP1; i++) { + writel(~0, base + IMGU_REG_IRQCTRL_EDGE_NOT_PULSE(i)); + writel(0, base + IMGU_REG_IRQCTRL_MASK(i)); + writel(IMGU_IRQCTRL_IRQ_MASK, base + IMGU_REG_IRQCTRL_EDGE(i)); + writel(IMGU_IRQCTRL_IRQ_MASK, + base + IMGU_REG_IRQCTRL_ENABLE(i)); + writel(IMGU_IRQCTRL_IRQ_MASK, base + IMGU_REG_IRQCTRL_CLEAR(i)); + writel(IMGU_IRQCTRL_IRQ_MASK, base + IMGU_REG_IRQCTRL_MASK(i)); + /* Wait for write complete */ + readl(base + IMGU_REG_IRQCTRL_ENABLE(i)); + } +} + +static int ipu3_css_hw_init(struct ipu3_css *css) +{ + /* For checking that streaming monitor statuses are valid */ + static const struct { + u32 reg; + u32 mask; + const char *name; + } stream_monitors[] = { + { + IMGU_REG_GP_SP1_STRMON_STAT, + IMGU_GP_STRMON_STAT_ISP_PORT_SP12ISP, + "ISP0 to SP0" + }, { + IMGU_REG_GP_ISP_STRMON_STAT, + IMGU_GP_STRMON_STAT_SP1_PORT_ISP2SP1, + "SP0 to ISP0" + }, { + IMGU_REG_GP_MOD_STRMON_STAT, + IMGU_GP_STRMON_STAT_MOD_PORT_ISP2DMA, + "ISP0 to DMA0" + }, { + IMGU_REG_GP_ISP_STRMON_STAT, + IMGU_GP_STRMON_STAT_ISP_PORT_DMA2ISP, + "DMA0 to ISP0" + }, { + IMGU_REG_GP_MOD_STRMON_STAT, + IMGU_GP_STRMON_STAT_MOD_PORT_CELLS2GDC, + "ISP0 to GDC0" + }, { + IMGU_REG_GP_MOD_STRMON_STAT, + IMGU_GP_STRMON_STAT_MOD_PORT_GDC2CELLS, + "GDC0 to ISP0" + }, { + IMGU_REG_GP_MOD_STRMON_STAT, + IMGU_GP_STRMON_STAT_MOD_PORT_SP12DMA, + "SP0 to DMA0" + }, { + IMGU_REG_GP_SP1_STRMON_STAT, + IMGU_GP_STRMON_STAT_SP1_PORT_DMA2SP1, + "DMA0 to SP0" + }, { + IMGU_REG_GP_MOD_STRMON_STAT, + IMGU_GP_STRMON_STAT_MOD_PORT_CELLS2GDC, + "SP0 to GDC0" + }, { + IMGU_REG_GP_MOD_STRMON_STAT, + IMGU_GP_STRMON_STAT_MOD_PORT_GDC2CELLS, + "GDC0 to SP0" + }, + }; + + struct device *dev = css->dev; + void __iomem *const base = css->base; + u32 val, i; + + /* Set instruction cache address and inv bit for ISP, SP, and SP1 */ + for (i = 0; i < IMGU_NUM_SP; i++) { + struct imgu_fw_info *bi = + &css->fwp->binary_header[css->fw_sp[i]]; + + writel(css->binary[css->fw_sp[i]].daddr, + base + IMGU_REG_SP_ICACHE_ADDR(bi->type)); + writel(readl(base + IMGU_REG_SP_CTRL(bi->type)) | + IMGU_CTRL_ICACHE_INV, + base + IMGU_REG_SP_CTRL(bi->type)); + } + writel(css->binary[css->fw_bl].daddr, base + IMGU_REG_ISP_ICACHE_ADDR); + writel(readl(base + IMGU_REG_ISP_CTRL) | IMGU_CTRL_ICACHE_INV, + base + IMGU_REG_ISP_CTRL); + + /* Check that IMGU hardware is ready */ + + if (!(readl(base + IMGU_REG_SP_CTRL(0)) & IMGU_CTRL_IDLE)) { + dev_err(dev, "SP is not idle\n"); + return -EIO; + } + if (!(readl(base + IMGU_REG_ISP_CTRL) & IMGU_CTRL_IDLE)) { + dev_err(dev, "ISP is not idle\n"); + return -EIO; + } + + for (i = 0; i < ARRAY_SIZE(stream_monitors); i++) { + val = readl(base + stream_monitors[i].reg); + if (val & stream_monitors[i].mask) { + dev_err(dev, "error: Stream monitor %s is valid\n", + stream_monitors[i].name); + return -EIO; + } + } + + /* Initialize GDC with default values */ + + for (i = 0; i < ARRAY_SIZE(ipu3_css_gdc_lut[0]); i++) { + u32 val0 = ipu3_css_gdc_lut[0][i] & IMGU_GDC_LUT_MASK; + u32 val1 = ipu3_css_gdc_lut[1][i] & IMGU_GDC_LUT_MASK; + u32 val2 = ipu3_css_gdc_lut[2][i] & IMGU_GDC_LUT_MASK; + u32 val3 = ipu3_css_gdc_lut[3][i] & IMGU_GDC_LUT_MASK; + + writel(val0 | (val1 << 16), + base + IMGU_REG_GDC_LUT_BASE + i * 8); + writel(val2 | (val3 << 16), + base + IMGU_REG_GDC_LUT_BASE + i * 8 + 4); + } + + return 0; +} + +/* Boot the given IPU3 CSS SP */ +static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp) +{ + void __iomem *const base = css->base; + struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[sp]]; + struct imgu_abi_sp_init_dmem_cfg dmem_cfg = { + .ddr_data_addr = css->binary[css->fw_sp[sp]].daddr + + bi->blob.data_source, + .dmem_data_addr = bi->blob.data_target, + .dmem_bss_addr = bi->blob.bss_target, + .data_size = bi->blob.data_size, + .bss_size = bi->blob.bss_size, + .sp_id = sp, + }; + + writes(&dmem_cfg, sizeof(dmem_cfg), base + + IMGU_REG_SP_DMEM_BASE(sp) + bi->info.sp.init_dmem_data); + + writel(bi->info.sp.sp_entry, base + IMGU_REG_SP_START_ADDR(sp)); + + writel(readl(base + IMGU_REG_SP_CTRL(sp)) + | IMGU_CTRL_START | IMGU_CTRL_RUN, base + IMGU_REG_SP_CTRL(sp)); + + if (ipu3_hw_wait(css->base, IMGU_REG_SP_DMEM_BASE(sp) + + bi->info.sp.sw_state, + ~0, IMGU_ABI_SP_SWSTATE_INITIALIZED)) + return -EIO; + + return 0; +} + +/* Start the IPU3 CSS ImgU (Imaging Unit) and all the SPs */ +static int ipu3_css_hw_start(struct ipu3_css *css) +{ + static const u32 event_mask = + ((1 << IMGU_ABI_EVTTYPE_OUT_FRAME_DONE) | + (1 << IMGU_ABI_EVTTYPE_2ND_OUT_FRAME_DONE) | + (1 << IMGU_ABI_EVTTYPE_VF_OUT_FRAME_DONE) | + (1 << IMGU_ABI_EVTTYPE_2ND_VF_OUT_FRAME_DONE) | + (1 << IMGU_ABI_EVTTYPE_3A_STATS_DONE) | + (1 << IMGU_ABI_EVTTYPE_DIS_STATS_DONE) | + (1 << IMGU_ABI_EVTTYPE_PIPELINE_DONE) | + (1 << IMGU_ABI_EVTTYPE_FRAME_TAGGED) | + (1 << IMGU_ABI_EVTTYPE_INPUT_FRAME_DONE) | + (1 << IMGU_ABI_EVTTYPE_METADATA_DONE) | + (1 << IMGU_ABI_EVTTYPE_ACC_STAGE_COMPLETE)) + << IMGU_ABI_SP_COMM_EVENT_IRQ_MASK_OR_SHIFT; + + void __iomem *const base = css->base; + struct imgu_fw_info *bi, *bl = &css->fwp->binary_header[css->fw_bl]; + unsigned int i; + + writel(IMGU_TLB_INVALIDATE, base + IMGU_REG_TLB_INVALIDATE); + + /* Start bootloader */ + + writel(IMGU_ABI_BL_SWSTATE_BUSY, + base + IMGU_REG_ISP_DMEM_BASE + bl->info.bl.sw_state); + writel(IMGU_NUM_SP, + base + IMGU_REG_ISP_DMEM_BASE + bl->info.bl.num_dma_cmds); + + for (i = 0; i < IMGU_NUM_SP; i++) { + int j = IMGU_NUM_SP - i - 1; /* load sp1 first, then sp0 */ + struct imgu_fw_info *sp = + &css->fwp->binary_header[css->fw_sp[j]]; + struct imgu_abi_bl_dma_cmd_entry dma_cmd = { + .src_addr = css->binary[css->fw_sp[j]].daddr + + sp->blob.text_source, + .size = sp->blob.text_size, + .dst_type = IMGU_ABI_BL_DMACMD_TYPE_SP_PMEM, + .dst_addr = IMGU_SP_PMEM_BASE(j), + }; + + writes(&dma_cmd, sizeof(dma_cmd), + base + IMGU_REG_ISP_DMEM_BASE + i * sizeof(dma_cmd) + + bl->info.bl.dma_cmd_list); + } + + writel(bl->info.bl.bl_entry, base + IMGU_REG_ISP_START_ADDR); + + writel(readl(base + IMGU_REG_ISP_CTRL) + | IMGU_CTRL_START | IMGU_CTRL_RUN, base + IMGU_REG_ISP_CTRL); + if (ipu3_hw_wait(css->base, IMGU_REG_ISP_DMEM_BASE + + bl->info.bl.sw_state, ~0, + IMGU_ABI_BL_SWSTATE_OK)) { + dev_err(css->dev, "failed to start bootloader\n"); + return -EIO; + } + + /* Start ISP */ + + memset(css->xmem_sp_group_ptrs.vaddr, 0, + sizeof(struct imgu_abi_sp_group)); + + bi = &css->fwp->binary_header[css->fw_sp[0]]; + + writel(css->xmem_sp_group_ptrs.daddr, + base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.per_frame_data); + + writel(IMGU_ABI_SP_SWSTATE_TERMINATED, + base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.sw_state); + writel(1, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.invalidate_tlb); + + if (ipu3_css_hw_start_sp(css, 0)) + return -EIO; + + writel(0, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.isp_started); + writel(0, base + IMGU_REG_SP_DMEM_BASE(0) + + bi->info.sp.host_sp_queues_initialized); + writel(0, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.sleep_mode); + writel(0, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.invalidate_tlb); + writel(IMGU_ABI_SP_COMM_COMMAND_READY, base + IMGU_REG_SP_DMEM_BASE(0) + + bi->info.sp.host_sp_com + IMGU_ABI_SP_COMM_COMMAND); + + /* Enable all events for all queues */ + + for (i = 0; i < IPU3_CSS_PIPE_ID_NUM; i++) + writel(event_mask, base + IMGU_REG_SP_DMEM_BASE(0) + + bi->info.sp.host_sp_com + + IMGU_ABI_SP_COMM_EVENT_IRQ_MASK(i)); + writel(1, base + IMGU_REG_SP_DMEM_BASE(0) + + bi->info.sp.host_sp_queues_initialized); + + /* Start SP1 */ + + bi = &css->fwp->binary_header[css->fw_sp[1]]; + + writel(IMGU_ABI_SP_SWSTATE_TERMINATED, + base + IMGU_REG_SP_DMEM_BASE(1) + bi->info.sp.sw_state); + + if (ipu3_css_hw_start_sp(css, 1)) + return -EIO; + + writel(IMGU_ABI_SP_COMM_COMMAND_READY, base + IMGU_REG_SP_DMEM_BASE(1) + + bi->info.sp.host_sp_com + IMGU_ABI_SP_COMM_COMMAND); + + return 0; +} + +static void ipu3_css_hw_stop(struct ipu3_css *css) +{ + void __iomem *const base = css->base; + struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[0]]; + + /* Stop fw */ + writel(IMGU_ABI_SP_COMM_COMMAND_TERMINATE, + base + IMGU_REG_SP_DMEM_BASE(0) + + bi->info.sp.host_sp_com + IMGU_ABI_SP_COMM_COMMAND); + if (ipu3_hw_wait(css->base, IMGU_REG_SP_CTRL(0), + IMGU_CTRL_IDLE, IMGU_CTRL_IDLE)) + dev_err(css->dev, "wait sp0 idle timeout.\n"); + if (readl(base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.sw_state) != + IMGU_ABI_SP_SWSTATE_TERMINATED) + dev_err(css->dev, "sp0 is not terminated.\n"); + if (ipu3_hw_wait(css->base, IMGU_REG_ISP_CTRL, + IMGU_CTRL_IDLE, IMGU_CTRL_IDLE)) + dev_err(css->dev, "wait isp idle timeout\n"); +} + +static void ipu3_css_hw_cleanup(struct ipu3_css *css) +{ + void __iomem *const base = css->base; + + /** Reset CSS **/ + + /* Clear the CSS busy signal */ + readl(base + IMGU_REG_GP_BUSY); + writel(0, base + IMGU_REG_GP_BUSY); + + /* Wait for idle signal */ + if (ipu3_hw_wait(css->base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, + IMGU_STATE_IDLE_STS)) + dev_err(css->dev, "failed to shut down hw cleanly\n"); + + /* Reset the css */ + writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_FORCE_RESET, + base + IMGU_REG_PM_CTRL); + + usleep_range(200, 300); +} + +static void ipu3_css_pipeline_cleanup(struct ipu3_css *css, unsigned int pipe) +{ + struct imgu_device *imgu = dev_get_drvdata(css->dev); + unsigned int i; + + ipu3_css_pool_cleanup(imgu, + &css->pipes[pipe].pool.parameter_set_info); + ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.acc); + ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.gdc); + ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.obgrid); + + for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) + ipu3_css_pool_cleanup(imgu, + &css->pipes[pipe].pool.binary_params_p[i]); +} + +/* + * This function initializes various stages of the + * IPU3 CSS ISP pipeline + */ +static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) +{ + static const int BYPC = 2; /* Bytes per component */ + static const struct imgu_abi_buffer_sp buffer_sp_init = { + .buf_src = {.queue_id = IMGU_ABI_QUEUE_EVENT_ID}, + .buf_type = IMGU_ABI_BUFFER_TYPE_INVALID, + }; + + struct imgu_abi_isp_iterator_config *cfg_iter; + struct imgu_abi_isp_ref_config *cfg_ref; + struct imgu_abi_isp_dvs_config *cfg_dvs; + struct imgu_abi_isp_tnr3_config *cfg_tnr; + struct imgu_abi_isp_ref_dmem_state *cfg_ref_state; + struct imgu_abi_isp_tnr3_dmem_state *cfg_tnr_state; + + const int stage = 0; + unsigned int i, j; + + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + const struct imgu_fw_info *bi = + &css->fwp->binary_header[css_pipe->bindex]; + const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes; + + struct imgu_fw_config_memory_offsets *cofs = (void *)css->fwp + + bi->blob.memory_offsets.offsets[IMGU_ABI_PARAM_CLASS_CONFIG]; + struct imgu_fw_state_memory_offsets *sofs = (void *)css->fwp + + bi->blob.memory_offsets.offsets[IMGU_ABI_PARAM_CLASS_STATE]; + + struct imgu_abi_isp_stage *isp_stage; + struct imgu_abi_sp_stage *sp_stage; + struct imgu_abi_sp_group *sp_group; + + const unsigned int bds_width_pad = + ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, + 2 * IPU3_UAPI_ISP_VEC_ELEMS); + + const enum imgu_abi_memories m0 = IMGU_ABI_MEM_ISP_DMEM0; + enum imgu_abi_param_class cfg = IMGU_ABI_PARAM_CLASS_CONFIG; + void *vaddr = css_pipe->binary_params_cs[cfg - 1][m0].vaddr; + + struct imgu_device *imgu = dev_get_drvdata(css->dev); + + dev_dbg(css->dev, "%s for pipe %d", __func__, pipe); + + /* Configure iterator */ + + cfg_iter = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + &cofs->dmem.iterator, + sizeof(*cfg_iter), vaddr); + if (!cfg_iter) + goto bad_firmware; + + cfg_iter->input_info.res.width = + css_pipe->queue[IPU3_CSS_QUEUE_IN].fmt.mpix.width; + cfg_iter->input_info.res.height = + css_pipe->queue[IPU3_CSS_QUEUE_IN].fmt.mpix.height; + cfg_iter->input_info.padded_width = + css_pipe->queue[IPU3_CSS_QUEUE_IN].width_pad; + cfg_iter->input_info.format = + css_pipe->queue[IPU3_CSS_QUEUE_IN].css_fmt->frame_format; + cfg_iter->input_info.raw_bit_depth = + css_pipe->queue[IPU3_CSS_QUEUE_IN].css_fmt->bit_depth; + cfg_iter->input_info.raw_bayer_order = + css_pipe->queue[IPU3_CSS_QUEUE_IN].css_fmt->bayer_order; + cfg_iter->input_info.raw_type = IMGU_ABI_RAW_TYPE_BAYER; + + cfg_iter->internal_info.res.width = css_pipe->rect[IPU3_CSS_RECT_BDS].width; + cfg_iter->internal_info.res.height = + css_pipe->rect[IPU3_CSS_RECT_BDS].height; + cfg_iter->internal_info.padded_width = bds_width_pad; + cfg_iter->internal_info.format = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->frame_format; + cfg_iter->internal_info.raw_bit_depth = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bit_depth; + cfg_iter->internal_info.raw_bayer_order = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bayer_order; + cfg_iter->internal_info.raw_type = IMGU_ABI_RAW_TYPE_BAYER; + + cfg_iter->output_info.res.width = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.width; + cfg_iter->output_info.res.height = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.height; + cfg_iter->output_info.padded_width = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].width_pad; + cfg_iter->output_info.format = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->frame_format; + cfg_iter->output_info.raw_bit_depth = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bit_depth; + cfg_iter->output_info.raw_bayer_order = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bayer_order; + cfg_iter->output_info.raw_type = IMGU_ABI_RAW_TYPE_BAYER; + + cfg_iter->vf_info.res.width = + css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.width; + cfg_iter->vf_info.res.height = + css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.height; + cfg_iter->vf_info.padded_width = + css_pipe->queue[IPU3_CSS_QUEUE_VF].width_pad; + cfg_iter->vf_info.format = + css_pipe->queue[IPU3_CSS_QUEUE_VF].css_fmt->frame_format; + cfg_iter->vf_info.raw_bit_depth = + css_pipe->queue[IPU3_CSS_QUEUE_VF].css_fmt->bit_depth; + cfg_iter->vf_info.raw_bayer_order = + css_pipe->queue[IPU3_CSS_QUEUE_VF].css_fmt->bayer_order; + cfg_iter->vf_info.raw_type = IMGU_ABI_RAW_TYPE_BAYER; + + cfg_iter->dvs_envelope.width = css_pipe->rect[IPU3_CSS_RECT_ENVELOPE].width; + cfg_iter->dvs_envelope.height = + css_pipe->rect[IPU3_CSS_RECT_ENVELOPE].height; + + /* Configure reference (delay) frames */ + + cfg_ref = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + &cofs->dmem.ref, + sizeof(*cfg_ref), vaddr); + if (!cfg_ref) + goto bad_firmware; + + cfg_ref->port_b.crop = 0; + cfg_ref->port_b.elems = IMGU_ABI_ISP_DDR_WORD_BYTES / BYPC; + cfg_ref->port_b.width = + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].width; + cfg_ref->port_b.stride = + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperline; + cfg_ref->width_a_over_b = + IPU3_UAPI_ISP_VEC_ELEMS / cfg_ref->port_b.elems; + cfg_ref->dvs_frame_delay = IPU3_CSS_AUX_FRAMES - 1; + for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) { + cfg_ref->ref_frame_addr_y[i] = + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].mem[i].daddr; + cfg_ref->ref_frame_addr_c[i] = + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].mem[i].daddr + + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperline * + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].height; + } + for (; i < IMGU_ABI_FRAMES_REF; i++) { + cfg_ref->ref_frame_addr_y[i] = 0; + cfg_ref->ref_frame_addr_c[i] = 0; + } + + /* Configure DVS (digital video stabilization) */ + + cfg_dvs = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + &cofs->dmem.dvs, sizeof(*cfg_dvs), + vaddr); + if (!cfg_dvs) + goto bad_firmware; + + cfg_dvs->num_horizontal_blocks = + ALIGN(DIV_ROUND_UP(css_pipe->rect[IPU3_CSS_RECT_GDC].width, + IMGU_DVS_BLOCK_W), 2); + cfg_dvs->num_vertical_blocks = + DIV_ROUND_UP(css_pipe->rect[IPU3_CSS_RECT_GDC].height, + IMGU_DVS_BLOCK_H); + + /* Configure TNR (temporal noise reduction) */ + + if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { + cfg_tnr = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + &cofs->dmem.tnr3, + sizeof(*cfg_tnr), + vaddr); + if (!cfg_tnr) + goto bad_firmware; + + cfg_tnr->port_b.crop = 0; + cfg_tnr->port_b.elems = IMGU_ABI_ISP_DDR_WORD_BYTES; + cfg_tnr->port_b.width = + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].width; + cfg_tnr->port_b.stride = + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].bytesperline; + cfg_tnr->width_a_over_b = + IPU3_UAPI_ISP_VEC_ELEMS / cfg_tnr->port_b.elems; + cfg_tnr->frame_height = + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].height; + cfg_tnr->delay_frame = IPU3_CSS_AUX_FRAMES - 1; + for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) + cfg_tnr->frame_addr[i] = + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR] + .mem[i].daddr; + for (; i < IMGU_ABI_FRAMES_TNR; i++) + cfg_tnr->frame_addr[i] = 0; + } + + /* Configure ref dmem state parameters */ + + cfg = IMGU_ABI_PARAM_CLASS_STATE; + vaddr = css_pipe->binary_params_cs[cfg - 1][m0].vaddr; + + cfg_ref_state = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + &sofs->dmem.ref, + sizeof(*cfg_ref_state), + vaddr); + if (!cfg_ref_state) + goto bad_firmware; + + cfg_ref_state->ref_in_buf_idx = 0; + cfg_ref_state->ref_out_buf_idx = 1; + + /* Configure tnr dmem state parameters */ + if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { + cfg_tnr_state = + ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + &sofs->dmem.tnr3, + sizeof(*cfg_tnr_state), + vaddr); + if (!cfg_tnr_state) + goto bad_firmware; + + cfg_tnr_state->in_bufidx = 0; + cfg_tnr_state->out_bufidx = 1; + cfg_tnr_state->bypass_filter = 0; + cfg_tnr_state->total_frame_counter = 0; + for (i = 0; i < IMGU_ABI_BUF_SETS_TNR; i++) + cfg_tnr_state->buffer_frame_counter[i] = 0; + } + + /* Configure ISP stage */ + + isp_stage = css_pipe->xmem_isp_stage_ptrs[pipe][stage].vaddr; + memset(isp_stage, 0, sizeof(*isp_stage)); + isp_stage->blob_info = bi->blob; + isp_stage->binary_info = bi->info.isp.sp; + strscpy(isp_stage->binary_name, + (char *)css->fwp + bi->blob.prog_name_offset, + sizeof(isp_stage->binary_name)); + isp_stage->mem_initializers = bi->info.isp.sp.mem_initializers; + for (i = IMGU_ABI_PARAM_CLASS_CONFIG; i < IMGU_ABI_PARAM_CLASS_NUM; i++) + for (j = 0; j < IMGU_ABI_NUM_MEMORIES; j++) + isp_stage->mem_initializers.params[i][j].address = + css_pipe->binary_params_cs[i - 1][j].daddr; + + /* Configure SP stage */ + + sp_stage = css_pipe->xmem_sp_stage_ptrs[pipe][stage].vaddr; + memset(sp_stage, 0, sizeof(*sp_stage)); + + sp_stage->frames.in.buf_attr = buffer_sp_init; + for (i = 0; i < IMGU_ABI_BINARY_MAX_OUTPUT_PORTS; i++) + sp_stage->frames.out[i].buf_attr = buffer_sp_init; + sp_stage->frames.out_vf.buf_attr = buffer_sp_init; + sp_stage->frames.s3a_buf = buffer_sp_init; + sp_stage->frames.dvs_buf = buffer_sp_init; + + sp_stage->stage_type = IMGU_ABI_STAGE_TYPE_ISP; + sp_stage->num = stage; + sp_stage->isp_online = 0; + sp_stage->isp_copy_vf = 0; + sp_stage->isp_copy_output = 0; + + sp_stage->enable.vf_output = css_pipe->vf_output_en; + + sp_stage->frames.effective_in_res.width = + css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].width; + sp_stage->frames.effective_in_res.height = + css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].height; + sp_stage->frames.in.info.res.width = + css_pipe->queue[IPU3_CSS_QUEUE_IN].fmt.mpix.width; + sp_stage->frames.in.info.res.height = + css_pipe->queue[IPU3_CSS_QUEUE_IN].fmt.mpix.height; + sp_stage->frames.in.info.padded_width = + css_pipe->queue[IPU3_CSS_QUEUE_IN].width_pad; + sp_stage->frames.in.info.format = + css_pipe->queue[IPU3_CSS_QUEUE_IN].css_fmt->frame_format; + sp_stage->frames.in.info.raw_bit_depth = + css_pipe->queue[IPU3_CSS_QUEUE_IN].css_fmt->bit_depth; + sp_stage->frames.in.info.raw_bayer_order = + css_pipe->queue[IPU3_CSS_QUEUE_IN].css_fmt->bayer_order; + sp_stage->frames.in.info.raw_type = IMGU_ABI_RAW_TYPE_BAYER; + sp_stage->frames.in.buf_attr.buf_src.queue_id = IMGU_ABI_QUEUE_C_ID; + sp_stage->frames.in.buf_attr.buf_type = + IMGU_ABI_BUFFER_TYPE_INPUT_FRAME; + + sp_stage->frames.out[0].info.res.width = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.width; + sp_stage->frames.out[0].info.res.height = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.height; + sp_stage->frames.out[0].info.padded_width = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].width_pad; + sp_stage->frames.out[0].info.format = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->frame_format; + sp_stage->frames.out[0].info.raw_bit_depth = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bit_depth; + sp_stage->frames.out[0].info.raw_bayer_order = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bayer_order; + sp_stage->frames.out[0].info.raw_type = IMGU_ABI_RAW_TYPE_BAYER; + sp_stage->frames.out[0].planes.nv.uv.offset = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].width_pad * + css_pipe->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.height; + sp_stage->frames.out[0].buf_attr.buf_src.queue_id = IMGU_ABI_QUEUE_D_ID; + sp_stage->frames.out[0].buf_attr.buf_type = + IMGU_ABI_BUFFER_TYPE_OUTPUT_FRAME; + + sp_stage->frames.out[1].buf_attr.buf_src.queue_id = + IMGU_ABI_QUEUE_EVENT_ID; + + sp_stage->frames.internal_frame_info.res.width = + css_pipe->rect[IPU3_CSS_RECT_BDS].width; + sp_stage->frames.internal_frame_info.res.height = + css_pipe->rect[IPU3_CSS_RECT_BDS].height; + sp_stage->frames.internal_frame_info.padded_width = bds_width_pad; + + sp_stage->frames.internal_frame_info.format = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->frame_format; + sp_stage->frames.internal_frame_info.raw_bit_depth = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bit_depth; + sp_stage->frames.internal_frame_info.raw_bayer_order = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bayer_order; + sp_stage->frames.internal_frame_info.raw_type = IMGU_ABI_RAW_TYPE_BAYER; + + sp_stage->frames.out_vf.info.res.width = + css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.width; + sp_stage->frames.out_vf.info.res.height = + css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.height; + sp_stage->frames.out_vf.info.padded_width = + css_pipe->queue[IPU3_CSS_QUEUE_VF].width_pad; + sp_stage->frames.out_vf.info.format = + css_pipe->queue[IPU3_CSS_QUEUE_VF].css_fmt->frame_format; + sp_stage->frames.out_vf.info.raw_bit_depth = + css_pipe->queue[IPU3_CSS_QUEUE_VF].css_fmt->bit_depth; + sp_stage->frames.out_vf.info.raw_bayer_order = + css_pipe->queue[IPU3_CSS_QUEUE_VF].css_fmt->bayer_order; + sp_stage->frames.out_vf.info.raw_type = IMGU_ABI_RAW_TYPE_BAYER; + sp_stage->frames.out_vf.planes.yuv.u.offset = + css_pipe->queue[IPU3_CSS_QUEUE_VF].width_pad * + css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.height; + sp_stage->frames.out_vf.planes.yuv.v.offset = + css_pipe->queue[IPU3_CSS_QUEUE_VF].width_pad * + css_pipe->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.height * 5 / 4; + sp_stage->frames.out_vf.buf_attr.buf_src.queue_id = IMGU_ABI_QUEUE_E_ID; + sp_stage->frames.out_vf.buf_attr.buf_type = + IMGU_ABI_BUFFER_TYPE_VF_OUTPUT_FRAME; + + sp_stage->frames.s3a_buf.buf_src.queue_id = IMGU_ABI_QUEUE_F_ID; + sp_stage->frames.s3a_buf.buf_type = IMGU_ABI_BUFFER_TYPE_3A_STATISTICS; + + sp_stage->frames.dvs_buf.buf_src.queue_id = IMGU_ABI_QUEUE_G_ID; + sp_stage->frames.dvs_buf.buf_type = IMGU_ABI_BUFFER_TYPE_DIS_STATISTICS; + + sp_stage->dvs_envelope.width = css_pipe->rect[IPU3_CSS_RECT_ENVELOPE].width; + sp_stage->dvs_envelope.height = + css_pipe->rect[IPU3_CSS_RECT_ENVELOPE].height; + + sp_stage->isp_pipe_version = + bi->info.isp.sp.pipeline.isp_pipe_version; + sp_stage->isp_deci_log_factor = + clamp(max(fls(css_pipe->rect[IPU3_CSS_RECT_BDS].width / + IMGU_MAX_BQ_GRID_WIDTH), + fls(css_pipe->rect[IPU3_CSS_RECT_BDS].height / + IMGU_MAX_BQ_GRID_HEIGHT)) - 1, 3, 5); + sp_stage->isp_vf_downscale_bits = 0; + sp_stage->if_config_index = 255; + sp_stage->sp_enable_xnr = 0; + sp_stage->num_stripes = stripes; + sp_stage->enable.s3a = 1; + sp_stage->enable.dvs_stats = 0; + + sp_stage->xmem_bin_addr = css->binary[css_pipe->bindex].daddr; + sp_stage->xmem_map_addr = css_pipe->sp_ddr_ptrs.daddr; + sp_stage->isp_stage_addr = + css_pipe->xmem_isp_stage_ptrs[pipe][stage].daddr; + + /* Configure SP group */ + + sp_group = css->xmem_sp_group_ptrs.vaddr; + memset(&sp_group->pipe[pipe], 0, sizeof(struct imgu_abi_sp_pipeline)); + + sp_group->pipe[pipe].num_stages = 1; + sp_group->pipe[pipe].pipe_id = css_pipe->pipe_id; + sp_group->pipe[pipe].thread_id = pipe; + sp_group->pipe[pipe].pipe_num = pipe; + sp_group->pipe[pipe].num_execs = -1; + sp_group->pipe[pipe].pipe_qos_config = -1; + sp_group->pipe[pipe].required_bds_factor = 0; + sp_group->pipe[pipe].dvs_frame_delay = IPU3_CSS_AUX_FRAMES - 1; + sp_group->pipe[pipe].inout_port_config = + IMGU_ABI_PORT_CONFIG_TYPE_INPUT_HOST | + IMGU_ABI_PORT_CONFIG_TYPE_OUTPUT_HOST; + sp_group->pipe[pipe].scaler_pp_lut = 0; + sp_group->pipe[pipe].shading.internal_frame_origin_x_bqs_on_sctbl = 0; + sp_group->pipe[pipe].shading.internal_frame_origin_y_bqs_on_sctbl = 0; + sp_group->pipe[pipe].sp_stage_addr[stage] = + css_pipe->xmem_sp_stage_ptrs[pipe][stage].daddr; + sp_group->pipe[pipe].pipe_config = + bi->info.isp.sp.enable.params ? (1 << pipe) : 0; + sp_group->pipe[pipe].pipe_config |= IMGU_ABI_PIPE_CONFIG_ACQUIRE_ISP; + + /* Initialize parameter pools */ + + if (ipu3_css_pool_init(imgu, &css_pipe->pool.parameter_set_info, + sizeof(struct imgu_abi_parameter_set_info)) || + ipu3_css_pool_init(imgu, &css_pipe->pool.acc, + sizeof(struct imgu_abi_acc_param)) || + ipu3_css_pool_init(imgu, &css_pipe->pool.gdc, + sizeof(struct imgu_abi_gdc_warp_param) * + 3 * cfg_dvs->num_horizontal_blocks / 2 * + cfg_dvs->num_vertical_blocks) || + ipu3_css_pool_init(imgu, &css_pipe->pool.obgrid, + ipu3_css_fw_obgrid_size( + &css->fwp->binary_header[css_pipe->bindex]))) + goto out_of_memory; + + for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) + if (ipu3_css_pool_init(imgu, + &css_pipe->pool.binary_params_p[i], + bi->info.isp.sp.mem_initializers.params + [IMGU_ABI_PARAM_CLASS_PARAM][i].size)) + goto out_of_memory; + + return 0; + +bad_firmware: + ipu3_css_pipeline_cleanup(css, pipe); + return -EPROTO; + +out_of_memory: + ipu3_css_pipeline_cleanup(css, pipe); + return -ENOMEM; +} + +static u8 ipu3_css_queue_pos(struct ipu3_css *css, int queue, int thread) +{ + static const unsigned int sp; + void __iomem *const base = css->base; + struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[sp]]; + struct imgu_abi_queues __iomem *q = base + IMGU_REG_SP_DMEM_BASE(sp) + + bi->info.sp.host_sp_queue; + + return queue >= 0 ? readb(&q->host2sp_bufq_info[thread][queue].end) : + readb(&q->host2sp_evtq_info.end); +} + +/* Sent data to sp using given buffer queue, or if queue < 0, event queue. */ +static int ipu3_css_queue_data(struct ipu3_css *css, + int queue, int thread, u32 data) +{ + static const unsigned int sp; + void __iomem *const base = css->base; + struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[sp]]; + struct imgu_abi_queues __iomem *q = base + IMGU_REG_SP_DMEM_BASE(sp) + + bi->info.sp.host_sp_queue; + u8 size, start, end, end2; + + if (queue >= 0) { + size = readb(&q->host2sp_bufq_info[thread][queue].size); + start = readb(&q->host2sp_bufq_info[thread][queue].start); + end = readb(&q->host2sp_bufq_info[thread][queue].end); + } else { + size = readb(&q->host2sp_evtq_info.size); + start = readb(&q->host2sp_evtq_info.start); + end = readb(&q->host2sp_evtq_info.end); + } + + if (size == 0) + return -EIO; + + end2 = (end + 1) % size; + if (end2 == start) + return -EBUSY; /* Queue full */ + + if (queue >= 0) { + writel(data, &q->host2sp_bufq[thread][queue][end]); + writeb(end2, &q->host2sp_bufq_info[thread][queue].end); + } else { + writel(data, &q->host2sp_evtq[end]); + writeb(end2, &q->host2sp_evtq_info.end); + } + + return 0; +} + +/* Receive data using given buffer queue, or if queue < 0, event queue. */ +static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data) +{ + static const unsigned int sp; + void __iomem *const base = css->base; + struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[sp]]; + struct imgu_abi_queues __iomem *q = base + IMGU_REG_SP_DMEM_BASE(sp) + + bi->info.sp.host_sp_queue; + u8 size, start, end, start2; + + if (queue >= 0) { + size = readb(&q->sp2host_bufq_info[queue].size); + start = readb(&q->sp2host_bufq_info[queue].start); + end = readb(&q->sp2host_bufq_info[queue].end); + } else { + size = readb(&q->sp2host_evtq_info.size); + start = readb(&q->sp2host_evtq_info.start); + end = readb(&q->sp2host_evtq_info.end); + } + + if (size == 0) + return -EIO; + + if (end == start) + return -EBUSY; /* Queue empty */ + + start2 = (start + 1) % size; + + if (queue >= 0) { + *data = readl(&q->sp2host_bufq[queue][start]); + writeb(start2, &q->sp2host_bufq_info[queue].start); + } else { + int r; + + *data = readl(&q->sp2host_evtq[start]); + writeb(start2, &q->sp2host_evtq_info.start); + + /* Acknowledge events dequeued from event queue */ + r = ipu3_css_queue_data(css, queue, 0, + IMGU_ABI_EVENT_EVENT_DEQUEUED); + if (r < 0) + return r; + } + + return 0; +} + +/* Free binary-specific resources */ +static void ipu3_css_binary_cleanup(struct ipu3_css *css, unsigned int pipe) +{ + struct imgu_device *imgu = dev_get_drvdata(css->dev); + unsigned int i, j; + + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + + for (j = 0; j < IMGU_ABI_PARAM_CLASS_NUM - 1; j++) + for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) + ipu3_dmamap_free(imgu, + &css_pipe->binary_params_cs[j][i]); + + j = IPU3_CSS_AUX_FRAME_REF; + for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) + ipu3_dmamap_free(imgu, + &css_pipe->aux_frames[j].mem[i]); + + j = IPU3_CSS_AUX_FRAME_TNR; + for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) + ipu3_dmamap_free(imgu, + &css_pipe->aux_frames[j].mem[i]); +} + +static int ipu3_css_binary_preallocate(struct ipu3_css *css, unsigned int pipe) +{ + struct imgu_device *imgu = dev_get_drvdata(css->dev); + unsigned int i, j; + + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + + for (j = IMGU_ABI_PARAM_CLASS_CONFIG; + j < IMGU_ABI_PARAM_CLASS_NUM; j++) + for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) + if (!ipu3_dmamap_alloc(imgu, + &css_pipe->binary_params_cs[j - 1][i], + CSS_ABI_SIZE)) + goto out_of_memory; + + for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) + if (!ipu3_dmamap_alloc(imgu, + &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF]. + mem[i], CSS_BDS_SIZE)) + goto out_of_memory; + + for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) + if (!ipu3_dmamap_alloc(imgu, + &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR]. + mem[i], CSS_GDC_SIZE)) + goto out_of_memory; + + return 0; + +out_of_memory: + ipu3_css_binary_cleanup(css, pipe); + return -ENOMEM; +} + +/* allocate binary-specific resources */ +static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) +{ + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; + struct imgu_device *imgu = dev_get_drvdata(css->dev); + int i, j, size; + static const int BYPC = 2; /* Bytes per component */ + unsigned int w, h; + + /* Allocate parameter memory blocks for this binary */ + + for (j = IMGU_ABI_PARAM_CLASS_CONFIG; j < IMGU_ABI_PARAM_CLASS_NUM; j++) + for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) { + if (ipu3_css_dma_buffer_resize( + imgu, + &css_pipe->binary_params_cs[j - 1][i], + bi->info.isp.sp.mem_initializers.params[j][i].size)) + goto out_of_memory; + } + + /* Allocate internal frame buffers */ + + /* Reference frames for DVS, FRAME_FORMAT_YUV420_16 */ + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperpixel = BYPC; + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].width = + css_pipe->rect[IPU3_CSS_RECT_BDS].width; + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].height = + ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].height, + IMGU_DVS_BLOCK_H) + 2 * IMGU_GDC_BUF_Y; + h = css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].height; + w = ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, + 2 * IPU3_UAPI_ISP_VEC_ELEMS) + 2 * IMGU_GDC_BUF_X; + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperline = + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperpixel * w; + size = w * h * BYPC + (w / 2) * (h / 2) * BYPC * 2; + for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) + if (ipu3_css_dma_buffer_resize( + imgu, + &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].mem[i], + size)) + goto out_of_memory; + + /* TNR frames for temporal noise reduction, FRAME_FORMAT_YUV_LINE */ + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].bytesperpixel = 1; + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].width = + roundup(css_pipe->rect[IPU3_CSS_RECT_GDC].width, + bi->info.isp.sp.block.block_width * + IPU3_UAPI_ISP_VEC_ELEMS); + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].height = + roundup(css_pipe->rect[IPU3_CSS_RECT_GDC].height, + bi->info.isp.sp.block.output_block_height); + + w = css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].width; + css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].bytesperline = w; + h = css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].height; + size = w * ALIGN(h * 3 / 2 + 3, 2); /* +3 for vf_pp prefetch */ + for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) + if (ipu3_css_dma_buffer_resize( + imgu, + &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].mem[i], + size)) + goto out_of_memory; + + return 0; + +out_of_memory: + ipu3_css_binary_cleanup(css, pipe); + return -ENOMEM; +} + +int ipu3_css_start_streaming(struct ipu3_css *css) +{ + u32 data; + int r, pipe; + + if (css->streaming) + return -EPROTO; + + for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { + r = ipu3_css_binary_setup(css, pipe); + if (r < 0) + return r; + } + + r = ipu3_css_hw_init(css); + if (r < 0) + return r; + + r = ipu3_css_hw_start(css); + if (r < 0) + goto fail; + + for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { + r = ipu3_css_pipeline_init(css, pipe); + if (r < 0) + goto fail; + } + + css->streaming = true; + + ipu3_css_hw_enable_irq(css); + + /* Initialize parameters to default */ + for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { + r = ipu3_css_set_parameters(css, pipe, NULL); + if (r < 0) + goto fail; + } + + while (!(r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_A_ID, &data))) + ; + if (r != -EBUSY) + goto fail; + + while (!(r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_B_ID, &data))) + ; + if (r != -EBUSY) + goto fail; + + for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { + r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + IMGU_ABI_EVENT_START_STREAM | + pipe << 16); + if (r < 0) + goto fail; + } + + return 0; + +fail: + css->streaming = false; + ipu3_css_hw_cleanup(css); + for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { + ipu3_css_pipeline_cleanup(css, pipe); + ipu3_css_binary_cleanup(css, pipe); + } + + return r; +} + +void ipu3_css_stop_streaming(struct ipu3_css *css) +{ + struct ipu3_css_buffer *b, *b0; + int q, r, pipe; + + for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { + r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + IMGU_ABI_EVENT_STOP_STREAM); + if (r < 0) + dev_warn(css->dev, "failed on stop stream event\n"); + } + + if (!css->streaming) + return; + + ipu3_css_hw_stop(css); + + ipu3_css_hw_cleanup(css); + + for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + + ipu3_css_pipeline_cleanup(css, pipe); + + spin_lock(&css_pipe->qlock); + for (q = 0; q < IPU3_CSS_QUEUES; q++) + list_for_each_entry_safe(b, b0, + &css_pipe->queue[q].bufs, + list) { + b->state = IPU3_CSS_BUFFER_FAILED; + list_del(&b->list); + } + spin_unlock(&css_pipe->qlock); + } + + css->streaming = false; +} + +bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe) +{ + int q; + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + + spin_lock(&css_pipe->qlock); + for (q = 0; q < IPU3_CSS_QUEUES; q++) + if (!list_empty(&css_pipe->queue[q].bufs)) + break; + spin_unlock(&css_pipe->qlock); + return (q == IPU3_CSS_QUEUES); +} + +bool ipu3_css_queue_empty(struct ipu3_css *css) +{ + unsigned int pipe; + bool ret = 0; + + for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) + ret &= ipu3_css_pipe_queue_empty(css, pipe); + + return ret; +} + +bool ipu3_css_is_streaming(struct ipu3_css *css) +{ + return css->streaming; +} + +static int ipu3_css_map_init(struct ipu3_css *css, unsigned int pipe) +{ + struct imgu_device *imgu = dev_get_drvdata(css->dev); + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + unsigned int p, q, i; + + /* Allocate and map common structures with imgu hardware */ + for (p = 0; p < IPU3_CSS_PIPE_ID_NUM; p++) + for (i = 0; i < IMGU_ABI_MAX_STAGES; i++) { + if (!ipu3_dmamap_alloc(imgu, + &css_pipe-> + xmem_sp_stage_ptrs[p][i], + sizeof(struct imgu_abi_sp_stage))) + return -ENOMEM; + if (!ipu3_dmamap_alloc(imgu, + &css_pipe-> + xmem_isp_stage_ptrs[p][i], + sizeof(struct imgu_abi_isp_stage))) + return -ENOMEM; + } + + if (!ipu3_dmamap_alloc(imgu, &css_pipe->sp_ddr_ptrs, + ALIGN(sizeof(struct imgu_abi_ddr_address_map), + IMGU_ABI_ISP_DDR_WORD_BYTES))) + return -ENOMEM; + + for (q = 0; q < IPU3_CSS_QUEUES; q++) { + unsigned int abi_buf_num = ARRAY_SIZE(css_pipe->abi_buffers[q]); + + for (i = 0; i < abi_buf_num; i++) + if (!ipu3_dmamap_alloc(imgu, + &css_pipe->abi_buffers[q][i], + sizeof(struct imgu_abi_buffer))) + return -ENOMEM; + } + + if (ipu3_css_binary_preallocate(css, pipe)) { + ipu3_css_binary_cleanup(css, pipe); + return -ENOMEM; + } + + return 0; +} + +static void ipu3_css_pipe_cleanup(struct ipu3_css *css, unsigned int pipe) +{ + struct imgu_device *imgu = dev_get_drvdata(css->dev); + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + unsigned int p, q, i, abi_buf_num; + + ipu3_css_binary_cleanup(css, pipe); + + for (q = 0; q < IPU3_CSS_QUEUES; q++) { + abi_buf_num = ARRAY_SIZE(css_pipe->abi_buffers[q]); + for (i = 0; i < abi_buf_num; i++) + ipu3_dmamap_free(imgu, &css_pipe->abi_buffers[q][i]); + } + + for (p = 0; p < IPU3_CSS_PIPE_ID_NUM; p++) + for (i = 0; i < IMGU_ABI_MAX_STAGES; i++) { + ipu3_dmamap_free(imgu, + &css_pipe->xmem_sp_stage_ptrs[p][i]); + ipu3_dmamap_free(imgu, + &css_pipe->xmem_isp_stage_ptrs[p][i]); + } + + ipu3_dmamap_free(imgu, &css_pipe->sp_ddr_ptrs); +} + +void ipu3_css_cleanup(struct ipu3_css *css) +{ + struct imgu_device *imgu = dev_get_drvdata(css->dev); + unsigned int pipe; + + ipu3_css_stop_streaming(css); + for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) + ipu3_css_pipe_cleanup(css, pipe); + ipu3_dmamap_free(imgu, &css->xmem_sp_group_ptrs); + ipu3_css_fw_cleanup(css); +} + +int ipu3_css_init(struct device *dev, struct ipu3_css *css, + void __iomem *base, int length) +{ + struct imgu_device *imgu = dev_get_drvdata(dev); + int r, q, pipe; + + /* Initialize main data structure */ + css->dev = dev; + css->base = base; + css->iomem_length = length; + + for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) { + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + + css_pipe->vf_output_en = false; + spin_lock_init(&css_pipe->qlock); + css_pipe->bindex = IPU3_CSS_DEFAULT_BINARY; + css_pipe->pipe_id = IPU3_CSS_PIPE_ID_VIDEO; + for (q = 0; q < IPU3_CSS_QUEUES; q++) { + r = ipu3_css_queue_init(&css_pipe->queue[q], NULL, 0); + if (r) + return r; + } + r = ipu3_css_map_init(css, pipe); + if (r) { + ipu3_css_cleanup(css); + return r; + } + } + if (!ipu3_dmamap_alloc(imgu, &css->xmem_sp_group_ptrs, + sizeof(struct imgu_abi_sp_group))) + return -ENOMEM; + + r = ipu3_css_fw_init(css); + if (r) + return r; + + return 0; +} + +static u32 ipu3_css_adjust(u32 res, u32 align) +{ + u32 val = max_t(u32, IPU3_CSS_MIN_RES, res); + + return DIV_ROUND_CLOSEST(val, align) * align; +} + +/* Select a binary matching the required resolutions and formats */ +static int ipu3_css_find_binary(struct ipu3_css *css, + unsigned int pipe, + struct ipu3_css_queue queue[IPU3_CSS_QUEUES], + struct v4l2_rect rects[IPU3_CSS_RECTS]) +{ + const int binary_nr = css->fwp->file_header.binary_nr; + unsigned int binary_mode = + (css->pipes[pipe].pipe_id == IPU3_CSS_PIPE_ID_CAPTURE) ? + IA_CSS_BINARY_MODE_PRIMARY : IA_CSS_BINARY_MODE_VIDEO; + const struct v4l2_pix_format_mplane *in = + &queue[IPU3_CSS_QUEUE_IN].fmt.mpix; + const struct v4l2_pix_format_mplane *out = + &queue[IPU3_CSS_QUEUE_OUT].fmt.mpix; + const struct v4l2_pix_format_mplane *vf = + &queue[IPU3_CSS_QUEUE_VF].fmt.mpix; + u32 stripe_w = 0, stripe_h = 0; + const char *name; + int i, j; + + if (!ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_IN])) + return -EINVAL; + + /* Find out the strip size boundary */ + for (i = 0; i < binary_nr; i++) { + struct imgu_fw_info *bi = &css->fwp->binary_header[i]; + + u32 max_width = bi->info.isp.sp.output.max_width; + u32 max_height = bi->info.isp.sp.output.max_height; + + if (bi->info.isp.sp.iterator.num_stripes <= 1) { + stripe_w = stripe_w ? + min(stripe_w, max_width) : max_width; + stripe_h = stripe_h ? + min(stripe_h, max_height) : max_height; + } + } + + for (i = 0; i < binary_nr; i++) { + struct imgu_fw_info *bi = &css->fwp->binary_header[i]; + enum imgu_abi_frame_format q_fmt; + + name = (void *)css->fwp + bi->blob.prog_name_offset; + + /* Check that binary supports memory-to-memory processing */ + if (bi->info.isp.sp.input.source != + IMGU_ABI_BINARY_INPUT_SOURCE_MEMORY) + continue; + + /* Check that binary supports raw10 input */ + if (!bi->info.isp.sp.enable.input_feeder && + !bi->info.isp.sp.enable.input_raw) + continue; + + /* Check binary mode */ + if (bi->info.isp.sp.pipeline.mode != binary_mode) + continue; + + /* Since input is RGGB bayer, need to process colors */ + if (bi->info.isp.sp.enable.luma_only) + continue; + + if (in->width < bi->info.isp.sp.input.min_width || + in->width > bi->info.isp.sp.input.max_width || + in->height < bi->info.isp.sp.input.min_height || + in->height > bi->info.isp.sp.input.max_height) + continue; + + if (ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_OUT])) { + if (bi->info.isp.num_output_pins <= 0) + continue; + + q_fmt = queue[IPU3_CSS_QUEUE_OUT].css_fmt->frame_format; + for (j = 0; j < bi->info.isp.num_output_formats; j++) + if (bi->info.isp.output_formats[j] == q_fmt) + break; + if (j >= bi->info.isp.num_output_formats) + continue; + + if (out->width < bi->info.isp.sp.output.min_width || + out->width > bi->info.isp.sp.output.max_width || + out->height < bi->info.isp.sp.output.min_height || + out->height > bi->info.isp.sp.output.max_height) + continue; + + if (out->width > bi->info.isp.sp.internal.max_width || + out->height > bi->info.isp.sp.internal.max_height) + continue; + } + + if (ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_VF])) { + if (bi->info.isp.num_output_pins <= 1) + continue; + + q_fmt = queue[IPU3_CSS_QUEUE_VF].css_fmt->frame_format; + for (j = 0; j < bi->info.isp.num_output_formats; j++) + if (bi->info.isp.output_formats[j] == q_fmt) + break; + if (j >= bi->info.isp.num_output_formats) + continue; + + if (vf->width < bi->info.isp.sp.output.min_width || + vf->width > bi->info.isp.sp.output.max_width || + vf->height < bi->info.isp.sp.output.min_height || + vf->height > bi->info.isp.sp.output.max_height) + continue; + } + + /* All checks passed, select the binary */ + dev_dbg(css->dev, "using binary %s id = %u\n", name, + bi->info.isp.sp.id); + return i; + } + + /* Can not find suitable binary for these parameters */ + return -EINVAL; +} + +/* + * Check that there is a binary matching requirements. Parameters may be + * NULL indicating disabled input/output. Return negative if given + * parameters can not be supported or on error, zero or positive indicating + * found binary number. May modify the given parameters if not exact match + * is found. + */ +int ipu3_css_fmt_try(struct ipu3_css *css, + struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], + struct v4l2_rect *rects[IPU3_CSS_RECTS], + unsigned int pipe) +{ + static const u32 EFF_ALIGN_W = 2; + static const u32 BDS_ALIGN_W = 4; + static const u32 OUT_ALIGN_W = 8; + static const u32 OUT_ALIGN_H = 4; + static const u32 VF_ALIGN_W = 2; + static const char *qnames[IPU3_CSS_QUEUES] = { + [IPU3_CSS_QUEUE_IN] = "in", + [IPU3_CSS_QUEUE_PARAMS] = "params", + [IPU3_CSS_QUEUE_OUT] = "out", + [IPU3_CSS_QUEUE_VF] = "vf", + [IPU3_CSS_QUEUE_STAT_3A] = "3a", + }; + static const char *rnames[IPU3_CSS_RECTS] = { + [IPU3_CSS_RECT_EFFECTIVE] = "effective resolution", + [IPU3_CSS_RECT_BDS] = "bayer-domain scaled resolution", + [IPU3_CSS_RECT_ENVELOPE] = "DVS envelope size", + [IPU3_CSS_RECT_GDC] = "GDC output res", + }; + struct v4l2_rect r[IPU3_CSS_RECTS] = { }; + struct v4l2_rect *const eff = &r[IPU3_CSS_RECT_EFFECTIVE]; + struct v4l2_rect *const bds = &r[IPU3_CSS_RECT_BDS]; + struct v4l2_rect *const env = &r[IPU3_CSS_RECT_ENVELOPE]; + struct v4l2_rect *const gdc = &r[IPU3_CSS_RECT_GDC]; + struct ipu3_css_queue q[IPU3_CSS_QUEUES]; + struct v4l2_pix_format_mplane *const in = + &q[IPU3_CSS_QUEUE_IN].fmt.mpix; + struct v4l2_pix_format_mplane *const out = + &q[IPU3_CSS_QUEUE_OUT].fmt.mpix; + struct v4l2_pix_format_mplane *const vf = + &q[IPU3_CSS_QUEUE_VF].fmt.mpix; + int i, s; + + /* Adjust all formats, get statistics buffer sizes and formats */ + for (i = 0; i < IPU3_CSS_QUEUES; i++) { + if (fmts[i]) + dev_dbg(css->dev, "%s %s: (%i,%i) fmt 0x%x\n", __func__, + qnames[i], fmts[i]->width, fmts[i]->height, + fmts[i]->pixelformat); + else + dev_dbg(css->dev, "%s %s: (not set)\n", __func__, + qnames[i]); + if (ipu3_css_queue_init(&q[i], fmts[i], + IPU3_CSS_QUEUE_TO_FLAGS(i))) { + dev_notice(css->dev, "can not initialize queue %s\n", + qnames[i]); + return -EINVAL; + } + } + for (i = 0; i < IPU3_CSS_RECTS; i++) { + if (rects[i]) { + dev_dbg(css->dev, "%s %s: (%i,%i)\n", __func__, + rnames[i], rects[i]->width, rects[i]->height); + r[i].width = rects[i]->width; + r[i].height = rects[i]->height; + } else { + dev_dbg(css->dev, "%s %s: (not set)\n", __func__, + rnames[i]); + } + /* For now, force known good resolutions */ + r[i].left = 0; + r[i].top = 0; + } + + /* Always require one input and vf only if out is also enabled */ + if (!ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_IN]) || + !ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { + dev_warn(css->dev, "required queues are disabled\n"); + return -EINVAL; + } + + if (!ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { + out->width = in->width; + out->height = in->height; + } + if (eff->width <= 0 || eff->height <= 0) { + eff->width = in->width; + eff->height = in->height; + } + if (bds->width <= 0 || bds->height <= 0) { + bds->width = out->width; + bds->height = out->height; + } + if (gdc->width <= 0 || gdc->height <= 0) { + gdc->width = out->width; + gdc->height = out->height; + } + + in->width = ipu3_css_adjust(in->width, 1); + in->height = ipu3_css_adjust(in->height, 1); + eff->width = ipu3_css_adjust(eff->width, EFF_ALIGN_W); + eff->height = ipu3_css_adjust(eff->height, 1); + bds->width = ipu3_css_adjust(bds->width, BDS_ALIGN_W); + bds->height = ipu3_css_adjust(bds->height, 1); + gdc->width = ipu3_css_adjust(gdc->width, OUT_ALIGN_W); + gdc->height = ipu3_css_adjust(gdc->height, OUT_ALIGN_H); + out->width = ipu3_css_adjust(out->width, OUT_ALIGN_W); + out->height = ipu3_css_adjust(out->height, OUT_ALIGN_H); + vf->width = ipu3_css_adjust(vf->width, VF_ALIGN_W); + vf->height = ipu3_css_adjust(vf->height, 1); + + s = (bds->width - gdc->width) / 2 - FILTER_SIZE; + env->width = s < MIN_ENVELOPE ? MIN_ENVELOPE : s; + s = (bds->height - gdc->height) / 2 - FILTER_SIZE; + env->height = s < MIN_ENVELOPE ? MIN_ENVELOPE : s; + + css->pipes[pipe].bindex = + ipu3_css_find_binary(css, pipe, q, r); + if (css->pipes[pipe].bindex < 0) { + dev_err(css->dev, "failed to find suitable binary\n"); + return -EINVAL; + } + + dev_dbg(css->dev, "Binary index %d for pipe %d found.", + css->pipes[pipe].bindex, pipe); + + /* Final adjustment and set back the queried formats */ + for (i = 0; i < IPU3_CSS_QUEUES; i++) { + if (fmts[i]) { + if (ipu3_css_queue_init(&q[i], &q[i].fmt.mpix, + IPU3_CSS_QUEUE_TO_FLAGS(i))) { + dev_err(css->dev, + "final resolution adjustment failed\n"); + return -EINVAL; + } + *fmts[i] = q[i].fmt.mpix; + } + } + + for (i = 0; i < IPU3_CSS_RECTS; i++) + if (rects[i]) + *rects[i] = r[i]; + + dev_dbg(css->dev, + "in(%u,%u) if(%u,%u) ds(%u,%u) gdc(%u,%u) out(%u,%u) vf(%u,%u)", + in->width, in->height, eff->width, eff->height, + bds->width, bds->height, gdc->width, gdc->height, + out->width, out->height, vf->width, vf->height); + + return 0; +} + +int ipu3_css_fmt_set(struct ipu3_css *css, + struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], + struct v4l2_rect *rects[IPU3_CSS_RECTS], + unsigned int pipe) +{ + struct v4l2_rect rect_data[IPU3_CSS_RECTS]; + struct v4l2_rect *all_rects[IPU3_CSS_RECTS]; + int i, r; + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + + for (i = 0; i < IPU3_CSS_RECTS; i++) { + if (rects[i]) + rect_data[i] = *rects[i]; + else + memset(&rect_data[i], 0, sizeof(rect_data[i])); + all_rects[i] = &rect_data[i]; + } + r = ipu3_css_fmt_try(css, fmts, all_rects, pipe); + if (r < 0) + return r; + + for (i = 0; i < IPU3_CSS_QUEUES; i++) + if (ipu3_css_queue_init(&css_pipe->queue[i], fmts[i], + IPU3_CSS_QUEUE_TO_FLAGS(i))) + return -EINVAL; + for (i = 0; i < IPU3_CSS_RECTS; i++) { + css_pipe->rect[i] = rect_data[i]; + if (rects[i]) + *rects[i] = rect_data[i]; + } + + return 0; +} + +int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt) +{ + switch (fmt->dataformat) { + case V4L2_META_FMT_IPU3_PARAMS: + fmt->buffersize = sizeof(struct ipu3_uapi_params); + break; + case V4L2_META_FMT_IPU3_STAT_3A: + fmt->buffersize = sizeof(struct ipu3_uapi_stats_3a); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * Queue given buffer to CSS. ipu3_css_buf_prepare() must have been first + * called for the buffer. May be called from interrupt context. + * Returns 0 on success, -EBUSY if the buffer queue is full, or some other + * code on error conditions. + */ +int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, + struct ipu3_css_buffer *b) +{ + struct imgu_abi_buffer *abi_buf; + struct imgu_addr_t *buf_addr; + u32 data; + int r; + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + + if (!css->streaming) + return -EPROTO; /* CSS or buffer in wrong state */ + + if (b->queue >= IPU3_CSS_QUEUES || !ipu3_css_queues[b->queue].qid) + return -EINVAL; + + b->queue_pos = ipu3_css_queue_pos(css, ipu3_css_queues[b->queue].qid, + pipe); + + if (b->queue_pos >= ARRAY_SIZE(css->pipes[pipe].abi_buffers[b->queue])) + return -EIO; + abi_buf = css->pipes[pipe].abi_buffers[b->queue][b->queue_pos].vaddr; + + /* Fill struct abi_buffer for firmware */ + memset(abi_buf, 0, sizeof(*abi_buf)); + + buf_addr = (void *)abi_buf + ipu3_css_queues[b->queue].ptr_ofs; + *(imgu_addr_t *)buf_addr = b->daddr; + + if (b->queue == IPU3_CSS_QUEUE_STAT_3A) + abi_buf->payload.s3a.data.dmem.s3a_tbl = b->daddr; + + if (b->queue == IPU3_CSS_QUEUE_OUT) + abi_buf->payload.frame.padded_width = + css_pipe->queue[IPU3_CSS_QUEUE_OUT].width_pad; + + if (b->queue == IPU3_CSS_QUEUE_VF) + abi_buf->payload.frame.padded_width = + css_pipe->queue[IPU3_CSS_QUEUE_VF].width_pad; + + spin_lock(&css_pipe->qlock); + list_add_tail(&b->list, &css_pipe->queue[b->queue].bufs); + spin_unlock(&css_pipe->qlock); + b->state = IPU3_CSS_BUFFER_QUEUED; + + data = css->pipes[pipe].abi_buffers[b->queue][b->queue_pos].daddr; + r = ipu3_css_queue_data(css, ipu3_css_queues[b->queue].qid, + pipe, data); + if (r < 0) + goto queueing_failed; + + data = IMGU_ABI_EVENT_BUFFER_ENQUEUED(pipe, + ipu3_css_queues[b->queue].qid); + r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, data); + if (r < 0) + goto queueing_failed; + + dev_dbg(css->dev, "queued buffer %p to css queue %i in pipe %d\n", + b, b->queue, pipe); + + return 0; + +queueing_failed: + b->state = (r == -EBUSY || r == -EAGAIN) ? + IPU3_CSS_BUFFER_NEW : IPU3_CSS_BUFFER_FAILED; + list_del(&b->list); + + return r; +} + +/* + * Get next ready CSS buffer. Returns -EAGAIN in which case the function + * should be called again, or -EBUSY which means that there are no more + * buffers available. May be called from interrupt context. + */ +struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) +{ + static const unsigned char evtype_to_queue[] = { + [IMGU_ABI_EVTTYPE_INPUT_FRAME_DONE] = IPU3_CSS_QUEUE_IN, + [IMGU_ABI_EVTTYPE_OUT_FRAME_DONE] = IPU3_CSS_QUEUE_OUT, + [IMGU_ABI_EVTTYPE_VF_OUT_FRAME_DONE] = IPU3_CSS_QUEUE_VF, + [IMGU_ABI_EVTTYPE_3A_STATS_DONE] = IPU3_CSS_QUEUE_STAT_3A, + }; + struct ipu3_css_buffer *b = ERR_PTR(-EAGAIN); + u32 event, daddr; + int evtype, pipe, pipeid, queue, qid, r; + struct ipu3_css_pipe *css_pipe; + + if (!css->streaming) + return ERR_PTR(-EPROTO); + + r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); + if (r < 0) + return ERR_PTR(r); + + evtype = (event & IMGU_ABI_EVTTYPE_EVENT_MASK) >> + IMGU_ABI_EVTTYPE_EVENT_SHIFT; + + switch (evtype) { + case IMGU_ABI_EVTTYPE_OUT_FRAME_DONE: + case IMGU_ABI_EVTTYPE_VF_OUT_FRAME_DONE: + case IMGU_ABI_EVTTYPE_3A_STATS_DONE: + case IMGU_ABI_EVTTYPE_INPUT_FRAME_DONE: + pipe = (event & IMGU_ABI_EVTTYPE_PIPE_MASK) >> + IMGU_ABI_EVTTYPE_PIPE_SHIFT; + pipeid = (event & IMGU_ABI_EVTTYPE_PIPEID_MASK) >> + IMGU_ABI_EVTTYPE_PIPEID_SHIFT; + queue = evtype_to_queue[evtype]; + qid = ipu3_css_queues[queue].qid; + + if (pipe >= IMGU_MAX_PIPE_NUM) { + dev_err(css->dev, "Invalid pipe: %i\n", pipe); + return ERR_PTR(-EIO); + } + + if (qid >= IMGU_ABI_QUEUE_NUM) { + dev_err(css->dev, "Invalid qid: %i\n", qid); + return ERR_PTR(-EIO); + } + css_pipe = &css->pipes[pipe]; + dev_dbg(css->dev, + "event: buffer done 0x%x queue %i pipe %i pipeid %i\n", + event, queue, pipe, pipeid); + + r = ipu3_css_dequeue_data(css, qid, &daddr); + if (r < 0) { + dev_err(css->dev, "failed to dequeue buffer\n"); + /* Force real error, not -EBUSY */ + return ERR_PTR(-EIO); + } + + r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + IMGU_ABI_EVENT_BUFFER_DEQUEUED(qid)); + if (r < 0) { + dev_err(css->dev, "failed to queue event\n"); + return ERR_PTR(-EIO); + } + + spin_lock(&css_pipe->qlock); + if (list_empty(&css_pipe->queue[queue].bufs)) { + spin_unlock(&css_pipe->qlock); + dev_err(css->dev, "event on empty queue\n"); + return ERR_PTR(-EIO); + } + b = list_first_entry(&css_pipe->queue[queue].bufs, + struct ipu3_css_buffer, list); + if (queue != b->queue || + daddr != css_pipe->abi_buffers + [b->queue][b->queue_pos].daddr) { + spin_unlock(&css_pipe->qlock); + dev_err(css->dev, "dequeued bad buffer 0x%x\n", daddr); + return ERR_PTR(-EIO); + } + + dev_dbg(css->dev, "buffer 0x%8x done from pipe %d\n", daddr, pipe); + b->pipe = pipe; + b->state = IPU3_CSS_BUFFER_DONE; + list_del(&b->list); + spin_unlock(&css_pipe->qlock); + break; + case IMGU_ABI_EVTTYPE_PIPELINE_DONE: + pipe = (event & IMGU_ABI_EVTTYPE_PIPE_MASK) >> + IMGU_ABI_EVTTYPE_PIPE_SHIFT; + if (pipe >= IMGU_MAX_PIPE_NUM) { + dev_err(css->dev, "Invalid pipe: %i\n", pipe); + return ERR_PTR(-EIO); + } + + css_pipe = &css->pipes[pipe]; + dev_dbg(css->dev, "event: pipeline done 0x%8x for pipe %d\n", + event, pipe); + break; + case IMGU_ABI_EVTTYPE_TIMER: + r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); + if (r < 0) + return ERR_PTR(r); + + if ((event & IMGU_ABI_EVTTYPE_EVENT_MASK) >> + IMGU_ABI_EVTTYPE_EVENT_SHIFT == IMGU_ABI_EVTTYPE_TIMER) + dev_dbg(css->dev, "event: timer\n"); + else + dev_warn(css->dev, "half of timer event missing\n"); + break; + case IMGU_ABI_EVTTYPE_FW_WARNING: + dev_warn(css->dev, "event: firmware warning 0x%x\n", event); + break; + case IMGU_ABI_EVTTYPE_FW_ASSERT: + dev_err(css->dev, + "event: firmware assert 0x%x module_id %i line_no %i\n", + event, + (event & IMGU_ABI_EVTTYPE_MODULEID_MASK) >> + IMGU_ABI_EVTTYPE_MODULEID_SHIFT, + swab16((event & IMGU_ABI_EVTTYPE_LINENO_MASK) >> + IMGU_ABI_EVTTYPE_LINENO_SHIFT)); + break; + default: + dev_warn(css->dev, "received unknown event 0x%x\n", event); + } + + return b; +} + +/* + * Get a new set of parameters from pool and initialize them based on + * the parameters params, gdc, and obgrid. Any of these may be NULL, + * in which case the previously set parameters are used. + * If parameters haven't been set previously, initialize from scratch. + * + * Return index to css->parameter_set_info which has the newly created + * parameters or negative value on error. + */ +int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, + struct ipu3_uapi_params *set_params) +{ + static const unsigned int queue_id = IMGU_ABI_QUEUE_A_ID; + struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + const int stage = 0; + const struct imgu_fw_info *bi; + int obgrid_size; + unsigned int stripes, i; + struct ipu3_uapi_flags *use = set_params ? &set_params->use : NULL; + + /* Destination buffers which are filled here */ + struct imgu_abi_parameter_set_info *param_set; + struct imgu_abi_acc_param *acc = NULL; + struct imgu_abi_gdc_warp_param *gdc = NULL; + struct ipu3_uapi_obgrid_param *obgrid = NULL; + const struct ipu3_css_map *map; + void *vmem0 = NULL; + void *dmem0 = NULL; + + enum imgu_abi_memories m; + int r = -EBUSY; + + if (!css->streaming) + return -EPROTO; + + dev_dbg(css->dev, "%s for pipe %d", __func__, pipe); + + bi = &css->fwp->binary_header[css_pipe->bindex]; + obgrid_size = ipu3_css_fw_obgrid_size(bi); + stripes = bi->info.isp.sp.iterator.num_stripes ? : 1; + + /* + * TODO(b/118782861): If userspace queues more than 4 buffers, the + * parameters from previous buffers will be overwritten. Fix the driver + * not to allow this. + */ + ipu3_css_pool_get(&css_pipe->pool.parameter_set_info); + param_set = ipu3_css_pool_last(&css_pipe->pool.parameter_set_info, + 0)->vaddr; + + /* Get a new acc only if new parameters given, or none yet */ + map = ipu3_css_pool_last(&css_pipe->pool.acc, 0); + if (set_params || !map->vaddr) { + ipu3_css_pool_get(&css_pipe->pool.acc); + map = ipu3_css_pool_last(&css_pipe->pool.acc, 0); + acc = map->vaddr; + } + + /* Get new VMEM0 only if needed, or none yet */ + m = IMGU_ABI_MEM_ISP_VMEM0; + map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + if (!map->vaddr || (set_params && (set_params->use.lin_vmem_params || + set_params->use.tnr3_vmem_params || + set_params->use.xnr3_vmem_params))) { + ipu3_css_pool_get(&css_pipe->pool.binary_params_p[m]); + map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + vmem0 = map->vaddr; + } + + /* Get new DMEM0 only if needed, or none yet */ + m = IMGU_ABI_MEM_ISP_DMEM0; + map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + if (!map->vaddr || (set_params && (set_params->use.tnr3_dmem_params || + set_params->use.xnr3_dmem_params))) { + ipu3_css_pool_get(&css_pipe->pool.binary_params_p[m]); + map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + dmem0 = map->vaddr; + } + + /* Configure acc parameter cluster */ + if (acc) { + /* get acc_old */ + map = ipu3_css_pool_last(&css_pipe->pool.acc, 1); + /* user acc */ + r = ipu3_css_cfg_acc(css, pipe, use, acc, map->vaddr, + set_params ? &set_params->acc_param : NULL); + if (r < 0) + goto fail; + } + + /* Configure late binding parameters */ + if (vmem0) { + m = IMGU_ABI_MEM_ISP_VMEM0; + map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); + r = ipu3_css_cfg_vmem0(css, pipe, use, vmem0, + map->vaddr, set_params); + if (r < 0) + goto fail; + } + + if (dmem0) { + m = IMGU_ABI_MEM_ISP_DMEM0; + map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); + r = ipu3_css_cfg_dmem0(css, pipe, use, dmem0, + map->vaddr, set_params); + if (r < 0) + goto fail; + } + + /* Get a new gdc only if a new gdc is given, or none yet */ + if (bi->info.isp.sp.enable.dvs_6axis) { + unsigned int a = IPU3_CSS_AUX_FRAME_REF; + unsigned int g = IPU3_CSS_RECT_GDC; + unsigned int e = IPU3_CSS_RECT_ENVELOPE; + + map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0); + if (!map->vaddr) { + ipu3_css_pool_get(&css_pipe->pool.gdc); + map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0); + gdc = map->vaddr; + ipu3_css_cfg_gdc_table(map->vaddr, + css_pipe->aux_frames[a].bytesperline / + css_pipe->aux_frames[a].bytesperpixel, + css_pipe->aux_frames[a].height, + css_pipe->rect[g].width, + css_pipe->rect[g].height, + css_pipe->rect[e].width + FILTER_SIZE, + css_pipe->rect[e].height + + FILTER_SIZE); + } + } + + /* Get a new obgrid only if a new obgrid is given, or none yet */ + map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0); + if (!map->vaddr || (set_params && set_params->use.obgrid_param)) { + ipu3_css_pool_get(&css_pipe->pool.obgrid); + map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0); + obgrid = map->vaddr; + + /* Configure optical black level grid (obgrid) */ + if (set_params && set_params->use.obgrid_param) + for (i = 0; i < obgrid_size / sizeof(*obgrid); i++) + obgrid[i] = set_params->obgrid_param; + else + memset(obgrid, 0, obgrid_size); + } + + /* Configure parameter set info, queued to `queue_id' */ + + memset(param_set, 0, sizeof(*param_set)); + map = ipu3_css_pool_last(&css_pipe->pool.acc, 0); + param_set->mem_map.acc_cluster_params_for_sp = map->daddr; + + map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0); + param_set->mem_map.dvs_6axis_params_y = map->daddr; + + for (i = 0; i < stripes; i++) { + map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0); + param_set->mem_map.obgrid_tbl[i] = + map->daddr + (obgrid_size / stripes) * i; + } + + for (m = 0; m < IMGU_ABI_NUM_MEMORIES; m++) { + map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + param_set->mem_map.isp_mem_param[stage][m] = map->daddr; + } + + /* Then queue the new parameter buffer */ + map = ipu3_css_pool_last(&css_pipe->pool.parameter_set_info, 0); + r = ipu3_css_queue_data(css, queue_id, pipe, map->daddr); + if (r < 0) + goto fail; + + r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + IMGU_ABI_EVENT_BUFFER_ENQUEUED(pipe, + queue_id)); + if (r < 0) + goto fail_no_put; + + /* Finally dequeue all old parameter buffers */ + + do { + u32 daddr; + + r = ipu3_css_dequeue_data(css, queue_id, &daddr); + if (r == -EBUSY) + break; + if (r) + goto fail_no_put; + r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + IMGU_ABI_EVENT_BUFFER_DEQUEUED + (queue_id)); + if (r < 0) { + dev_err(css->dev, "failed to queue parameter event\n"); + goto fail_no_put; + } + } while (1); + + return 0; + +fail: + /* + * A failure, most likely the parameter queue was full. + * Return error but continue streaming. User can try submitting new + * parameters again later. + */ + + ipu3_css_pool_put(&css_pipe->pool.parameter_set_info); + if (acc) + ipu3_css_pool_put(&css_pipe->pool.acc); + if (gdc) + ipu3_css_pool_put(&css_pipe->pool.gdc); + if (obgrid) + ipu3_css_pool_put(&css_pipe->pool.obgrid); + if (vmem0) + ipu3_css_pool_put( + &css_pipe->pool.binary_params_p + [IMGU_ABI_MEM_ISP_VMEM0]); + if (dmem0) + ipu3_css_pool_put( + &css_pipe->pool.binary_params_p + [IMGU_ABI_MEM_ISP_DMEM0]); + +fail_no_put: + return r; +} + +int ipu3_css_irq_ack(struct ipu3_css *css) +{ + static const int NUM_SWIRQS = 3; + struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[0]]; + void __iomem *const base = css->base; + u32 irq_status[IMGU_IRQCTRL_NUM]; + int i; + + u32 imgu_status = readl(base + IMGU_REG_INT_STATUS); + + writel(imgu_status, base + IMGU_REG_INT_STATUS); + for (i = 0; i < IMGU_IRQCTRL_NUM; i++) + irq_status[i] = readl(base + IMGU_REG_IRQCTRL_STATUS(i)); + + for (i = 0; i < NUM_SWIRQS; i++) { + if (irq_status[IMGU_IRQCTRL_SP0] & IMGU_IRQCTRL_IRQ_SW_PIN(i)) { + /* SP SW interrupt */ + u32 cnt = readl(base + IMGU_REG_SP_DMEM_BASE(0) + + bi->info.sp.output); + u32 val = readl(base + IMGU_REG_SP_DMEM_BASE(0) + + bi->info.sp.output + 4 + 4 * i); + + dev_dbg(css->dev, "%s: swirq %i cnt %i val 0x%x\n", + __func__, i, cnt, val); + } + } + + for (i = IMGU_IRQCTRL_NUM - 1; i >= 0; i--) + if (irq_status[i]) { + writel(irq_status[i], base + IMGU_REG_IRQCTRL_CLEAR(i)); + /* Wait for write to complete */ + readl(base + IMGU_REG_IRQCTRL_ENABLE(i)); + } + + dev_dbg(css->dev, "%s: imgu 0x%x main 0x%x sp0 0x%x sp1 0x%x\n", + __func__, imgu_status, irq_status[IMGU_IRQCTRL_MAIN], + irq_status[IMGU_IRQCTRL_SP0], irq_status[IMGU_IRQCTRL_SP1]); + + if (!imgu_status && !irq_status[IMGU_IRQCTRL_MAIN]) + return -ENOMSG; + + return 0; +} diff --git a/drivers/staging/media/ipu3/ipu3-css.h b/drivers/staging/media/ipu3/ipu3-css.h @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ + +#ifndef __IPU3_CSS_H +#define __IPU3_CSS_H + +#include <linux/videodev2.h> +#include <linux/types.h> + +#include "ipu3-abi.h" +#include "ipu3-css-pool.h" + +/* 2 stages for split isp pipeline, 1 for scaling */ +#define IMGU_NUM_SP 2 +#define IMGU_MAX_PIPELINE_NUM 20 +#define IMGU_MAX_PIPE_NUM 2 + +/* For DVS etc., format FRAME_FMT_YUV420_16 */ +#define IPU3_CSS_AUX_FRAME_REF 0 +/* For temporal noise reduction DVS etc., format FRAME_FMT_YUV_LINE */ +#define IPU3_CSS_AUX_FRAME_TNR 1 +#define IPU3_CSS_AUX_FRAME_TYPES 2 /* REF and TNR */ +#define IPU3_CSS_AUX_FRAMES 2 /* 2 for REF and 2 for TNR */ + +#define IPU3_CSS_QUEUE_IN 0 +#define IPU3_CSS_QUEUE_PARAMS 1 +#define IPU3_CSS_QUEUE_OUT 2 +#define IPU3_CSS_QUEUE_VF 3 +#define IPU3_CSS_QUEUE_STAT_3A 4 +#define IPU3_CSS_QUEUES 5 + +#define IPU3_CSS_RECT_EFFECTIVE 0 /* Effective resolution */ +#define IPU3_CSS_RECT_BDS 1 /* Resolution after BDS */ +#define IPU3_CSS_RECT_ENVELOPE 2 /* DVS envelope size */ +#define IPU3_CSS_RECT_GDC 3 /* gdc output res */ +#define IPU3_CSS_RECTS 4 /* number of rects */ + +#define IA_CSS_BINARY_MODE_PRIMARY 2 +#define IA_CSS_BINARY_MODE_VIDEO 3 +#define IPU3_CSS_DEFAULT_BINARY 3 /* default binary index */ + +/* + * The pipe id type, distinguishes the kind of pipes that + * can be run in parallel. + */ +enum ipu3_css_pipe_id { + IPU3_CSS_PIPE_ID_PREVIEW, + IPU3_CSS_PIPE_ID_COPY, + IPU3_CSS_PIPE_ID_VIDEO, + IPU3_CSS_PIPE_ID_CAPTURE, + IPU3_CSS_PIPE_ID_YUVPP, + IPU3_CSS_PIPE_ID_ACC, + IPU3_CSS_PIPE_ID_NUM +}; + +struct ipu3_css_resolution { + u32 w; + u32 h; +}; + +enum ipu3_css_buffer_state { + IPU3_CSS_BUFFER_NEW, /* Not yet queued */ + IPU3_CSS_BUFFER_QUEUED, /* Queued, waiting to be filled */ + IPU3_CSS_BUFFER_DONE, /* Finished processing, removed from queue */ + IPU3_CSS_BUFFER_FAILED, /* Was not processed, removed from queue */ +}; + +struct ipu3_css_buffer { + /* Private fields: user doesn't touch */ + dma_addr_t daddr; + unsigned int queue; + enum ipu3_css_buffer_state state; + struct list_head list; + u8 queue_pos; + unsigned int pipe; +}; + +struct ipu3_css_format { + u32 pixelformat; + enum v4l2_colorspace colorspace; + enum imgu_abi_frame_format frame_format; + enum imgu_abi_bayer_order bayer_order; + enum imgu_abi_osys_format osys_format; + enum imgu_abi_osys_tiling osys_tiling; + u32 bytesperpixel_num; /* Bytes per pixel in first plane * 50 */ + u8 bit_depth; /* Effective bits per pixel */ + u8 chroma_decim; /* Chroma plane decimation, 0=no chroma plane */ + u8 width_align; /* Alignment requirement for width_pad */ + u8 flags; +}; + +struct ipu3_css_queue { + union { + struct v4l2_pix_format_mplane mpix; + struct v4l2_meta_format meta; + + } fmt; + const struct ipu3_css_format *css_fmt; + unsigned int width_pad; + struct list_head bufs; +}; + +struct ipu3_css_pipe { + enum ipu3_css_pipe_id pipe_id; + unsigned int bindex; + + struct ipu3_css_queue queue[IPU3_CSS_QUEUES]; + struct v4l2_rect rect[IPU3_CSS_RECTS]; + + bool vf_output_en; + /* Protect access to queue[IPU3_CSS_QUEUES] */ + spinlock_t qlock; + + /* Data structures shared with IMGU and driver, always allocated */ + struct ipu3_css_map sp_ddr_ptrs; + struct ipu3_css_map xmem_sp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] + [IMGU_ABI_MAX_STAGES]; + struct ipu3_css_map xmem_isp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] + [IMGU_ABI_MAX_STAGES]; + + /* + * Data structures shared with IMGU and driver, binary specific. + * PARAM_CLASS_CONFIG and PARAM_CLASS_STATE parameters. + */ + struct ipu3_css_map binary_params_cs[IMGU_ABI_PARAM_CLASS_NUM - 1] + [IMGU_ABI_NUM_MEMORIES]; + + struct { + struct ipu3_css_map mem[IPU3_CSS_AUX_FRAMES]; + unsigned int width; + unsigned int height; + unsigned int bytesperline; + unsigned int bytesperpixel; + } aux_frames[IPU3_CSS_AUX_FRAME_TYPES]; + + struct { + struct ipu3_css_pool parameter_set_info; + struct ipu3_css_pool acc; + struct ipu3_css_pool gdc; + struct ipu3_css_pool obgrid; + /* PARAM_CLASS_PARAM parameters for binding while streaming */ + struct ipu3_css_pool binary_params_p[IMGU_ABI_NUM_MEMORIES]; + } pool; + + struct ipu3_css_map abi_buffers[IPU3_CSS_QUEUES] + [IMGU_ABI_HOST2SP_BUFQ_SIZE]; +}; + +/* IPU3 Camera Sub System structure */ +struct ipu3_css { + struct device *dev; + void __iomem *base; + const struct firmware *fw; + struct imgu_fw_header *fwp; + int iomem_length; + int fw_bl, fw_sp[IMGU_NUM_SP]; /* Indices of bl and SP binaries */ + struct ipu3_css_map *binary; /* fw binaries mapped to device */ + bool streaming; /* true when streaming is enabled */ + + struct ipu3_css_pipe pipes[IMGU_MAX_PIPE_NUM]; + struct ipu3_css_map xmem_sp_group_ptrs; + + /* enabled pipe(s) */ + DECLARE_BITMAP(enabled_pipes, IMGU_MAX_PIPE_NUM); +}; + +/******************* css v4l *******************/ +int ipu3_css_init(struct device *dev, struct ipu3_css *css, + void __iomem *base, int length); +void ipu3_css_cleanup(struct ipu3_css *css); +int ipu3_css_fmt_try(struct ipu3_css *css, + struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], + struct v4l2_rect *rects[IPU3_CSS_RECTS], + unsigned int pipe); +int ipu3_css_fmt_set(struct ipu3_css *css, + struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], + struct v4l2_rect *rects[IPU3_CSS_RECTS], + unsigned int pipe); +int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt); +int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, + struct ipu3_css_buffer *b); +struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css); +int ipu3_css_start_streaming(struct ipu3_css *css); +void ipu3_css_stop_streaming(struct ipu3_css *css); +bool ipu3_css_queue_empty(struct ipu3_css *css); +bool ipu3_css_is_streaming(struct ipu3_css *css); +bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe); + +/******************* css hw *******************/ +int ipu3_css_set_powerup(struct device *dev, void __iomem *base); +void ipu3_css_set_powerdown(struct device *dev, void __iomem *base); +int ipu3_css_irq_ack(struct ipu3_css *css); + +/******************* set parameters ************/ +int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, + struct ipu3_uapi_params *set_params); + +/******************* auxiliary helpers *******************/ +static inline enum ipu3_css_buffer_state +ipu3_css_buf_state(struct ipu3_css_buffer *b) +{ + return b->state; +} + +/* Initialize given buffer. May be called several times. */ +static inline void ipu3_css_buf_init(struct ipu3_css_buffer *b, + unsigned int queue, dma_addr_t daddr) +{ + b->state = IPU3_CSS_BUFFER_NEW; + b->queue = queue; + b->daddr = daddr; +} +#endif diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.c b/drivers/staging/media/ipu3/ipu3-dmamap.c @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Intel Corporation + * Copyright 2018 Google LLC. + * + * Author: Tomasz Figa <tfiga@chromium.org> + * Author: Yong Zhi <yong.zhi@intel.com> + */ + +#include <linux/vmalloc.h> + +#include "ipu3.h" +#include "ipu3-css-pool.h" +#include "ipu3-mmu.h" + +/* + * Free a buffer allocated by ipu3_dmamap_alloc_buffer() + */ +static void ipu3_dmamap_free_buffer(struct page **pages, + size_t size) +{ + int count = size >> PAGE_SHIFT; + + while (count--) + __free_page(pages[count]); + kvfree(pages); +} + +/* + * Based on the implementation of __iommu_dma_alloc_pages() + * defined in drivers/iommu/dma-iommu.c + */ +static struct page **ipu3_dmamap_alloc_buffer(size_t size, + unsigned long order_mask, + gfp_t gfp) +{ + struct page **pages; + unsigned int i = 0, count = size >> PAGE_SHIFT; + const gfp_t high_order_gfp = __GFP_NOWARN | __GFP_NORETRY; + + /* Allocate mem for array of page ptrs */ + pages = kvmalloc_array(count, sizeof(*pages), GFP_KERNEL); + + if (!pages) + return NULL; + + order_mask &= (2U << MAX_ORDER) - 1; + if (!order_mask) + return NULL; + + gfp |= __GFP_HIGHMEM | __GFP_ZERO; + + while (count) { + struct page *page = NULL; + unsigned int order_size; + + for (order_mask &= (2U << __fls(count)) - 1; + order_mask; order_mask &= ~order_size) { + unsigned int order = __fls(order_mask); + + order_size = 1U << order; + page = alloc_pages((order_mask - order_size) ? + gfp | high_order_gfp : gfp, order); + if (!page) + continue; + if (!order) + break; + if (!PageCompound(page)) { + split_page(page, order); + break; + } + + __free_pages(page, order); + } + if (!page) { + ipu3_dmamap_free_buffer(pages, i << PAGE_SHIFT); + return NULL; + } + count -= order_size; + while (order_size--) + pages[i++] = page++; + } + + return pages; +} + +/** + * ipu3_dmamap_alloc - allocate and map a buffer into KVA + * @imgu: struct device pointer + * @map: struct to store mapping variables + * @len: size required + * + * Returns: + * KVA on success + * %NULL on failure + */ +void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, + size_t len) +{ + unsigned long shift = iova_shift(&imgu->iova_domain); + unsigned int alloc_sizes = imgu->mmu->pgsize_bitmap; + struct device *dev = &imgu->pci_dev->dev; + size_t size = PAGE_ALIGN(len); + struct page **pages; + dma_addr_t iovaddr; + struct iova *iova; + int i, rval; + + dev_dbg(dev, "%s: allocating %zu\n", __func__, size); + + iova = alloc_iova(&imgu->iova_domain, size >> shift, + imgu->mmu->aperture_end >> shift, 0); + if (!iova) + return NULL; + + pages = ipu3_dmamap_alloc_buffer(size, alloc_sizes >> PAGE_SHIFT, + GFP_KERNEL); + if (!pages) + goto out_free_iova; + + /* Call IOMMU driver to setup pgt */ + iovaddr = iova_dma_addr(&imgu->iova_domain, iova); + for (i = 0; i < size / PAGE_SIZE; ++i) { + rval = ipu3_mmu_map(imgu->mmu, iovaddr, + page_to_phys(pages[i]), PAGE_SIZE); + if (rval) + goto out_unmap; + + iovaddr += PAGE_SIZE; + } + + /* Now grab a virtual region */ + map->vma = __get_vm_area(size, VM_USERMAP, VMALLOC_START, VMALLOC_END); + if (!map->vma) + goto out_unmap; + + map->vma->pages = pages; + /* And map it in KVA */ + if (map_vm_area(map->vma, PAGE_KERNEL, pages)) + goto out_vunmap; + + map->size = size; + map->daddr = iova_dma_addr(&imgu->iova_domain, iova); + map->vaddr = map->vma->addr; + + dev_dbg(dev, "%s: allocated %zu @ IOVA %pad @ VA %p\n", __func__, + size, &map->daddr, map->vma->addr); + + return map->vma->addr; + +out_vunmap: + vunmap(map->vma->addr); + +out_unmap: + ipu3_dmamap_free_buffer(pages, size); + ipu3_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), + i * PAGE_SIZE); + map->vma = NULL; + +out_free_iova: + __free_iova(&imgu->iova_domain, iova); + + return NULL; +} + +void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map) +{ + struct iova *iova; + + iova = find_iova(&imgu->iova_domain, + iova_pfn(&imgu->iova_domain, map->daddr)); + if (WARN_ON(!iova)) + return; + + ipu3_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), + iova_size(iova) << iova_shift(&imgu->iova_domain)); + + __free_iova(&imgu->iova_domain, iova); +} + +/* + * Counterpart of ipu3_dmamap_alloc + */ +void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map) +{ + struct vm_struct *area = map->vma; + + dev_dbg(&imgu->pci_dev->dev, "%s: freeing %zu @ IOVA %pad @ VA %p\n", + __func__, map->size, &map->daddr, map->vaddr); + + if (!map->vaddr) + return; + + ipu3_dmamap_unmap(imgu, map); + + if (WARN_ON(!area) || WARN_ON(!area->pages)) + return; + + ipu3_dmamap_free_buffer(area->pages, map->size); + vunmap(map->vaddr); + map->vaddr = NULL; +} + +int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, + int nents, struct ipu3_css_map *map) +{ + unsigned long shift = iova_shift(&imgu->iova_domain); + struct scatterlist *sg; + struct iova *iova; + size_t size = 0; + int i; + + for_each_sg(sglist, sg, nents, i) { + if (sg->offset) + return -EINVAL; + + if (i != nents - 1 && !PAGE_ALIGNED(sg->length)) + return -EINVAL; + + size += sg->length; + } + + size = iova_align(&imgu->iova_domain, size); + dev_dbg(&imgu->pci_dev->dev, "dmamap: mapping sg %d entries, %zu pages\n", + nents, size >> shift); + + iova = alloc_iova(&imgu->iova_domain, size >> shift, + imgu->mmu->aperture_end >> shift, 0); + if (!iova) + return -ENOMEM; + + dev_dbg(&imgu->pci_dev->dev, "dmamap: iova low pfn %lu, high pfn %lu\n", + iova->pfn_lo, iova->pfn_hi); + + if (ipu3_mmu_map_sg(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), + sglist, nents) < size) + goto out_fail; + + memset(map, 0, sizeof(*map)); + map->daddr = iova_dma_addr(&imgu->iova_domain, iova); + map->size = size; + + return 0; + +out_fail: + __free_iova(&imgu->iova_domain, iova); + + return -EFAULT; +} + +int ipu3_dmamap_init(struct imgu_device *imgu) +{ + unsigned long order, base_pfn; + int ret = iova_cache_get(); + + if (ret) + return ret; + + order = __ffs(imgu->mmu->pgsize_bitmap); + base_pfn = max_t(unsigned long, 1, imgu->mmu->aperture_start >> order); + init_iova_domain(&imgu->iova_domain, 1UL << order, base_pfn); + + return 0; +} + +void ipu3_dmamap_exit(struct imgu_device *imgu) +{ + put_iova_domain(&imgu->iova_domain); + iova_cache_put(); +} diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.h b/drivers/staging/media/ipu3/ipu3-dmamap.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ +/* Copyright 2018 Google LLC. */ + +#ifndef __IPU3_DMAMAP_H +#define __IPU3_DMAMAP_H + +struct imgu_device; +struct scatterlist; + +void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, + size_t len); +void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map); + +int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, + int nents, struct ipu3_css_map *map); +void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map); + +int ipu3_dmamap_init(struct imgu_device *imgu); +void ipu3_dmamap_exit(struct imgu_device *imgu); + +#endif diff --git a/drivers/staging/media/ipu3/ipu3-mmu.c b/drivers/staging/media/ipu3/ipu3-mmu.c @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Intel Corporation. + * Copyright 2018 Google LLC. + * + * Author: Tuukka Toivonen <tuukka.toivonen@intel.com> + * Author: Sakari Ailus <sakari.ailus@linux.intel.com> + * Author: Samu Onkalo <samu.onkalo@intel.com> + * Author: Tomasz Figa <tfiga@chromium.org> + * + */ + +#include <linux/dma-mapping.h> +#include <linux/iopoll.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#include <asm/set_memory.h> + +#include "ipu3-mmu.h" + +#define IPU3_PAGE_SHIFT 12 +#define IPU3_PAGE_SIZE (1UL << IPU3_PAGE_SHIFT) + +#define IPU3_PT_BITS 10 +#define IPU3_PT_PTES (1UL << IPU3_PT_BITS) +#define IPU3_PT_SIZE (IPU3_PT_PTES << 2) +#define IPU3_PT_ORDER (IPU3_PT_SIZE >> PAGE_SHIFT) + +#define IPU3_ADDR2PTE(addr) ((addr) >> IPU3_PAGE_SHIFT) +#define IPU3_PTE2ADDR(pte) ((phys_addr_t)(pte) << IPU3_PAGE_SHIFT) + +#define IPU3_L2PT_SHIFT IPU3_PT_BITS +#define IPU3_L2PT_MASK ((1UL << IPU3_L2PT_SHIFT) - 1) + +#define IPU3_L1PT_SHIFT IPU3_PT_BITS +#define IPU3_L1PT_MASK ((1UL << IPU3_L1PT_SHIFT) - 1) + +#define IPU3_MMU_ADDRESS_BITS (IPU3_PAGE_SHIFT + \ + IPU3_L2PT_SHIFT + \ + IPU3_L1PT_SHIFT) + +#define IMGU_REG_BASE 0x4000 +#define REG_TLB_INVALIDATE (IMGU_REG_BASE + 0x300) +#define TLB_INVALIDATE 1 +#define REG_L1_PHYS (IMGU_REG_BASE + 0x304) /* 27-bit pfn */ +#define REG_GP_HALT (IMGU_REG_BASE + 0x5dc) +#define REG_GP_HALTED (IMGU_REG_BASE + 0x5e0) + +struct ipu3_mmu { + struct device *dev; + void __iomem *base; + /* protect access to l2pts, l1pt */ + spinlock_t lock; + + void *dummy_page; + u32 dummy_page_pteval; + + u32 *dummy_l2pt; + u32 dummy_l2pt_pteval; + + u32 **l2pts; + u32 *l1pt; + + struct ipu3_mmu_info geometry; +}; + +static inline struct ipu3_mmu *to_ipu3_mmu(struct ipu3_mmu_info *info) +{ + return container_of(info, struct ipu3_mmu, geometry); +} + +/** + * ipu3_mmu_tlb_invalidate - invalidate translation look-aside buffer + * @mmu: MMU to perform the invalidate operation on + * + * This function invalidates the whole TLB. Must be called when the hardware + * is powered on. + */ +static void ipu3_mmu_tlb_invalidate(struct ipu3_mmu *mmu) +{ + writel(TLB_INVALIDATE, mmu->base + REG_TLB_INVALIDATE); +} + +static void call_if_ipu3_is_powered(struct ipu3_mmu *mmu, + void (*func)(struct ipu3_mmu *mmu)) +{ + if (!pm_runtime_get_if_in_use(mmu->dev)) + return; + + func(mmu); + pm_runtime_put(mmu->dev); +} + +/** + * ipu3_mmu_set_halt - set CIO gate halt bit + * @mmu: MMU to set the CIO gate bit in. + * @halt: Desired state of the gate bit. + * + * This function sets the CIO gate bit that controls whether external memory + * accesses are allowed. Must be called when the hardware is powered on. + */ +static void ipu3_mmu_set_halt(struct ipu3_mmu *mmu, bool halt) +{ + int ret; + u32 val; + + writel(halt, mmu->base + REG_GP_HALT); + ret = readl_poll_timeout(mmu->base + REG_GP_HALTED, + val, (val & 1) == halt, 1000, 100000); + + if (ret) + dev_err(mmu->dev, "failed to %s CIO gate halt\n", + halt ? "set" : "clear"); +} + +/** + * ipu3_mmu_alloc_page_table - allocate a pre-filled page table + * @pteval: Value to initialize f