media fixes for v4.20-rc8

-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJcG4weAAoJEAhfPr2O5OEVMu8P/j5a3+qringy/94NgmpHBQop
 aOACNWTRE746CG8T0yKgHV13iiLTHhlVxrRfhADKomWFit0DOiE2G1QXDsw6jptl
 gfMKMz3tE7MNOt0E+1tFzkezduJ7TIvFw9rorJ9MuACr9RGc+jMc+8muu1Uhu+fS
 JArKYrn1xlBEP9k2pcgbxFIjzdRr1jEJ9MCdyxybaU4QWyH4rUq0u+i6RJ2JlLcV
 urXGeDaNwgq7uTcOdUIwcbB0lKCi3hhkuPyl850IaQHWkR1cIzRoMWk5ilW9YYR3
 8zDix9hORmPAdRIwhj0kJElTLZlTzF/u4+1fARmwKJlOYnAfRzSyFhC5P4huvmeA
 6KXP1jg3OcyU5mB/Ys0YpZDhUtlhpRmMCMKtZW0x4BPpYTfRkAz6almUM6kIyaKp
 Dv1cp4UJ8SGpqVxEFBSr6qPsoO0by8jdkgRsupaCn6tAtkG7VXDGR8yhzZB8Leai
 pdCXP/7Z5t3kWI0XceVlpgOjkC7qLbEQKLDk0dqg0XqFLEuXm7sifLGamGhqubVy
 BmRPzH9oVISutmM/8DL8MHBkAv5IESF1lwhOzM75LTtA7aDaBuHS6zRhh45VtdJx
 r8/+R9KEvKdWTFgnRpvaERJk64I0hU44gmPLOtt/kkCfaTOonbhIsZ55uZxKjkqg
 rHPVHJORalrJGy9Hnhpl
 =uZDa
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2018-12-25 13:11:30 -08:00
commit 996680d461
40 changed files with 24661 additions and 19 deletions

View File

@ -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`.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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);

View File

@ -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");

View File

@ -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;
}

View File

@ -39,4 +39,6 @@ source "drivers/staging/media/tegra-vde/Kconfig"
source "drivers/staging/media/zoran/Kconfig"
source "drivers/staging/media/ipu3/Kconfig"
endif

View File

@ -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/

View File

@ -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.

View File

@ -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

View File

@ -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?

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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 for page table entries with.
*
* Return: Pointer to allocated page table or NULL on failure.
*/
static u32 *ipu3_mmu_alloc_page_table(u32 pteval)
{
u32 *pt;
int pte;
pt = (u32 *)__get_free_page(GFP_KERNEL);
if (!pt)
return NULL;
for (pte = 0; pte < IPU3_PT_PTES; pte++)
pt[pte] = pteval;
set_memory_uc((unsigned long int)pt, IPU3_PT_ORDER);
return pt;
}
/**
* ipu3_mmu_free_page_table - free page table
* @pt: Page table to free.
*/
static void ipu3_mmu_free_page_table(u32 *pt)
{
set_memory_wb((unsigned long int)pt, IPU3_PT_ORDER);
free_page((unsigned long)pt);
}
/**
* address_to_pte_idx - split IOVA into L1 and L2 page table indices
* @iova: IOVA to split.
* @l1pt_idx: Output for the L1 page table index.
* @l2pt_idx: Output for the L2 page index.
*/
static inline void address_to_pte_idx(unsigned long iova, u32 *l1pt_idx,
u32 *l2pt_idx)
{
iova >>= IPU3_PAGE_SHIFT;
if (l2pt_idx)
*l2pt_idx = iova & IPU3_L2PT_MASK;
iova >>= IPU3_L2PT_SHIFT;
if (l1pt_idx)
*l1pt_idx = iova & IPU3_L1PT_MASK;
}
static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx)
{
unsigned long flags;
u32 *l2pt, *new_l2pt;
u32 pteval;
spin_lock_irqsave(&mmu->lock, flags);
l2pt = mmu->l2pts[l1pt_idx];
if (l2pt)
goto done;
spin_unlock_irqrestore(&mmu->lock, flags);
new_l2pt = ipu3_mmu_alloc_page_table(mmu->dummy_page_pteval);
if (!new_l2pt)
return NULL;
spin_lock_irqsave(&mmu->lock, flags);
dev_dbg(mmu->dev, "allocated page table %p for l1pt_idx %u\n",
new_l2pt, l1pt_idx);
l2pt = mmu->l2pts[l1pt_idx];
if (l2pt) {
ipu3_mmu_free_page_table(new_l2pt);
goto done;
}
l2pt = new_l2pt;
mmu->l2pts[l1pt_idx] = new_l2pt;
pteval = IPU3_ADDR2PTE(virt_to_phys(new_l2pt));
mmu->l1pt[l1pt_idx] = pteval;
done:
spin_unlock_irqrestore(&mmu->lock, flags);
return l2pt;
}
static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova,
phys_addr_t paddr)
{
u32 l1pt_idx, l2pt_idx;
unsigned long flags;
u32 *l2pt;
if (!mmu)
return -ENODEV;
address_to_pte_idx(iova, &l1pt_idx, &l2pt_idx);
l2pt = ipu3_mmu_get_l2pt(mmu, l1pt_idx);
if (!l2pt)
return -ENOMEM;
spin_lock_irqsave(&mmu->lock, flags);
if (l2pt[l2pt_idx] != mmu->dummy_page_pteval) {
spin_unlock_irqrestore(&mmu->lock, flags);
return -EBUSY;
}
l2pt[l2pt_idx] = IPU3_ADDR2PTE(paddr);
spin_unlock_irqrestore(&mmu->lock, flags);
return 0;
}
/**
* The following four functions are implemented based on iommu.c
* drivers/iommu/iommu.c/iommu_pgsize().
*/
static size_t ipu3_mmu_pgsize(unsigned long pgsize_bitmap,
unsigned long addr_merge, size_t size)
{
unsigned int pgsize_idx;
size_t pgsize;
/* Max page size that still fits into 'size' */
pgsize_idx = __fls(size);
/* need to consider alignment requirements ? */
if (likely(addr_merge)) {
/* Max page size allowed by address */
unsigned int align_pgsize_idx = __ffs(addr_merge);
pgsize_idx = min(pgsize_idx, align_pgsize_idx);
}
/* build a mask of acceptable page sizes */
pgsize = (1UL << (pgsize_idx + 1)) - 1;
/* throw away page sizes not supported by the hardware */
pgsize &= pgsize_bitmap;
/* make sure we're still sane */
WARN_ON(!pgsize);
/* pick the biggest page */
pgsize_idx = __fls(pgsize);
pgsize = 1UL << pgsize_idx;
return pgsize;
}
/* drivers/iommu/iommu.c/iommu_map() */
int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova,
phys_addr_t paddr, size_t size)
{
struct ipu3_mmu *mmu = to_ipu3_mmu(info);
unsigned int min_pagesz;
int ret = 0;
/* find out the minimum page size supported */
min_pagesz = 1 << __ffs(mmu->geometry.pgsize_bitmap);
/*
* both the virtual address and the physical one, as well as
* the size of the mapping, must be aligned (at least) to the
* size of the smallest page supported by the hardware
*/
if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) {
dev_err(mmu->dev, "unaligned: iova 0x%lx pa %pa size 0x%zx min_pagesz 0x%x\n",
iova, &paddr, size, min_pagesz);
return -EINVAL;
}
dev_dbg(mmu->dev, "map: iova 0x%lx pa %pa size 0x%zx\n",
iova, &paddr, size);
while (size) {
size_t pgsize = ipu3_mmu_pgsize(mmu->geometry.pgsize_bitmap,
iova | paddr, size);
dev_dbg(mmu->dev, "mapping: iova 0x%lx pa %pa pgsize 0x%zx\n",
iova, &paddr, pgsize);
ret = __ipu3_mmu_map(mmu, iova, paddr);
if (ret)
break;
iova += pgsize;
paddr += pgsize;
size -= pgsize;
}
call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate);
return ret;
}
/* drivers/iommu/iommu.c/default_iommu_map_sg() */
size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova,
struct scatterlist *sg, unsigned int nents)
{
struct ipu3_mmu *mmu = to_ipu3_mmu(info);
struct scatterlist *s;
size_t s_length, mapped = 0;
unsigned int i, min_pagesz;
int ret;
min_pagesz = 1 << __ffs(mmu->geometry.pgsize_bitmap);
for_each_sg(sg, s, nents, i) {
phys_addr_t phys = page_to_phys(sg_page(s)) + s->offset;
s_length = s->length;
if (!IS_ALIGNED(s->offset, min_pagesz))
goto out_err;
/* must be min_pagesz aligned to be mapped singlely */
if (i == nents - 1 && !IS_ALIGNED(s->length, min_pagesz))
s_length = PAGE_ALIGN(s->length);
ret = ipu3_mmu_map(info, iova + mapped, phys, s_length);
if (ret)
goto out_err;
mapped += s_length;
}
call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate);
return mapped;
out_err:
/* undo mappings already done */
ipu3_mmu_unmap(info, iova, mapped);
return 0;
}
static size_t __ipu3_mmu_unmap(struct ipu3_mmu *mmu,
unsigned long iova, size_t size)
{
u32 l1pt_idx, l2pt_idx;
unsigned long flags;
size_t unmap = size;
u32 *l2pt;
if (!mmu)
return 0;
address_to_pte_idx(iova, &l1pt_idx, &l2pt_idx);
spin_lock_irqsave(&mmu->lock, flags);
l2pt = mmu->l2pts[l1pt_idx];
if (!l2pt) {
spin_unlock_irqrestore(&mmu->lock, flags);
return 0;
}
if (l2pt[l2pt_idx] == mmu->dummy_page_pteval)
unmap = 0;
l2pt[l2pt_idx] = mmu->dummy_page_pteval;
spin_unlock_irqrestore(&mmu->lock, flags);
return unmap;
}
/* drivers/iommu/iommu.c/iommu_unmap() */
size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova,
size_t size)
{
struct ipu3_mmu *mmu = to_ipu3_mmu(info);
size_t unmapped_page, unmapped = 0;
unsigned int min_pagesz;
/* find out the minimum page size supported */
min_pagesz = 1 << __ffs(mmu->geometry.pgsize_bitmap);
/*
* The virtual address, as well as the size of the mapping, must be
* aligned (at least) to the size of the smallest page supported
* by the hardware
*/
if (!IS_ALIGNED(iova | size, min_pagesz)) {
dev_err(mmu->dev, "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n",
iova, size, min_pagesz);
return -EINVAL;
}
dev_dbg(mmu->dev, "unmap this: iova 0x%lx size 0x%zx\n", iova, size);
/*
* Keep iterating until we either unmap 'size' bytes (or more)
* or we hit an area that isn't mapped.
*/
while (unmapped < size) {
size_t pgsize = ipu3_mmu_pgsize(mmu->geometry.pgsize_bitmap,
iova, size - unmapped);
unmapped_page = __ipu3_mmu_unmap(mmu, iova, pgsize);
if (!unmapped_page)
break;
dev_dbg(mmu->dev, "unmapped: iova 0x%lx size 0x%zx\n",
iova, unmapped_page);
iova += unmapped_page;
unmapped += unmapped_page;
}
call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate);
return unmapped;
}
/**
* ipu3_mmu_init() - initialize IPU3 MMU block
* @base: IOMEM base of hardware registers.
*
* Return: Pointer to IPU3 MMU private data pointer or ERR_PTR() on error.
*/
struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base)
{
struct ipu3_mmu *mmu;
u32 pteval;
mmu = kzalloc(sizeof(*mmu), GFP_KERNEL);
if (!mmu)
return ERR_PTR(-ENOMEM);
mmu->dev = parent;
mmu->base = base;
spin_lock_init(&mmu->lock);
/* Disallow external memory access when having no valid page tables. */
ipu3_mmu_set_halt(mmu, true);
/*
* The MMU does not have a "valid" bit, so we have to use a dummy
* page for invalid entries.
*/
mmu->dummy_page = (void *)__get_free_page(GFP_KERNEL);
if (!mmu->dummy_page)
goto fail_group;
pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->dummy_page));
mmu->dummy_page_pteval = pteval;
/*
* Allocate a dummy L2 page table with all entries pointing to
* the dummy page.
*/
mmu->dummy_l2pt = ipu3_mmu_alloc_page_table(pteval);
if (!mmu->dummy_l2pt)
goto fail_dummy_page;
pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->dummy_l2pt));
mmu->dummy_l2pt_pteval = pteval;
/*
* Allocate the array of L2PT CPU pointers, initialized to zero,
* which means the dummy L2PT allocated above.
*/
mmu->l2pts = vzalloc(IPU3_PT_PTES * sizeof(*mmu->l2pts));
if (!mmu->l2pts)
goto fail_l2pt;
/* Allocate the L1 page table. */
mmu->l1pt = ipu3_mmu_alloc_page_table(mmu->dummy_l2pt_pteval);
if (!mmu->l1pt)
goto fail_l2pts;
pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->l1pt));
writel(pteval, mmu->base + REG_L1_PHYS);
ipu3_mmu_tlb_invalidate(mmu);
ipu3_mmu_set_halt(mmu, false);
mmu->geometry.aperture_start = 0;
mmu->geometry.aperture_end = DMA_BIT_MASK(IPU3_MMU_ADDRESS_BITS);
mmu->geometry.pgsize_bitmap = IPU3_PAGE_SIZE;
return &mmu->geometry;
fail_l2pts:
vfree(mmu->l2pts);
fail_l2pt:
ipu3_mmu_free_page_table(mmu->dummy_l2pt);
fail_dummy_page:
free_page((unsigned long)mmu->dummy_page);
fail_group:
kfree(mmu);
return ERR_PTR(-ENOMEM);
}
/**
* ipu3_mmu_exit() - clean up IPU3 MMU block
* @mmu: IPU3 MMU private data
*/
void ipu3_mmu_exit(struct ipu3_mmu_info *info)
{
struct ipu3_mmu *mmu = to_ipu3_mmu(info);
/* We are going to free our page tables, no more memory access. */
ipu3_mmu_set_halt(mmu, true);
ipu3_mmu_tlb_invalidate(mmu);
ipu3_mmu_free_page_table(mmu->l1pt);
vfree(mmu->l2pts);
ipu3_mmu_free_page_table(mmu->dummy_l2pt);
free_page((unsigned long)mmu->dummy_page);
kfree(mmu);
}
void ipu3_mmu_suspend(struct ipu3_mmu_info *info)
{
struct ipu3_mmu *mmu = to_ipu3_mmu(info);
ipu3_mmu_set_halt(mmu, true);
}
void ipu3_mmu_resume(struct ipu3_mmu_info *info)
{
struct ipu3_mmu *mmu = to_ipu3_mmu(info);
u32 pteval;
ipu3_mmu_set_halt(mmu, true);
pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->l1pt));
writel(pteval, mmu->base + REG_L1_PHYS);
ipu3_mmu_tlb_invalidate(mmu);
ipu3_mmu_set_halt(mmu, false);
}

View File

@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018 Intel Corporation */
/* Copyright 2018 Google LLC. */
#ifndef __IPU3_MMU_H
#define __IPU3_MMU_H
/**
* struct ipu3_mmu_info - Describes mmu geometry
*
* @aperture_start: First address that can be mapped
* @aperture_end: Last address that can be mapped
* @pgsize_bitmap: Bitmap of page sizes in use
*/
struct ipu3_mmu_info {
dma_addr_t aperture_start;
dma_addr_t aperture_end;
unsigned long pgsize_bitmap;
};
struct device;
struct scatterlist;
struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base);
void ipu3_mmu_exit(struct ipu3_mmu_info *info);
void ipu3_mmu_suspend(struct ipu3_mmu_info *info);
void ipu3_mmu_resume(struct ipu3_mmu_info *info);
int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova,
phys_addr_t paddr, size_t size);
size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova,
size_t size);
size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova,
struct scatterlist *sg, unsigned int nents);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,66 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018 Intel Corporation */
#ifndef __IPU3_TABLES_H
#define __IPU3_TABLES_H
#include "ipu3-abi.h"
#define IMGU_BDS_GRANULARITY 32 /* Downscaling granularity */
#define IMGU_BDS_MIN_SF_INV IMGU_BDS_GRANULARITY
#define IMGU_BDS_CONFIG_LEN 97
#define IMGU_SCALER_DOWNSCALE_4TAPS_LEN 128
#define IMGU_SCALER_DOWNSCALE_2TAPS_LEN 64
#define IMGU_SCALER_FP ((u32)1 << 31) /* 1.0 in fixed point */
#define IMGU_XNR3_VMEM_LUT_LEN 16
#define IMGU_GDC_LUT_UNIT 4
#define IMGU_GDC_LUT_LEN 256
struct ipu3_css_bds_config {
struct imgu_abi_bds_phase_arr hor_phase_arr;
struct imgu_abi_bds_phase_arr ver_phase_arr;
struct imgu_abi_bds_ptrn_arr ptrn_arr;
u16 sample_patrn_length;
u8 hor_ds_en;
u8 ver_ds_en;
};
struct ipu3_css_xnr3_vmem_defaults {
s16 x[IMGU_XNR3_VMEM_LUT_LEN];
s16 a[IMGU_XNR3_VMEM_LUT_LEN];
s16 b[IMGU_XNR3_VMEM_LUT_LEN];
s16 c[IMGU_XNR3_VMEM_LUT_LEN];
};
extern const struct ipu3_css_bds_config
ipu3_css_bds_configs[IMGU_BDS_CONFIG_LEN];
extern const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN];
extern const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN];
extern const s16 ipu3_css_gdc_lut[IMGU_GDC_LUT_UNIT][IMGU_GDC_LUT_LEN];
extern const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults;
extern const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults;
extern const struct ipu3_uapi_dm_config ipu3_css_dm_defaults;
extern const struct ipu3_uapi_ccm_mat_config ipu3_css_ccm_defaults;
extern const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut;
extern const struct ipu3_uapi_csc_mat_config ipu3_css_csc_defaults;
extern const struct ipu3_uapi_cds_params ipu3_css_cds_defaults;
extern const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults;
extern const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults;
extern const struct ipu3_uapi_yuvp1_yds_config ipu3_css_yds_defaults;
extern const struct ipu3_uapi_yuvp1_chnr_config ipu3_css_chnr_defaults;
extern const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults;
extern const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config
ipu3_css_tcc_gain_pcwl_lut;
extern const struct ipu3_uapi_yuvp2_tcc_r_sqr_lut_static_config
ipu3_css_tcc_r_sqr_lut;
extern const struct imgu_abi_anr_config ipu3_css_anr_defaults;
extern const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults;
extern const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults;
extern const struct ipu3_uapi_ae_ccm ipu3_css_ae_ccm_defaults;
extern const struct ipu3_uapi_af_config_s ipu3_css_af_defaults;
extern const struct ipu3_uapi_awb_config_s ipu3_css_awb_defaults;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,830 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017 - 2018 Intel Corporation
* Copyright 2017 Google LLC
*
* Based on Intel IPU4 driver.
*
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include "ipu3.h"
#include "ipu3-dmamap.h"
#include "ipu3-mmu.h"
#define IMGU_PCI_ID 0x1919
#define IMGU_PCI_BAR 0
#define IMGU_DMA_MASK DMA_BIT_MASK(39)
#define IMGU_MAX_QUEUE_DEPTH (2 + 2)
/*
* pre-allocated buffer size for IMGU dummy buffers. Those
* values should be tuned to big enough to avoid buffer
* re-allocation when streaming to lower streaming latency.
*/
#define CSS_QUEUE_IN_BUF_SIZE 0
#define CSS_QUEUE_PARAMS_BUF_SIZE 0
#define CSS_QUEUE_OUT_BUF_SIZE (4160 * 3120 * 12 / 8)
#define CSS_QUEUE_VF_BUF_SIZE (1920 * 1080 * 12 / 8)
#define CSS_QUEUE_STAT_3A_BUF_SIZE sizeof(struct ipu3_uapi_stats_3a)
static const size_t css_queue_buf_size_map[IPU3_CSS_QUEUES] = {
[IPU3_CSS_QUEUE_IN] = CSS_QUEUE_IN_BUF_SIZE,
[IPU3_CSS_QUEUE_PARAMS] = CSS_QUEUE_PARAMS_BUF_SIZE,
[IPU3_CSS_QUEUE_OUT] = CSS_QUEUE_OUT_BUF_SIZE,
[IPU3_CSS_QUEUE_VF] = CSS_QUEUE_VF_BUF_SIZE,
[IPU3_CSS_QUEUE_STAT_3A] = CSS_QUEUE_STAT_3A_BUF_SIZE,
};
static const struct imgu_node_mapping imgu_node_map[IMGU_NODE_NUM] = {
[IMGU_NODE_IN] = {IPU3_CSS_QUEUE_IN, "input"},
[IMGU_NODE_PARAMS] = {IPU3_CSS_QUEUE_PARAMS, "parameters"},
[IMGU_NODE_OUT] = {IPU3_CSS_QUEUE_OUT, "output"},
[IMGU_NODE_VF] = {IPU3_CSS_QUEUE_VF, "viewfinder"},
[IMGU_NODE_STAT_3A] = {IPU3_CSS_QUEUE_STAT_3A, "3a stat"},
};
unsigned int imgu_node_to_queue(unsigned int node)
{
return imgu_node_map[node].css_queue;
}
unsigned int imgu_map_node(struct imgu_device *imgu, unsigned int css_queue)
{
unsigned int i;
for (i = 0; i < IMGU_NODE_NUM; i++)
if (imgu_node_map[i].css_queue == css_queue)
break;
return i;
}
/**************** Dummy buffers ****************/
static void imgu_dummybufs_cleanup(struct imgu_device *imgu, unsigned int pipe)
{
unsigned int i;
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe];
for (i = 0; i < IPU3_CSS_QUEUES; i++)
ipu3_dmamap_free(imgu,
&imgu_pipe->queues[i].dmap);
}
static int imgu_dummybufs_preallocate(struct imgu_device *imgu,
unsigned int pipe)
{
unsigned int i;
size_t size;
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe];
for (i = 0; i < IPU3_CSS_QUEUES; i++) {
size = css_queue_buf_size_map[i];
/*
* Do not enable dummy buffers for master queue,
* always require that real buffers from user are
* available.
*/
if (i == IMGU_QUEUE_MASTER || size == 0)
continue;
if (!ipu3_dmamap_alloc(imgu,
&imgu_pipe->queues[i].dmap, size)) {
imgu_dummybufs_cleanup(imgu, pipe);
return -ENOMEM;
}
}
return 0;
}
static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe)
{
const struct v4l2_pix_format_mplane *mpix;
const struct v4l2_meta_format *meta;
unsigned int i, k, node;
size_t size;
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe];
/* Allocate a dummy buffer for each queue where buffer is optional */
for (i = 0; i < IPU3_CSS_QUEUES; i++) {
node = imgu_map_node(imgu, i);
if (!imgu_pipe->queue_enabled[node] || i == IMGU_QUEUE_MASTER)
continue;
if (!imgu_pipe->nodes[IMGU_NODE_VF].enabled &&
i == IPU3_CSS_QUEUE_VF)
/*
* Do not enable dummy buffers for VF if it is not
* requested by the user.
*/
continue;
meta = &imgu_pipe->nodes[node].vdev_fmt.fmt.meta;
mpix = &imgu_pipe->nodes[node].vdev_fmt.fmt.pix_mp;
if (node == IMGU_NODE_STAT_3A || node == IMGU_NODE_PARAMS)
size = meta->buffersize;
else
size = mpix->plane_fmt[0].sizeimage;
if (ipu3_css_dma_buffer_resize(imgu,
&imgu_pipe->queues[i].dmap,
size)) {
imgu_dummybufs_cleanup(imgu, pipe);
return -ENOMEM;
}
for (k = 0; k < IMGU_MAX_QUEUE_DEPTH; k++)
ipu3_css_buf_init(&imgu_pipe->queues[i].dummybufs[k], i,
imgu_pipe->queues[i].dmap.daddr);
}
return 0;
}
/* May be called from atomic context */
static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu,
int queue, unsigned int pipe)
{
unsigned int i;
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe];
/* dummybufs are not allocated for master q */
if (queue == IPU3_CSS_QUEUE_IN)
return NULL;
if (WARN_ON(!imgu_pipe->queues[queue].dmap.vaddr))
/* Buffer should not be allocated here */
return NULL;
for (i = 0; i < IMGU_MAX_QUEUE_DEPTH; i++)
if (ipu3_css_buf_state(&imgu_pipe->queues[queue].dummybufs[i]) !=
IPU3_CSS_BUFFER_QUEUED)
break;
if (i == IMGU_MAX_QUEUE_DEPTH)
return NULL;
ipu3_css_buf_init(&imgu_pipe->queues[queue].dummybufs[i], queue,
imgu_pipe->queues[queue].dmap.daddr);
return &imgu_pipe->queues[queue].dummybufs[i];
}
/* Check if given buffer is a dummy buffer */
static bool imgu_dummybufs_check(struct imgu_device *imgu,
struct ipu3_css_buffer *buf,
unsigned int pipe)
{
unsigned int i;
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe];
for (i = 0; i < IMGU_MAX_QUEUE_DEPTH; i++)
if (buf == &imgu_pipe->queues[buf->queue].dummybufs[i])
break;
return i < IMGU_MAX_QUEUE_DEPTH;
}
static void imgu_buffer_done(struct imgu_device *imgu, struct vb2_buffer *vb,
enum vb2_buffer_state state)
{
mutex_lock(&imgu->lock);
imgu_v4l2_buffer_done(vb, state);
mutex_unlock(&imgu->lock);
}
static struct ipu3_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu,
unsigned int node,
unsigned int pipe)
{
struct imgu_buffer *buf;
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe];
if (WARN_ON(node >= IMGU_NODE_NUM))
return NULL;
/* Find first free buffer from the node */
list_for_each_entry(buf, &imgu_pipe->nodes[node].buffers, vid_buf.list) {
if (ipu3_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_NEW)
return &buf->css_buf;
}
/* There were no free buffers, try to return a dummy buffer */
return imgu_dummybufs_get(imgu, imgu_node_map[node].css_queue, pipe);
}
/*
* Queue as many buffers to CSS as possible. If all buffers don't fit into
* CSS buffer queues, they remain unqueued and will be queued later.
*/
int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe)
{
unsigned int node;
int r = 0;
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe];
if (!ipu3_css_is_streaming(&imgu->css))
return 0;
dev_dbg(&imgu->pci_dev->dev, "Queue buffers to pipe %d", pipe);
mutex_lock(&imgu->lock);
/* Buffer set is queued to FW only when input buffer is ready */
for (node = IMGU_NODE_NUM - 1;
imgu_queue_getbuf(imgu, IMGU_NODE_IN, pipe);
node = node ? node - 1 : IMGU_NODE_NUM - 1) {
if (node == IMGU_NODE_VF &&
!imgu_pipe->nodes[IMGU_NODE_VF].enabled) {
dev_warn(&imgu->pci_dev->dev,
"Vf not enabled, ignore queue");
continue;
} else if (imgu_pipe->queue_enabled[node]) {
struct ipu3_css_buffer *buf =
imgu_queue_getbuf(imgu, node, pipe);
struct imgu_buffer *ibuf = NULL;
bool dummy;
if (!buf)
break;
r = ipu3_css_buf_queue(&imgu->css, pipe, buf);
if (r)
break;
dummy = imgu_dummybufs_check(imgu, buf, pipe);
if (!dummy)
ibuf = container_of(buf, struct imgu_buffer,
css_buf);
dev_dbg(&imgu->pci_dev->dev,
"queue %s %s buffer %u to css da: 0x%08x\n",
dummy ? "dummy" : "user",
imgu_node_map[node].name,
dummy ? 0 : ibuf->vid_buf.vbb.vb2_buf.index,
(u32)buf->daddr);
}
}
mutex_unlock(&imgu->lock);
if (r && r != -EBUSY)
goto failed;
return 0;
failed:
/*
* On error, mark all buffers as failed which are not
* yet queued to CSS
*/
dev_err(&imgu->pci_dev->dev,
"failed to queue buffer to CSS on queue %i (%d)\n",
node, r);
if (initial)
/* If we were called from streamon(), no need to finish bufs */
return r;
for (node = 0; node < IMGU_NODE_NUM; node++) {
struct imgu_buffer *buf, *buf0;
if (!imgu_pipe->queue_enabled[node])
continue; /* Skip disabled queues */
mutex_lock(&imgu->lock);
list_for_each_entry_safe(buf, buf0,
&imgu_pipe->nodes[node].buffers,
vid_buf.list) {
if (ipu3_css_buf_state(&buf->css_buf) ==
IPU3_CSS_BUFFER_QUEUED)
continue; /* Was already queued, skip */
imgu_v4l2_buffer_done(&buf->vid_buf.vbb.vb2_buf,
VB2_BUF_STATE_ERROR);
}
mutex_unlock(&imgu->lock);
}
return r;
}
static int imgu_powerup(struct imgu_device *imgu)
{
int r;
r = ipu3_css_set_powerup(&imgu->pci_dev->dev, imgu->base);
if (r)
return r;
ipu3_mmu_resume(imgu->mmu);
return 0;
}
static void imgu_powerdown(struct imgu_device *imgu)
{
ipu3_mmu_suspend(imgu->mmu);
ipu3_css_set_powerdown(&imgu->pci_dev->dev, imgu->base);
}
int imgu_s_stream(struct imgu_device *imgu, int enable)
{
struct device *dev = &imgu->pci_dev->dev;
int r, pipe;
if (!enable) {
/* Stop streaming */
dev_dbg(dev, "stream off\n");
/* Block new buffers to be queued to CSS. */
atomic_set(&imgu->qbuf_barrier, 1);
ipu3_css_stop_streaming(&imgu->css);
synchronize_irq(imgu->pci_dev->irq);
atomic_set(&imgu->qbuf_barrier, 0);
imgu_powerdown(imgu);
pm_runtime_put(&imgu->pci_dev->dev);
return 0;
}
/* Set Power */
r = pm_runtime_get_sync(dev);
if (r < 0) {
dev_err(dev, "failed to set imgu power\n");
pm_runtime_put(dev);
return r;
}
r = imgu_powerup(imgu);
if (r) {
dev_err(dev, "failed to power up imgu\n");
pm_runtime_put(dev);
return r;
}
/* Start CSS streaming */
r = ipu3_css_start_streaming(&imgu->css);
if (r) {
dev_err(dev, "failed to start css streaming (%d)", r);
goto fail_start_streaming;
}
for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM) {
/* Initialize dummy buffers */
r = imgu_dummybufs_init(imgu, pipe);
if (r) {
dev_err(dev, "failed to initialize dummy buffers (%d)", r);
goto fail_dummybufs;
}
/* Queue as many buffers from queue as possible */
r = imgu_queue_buffers(imgu, true, pipe);
if (r) {
dev_err(dev, "failed to queue initial buffers (%d)", r);
goto fail_queueing;
}
}
return 0;
fail_queueing:
for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM)
imgu_dummybufs_cleanup(imgu, pipe);
fail_dummybufs:
ipu3_css_stop_streaming(&imgu->css);
fail_start_streaming:
pm_runtime_put(dev);
return r;
}
static int imgu_video_nodes_init(struct imgu_device *imgu)
{
struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES] = { NULL };
struct v4l2_rect *rects[IPU3_CSS_RECTS] = { NULL };
struct imgu_media_pipe *imgu_pipe;
unsigned int i, j;
int r;
imgu->buf_struct_size = sizeof(struct imgu_buffer);
for (j = 0; j < IMGU_MAX_PIPE_NUM; j++) {
imgu_pipe = &imgu->imgu_pipe[j];
for (i = 0; i < IMGU_NODE_NUM; i++) {
imgu_pipe->nodes[i].name = imgu_node_map[i].name;
imgu_pipe->nodes[i].output = i < IMGU_QUEUE_FIRST_INPUT;
imgu_pipe->nodes[i].enabled = false;
if (i != IMGU_NODE_PARAMS && i != IMGU_NODE_STAT_3A)
fmts[imgu_node_map[i].css_queue] =
&imgu_pipe->nodes[i].vdev_fmt.fmt.pix_mp;
atomic_set(&imgu_pipe->nodes[i].sequence, 0);
}
}
r = imgu_v4l2_register(imgu);
if (r)
return r;
/* Set initial formats and initialize formats of video nodes */
for (j = 0; j < IMGU_MAX_PIPE_NUM; j++) {
imgu_pipe = &imgu->imgu_pipe[j];
rects[IPU3_CSS_RECT_EFFECTIVE] = &imgu_pipe->imgu_sd.rect.eff;
rects[IPU3_CSS_RECT_BDS] = &imgu_pipe->imgu_sd.rect.bds;
ipu3_css_fmt_set(&imgu->css, fmts, rects, j);
/* Pre-allocate dummy buffers */
r = imgu_dummybufs_preallocate(imgu, j);
if (r) {
dev_err(&imgu->pci_dev->dev,
"failed to pre-allocate dummy buffers (%d)", r);
goto out_cleanup;
}
}
return 0;
out_cleanup:
for (j = 0; j < IMGU_MAX_PIPE_NUM; j++)
imgu_dummybufs_cleanup(imgu, j);
imgu_v4l2_unregister(imgu);
return r;
}
static void imgu_video_nodes_exit(struct imgu_device *imgu)
{
int i;
for (i = 0; i < IMGU_MAX_PIPE_NUM; i++)
imgu_dummybufs_cleanup(imgu, i);
imgu_v4l2_unregister(imgu);
}
/**************** PCI interface ****************/
static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr)
{
struct imgu_device *imgu = imgu_ptr;
struct imgu_media_pipe *imgu_pipe;
int p;
/* Dequeue / queue buffers */
do {
u64 ns = ktime_get_ns();
struct ipu3_css_buffer *b;
struct imgu_buffer *buf = NULL;
unsigned int node, pipe;
bool dummy;
do {
mutex_lock(&imgu->lock);
b = ipu3_css_buf_dequeue(&imgu->css);
mutex_unlock(&imgu->lock);
} while (PTR_ERR(b) == -EAGAIN);
if (IS_ERR_OR_NULL(b)) {
if (!b || PTR_ERR(b) == -EBUSY) /* All done */
break;
dev_err(&imgu->pci_dev->dev,
"failed to dequeue buffers (%ld)\n",
PTR_ERR(b));
break;
}
node = imgu_map_node(imgu, b->queue);
pipe = b->pipe;
dummy = imgu_dummybufs_check(imgu, b, pipe);
if (!dummy)
buf = container_of(b, struct imgu_buffer, css_buf);
dev_dbg(&imgu->pci_dev->dev,
"dequeue %s %s buffer %d daddr 0x%x from css\n",
dummy ? "dummy" : "user",
imgu_node_map[node].name,
dummy ? 0 : buf->vid_buf.vbb.vb2_buf.index,
(u32)b->daddr);
if (dummy)
/* It was a dummy buffer, skip it */
continue;
/* Fill vb2 buffer entries and tell it's ready */
imgu_pipe = &imgu->imgu_pipe[pipe];
if (!imgu_pipe->nodes[node].output) {
buf->vid_buf.vbb.vb2_buf.timestamp = ns;
buf->vid_buf.vbb.field = V4L2_FIELD_NONE;
buf->vid_buf.vbb.sequence =
atomic_inc_return(
&imgu_pipe->nodes[node].sequence);
dev_dbg(&imgu->pci_dev->dev, "vb2 buffer sequence %d",
buf->vid_buf.vbb.sequence);
}
imgu_buffer_done(imgu, &buf->vid_buf.vbb.vb2_buf,
ipu3_css_buf_state(&buf->css_buf) ==
IPU3_CSS_BUFFER_DONE ?
VB2_BUF_STATE_DONE :
VB2_BUF_STATE_ERROR);
mutex_lock(&imgu->lock);
if (ipu3_css_queue_empty(&imgu->css))
wake_up_all(&imgu->buf_drain_wq);
mutex_unlock(&imgu->lock);
} while (1);
/*
* Try to queue more buffers for CSS.
* qbuf_barrier is used to disable new buffers
* to be queued to CSS.
*/
if (!atomic_read(&imgu->qbuf_barrier))
for_each_set_bit(p, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM)
imgu_queue_buffers(imgu, false, p);
return IRQ_HANDLED;
}
static irqreturn_t imgu_isr(int irq, void *imgu_ptr)
{
struct imgu_device *imgu = imgu_ptr;
/* acknowledge interruption */
if (ipu3_css_irq_ack(&imgu->css) < 0)
return IRQ_NONE;
return IRQ_WAKE_THREAD;
}
static int imgu_pci_config_setup(struct pci_dev *dev)
{
u16 pci_command;
int r = pci_enable_msi(dev);
if (r) {
dev_err(&dev->dev, "failed to enable MSI (%d)\n", r);
return r;
}
pci_read_config_word(dev, PCI_COMMAND, &pci_command);
pci_command |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
PCI_COMMAND_INTX_DISABLE;
pci_write_config_word(dev, PCI_COMMAND, pci_command);
return 0;
}
static int imgu_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *id)
{
struct imgu_device *imgu;
phys_addr_t phys;
unsigned long phys_len;
void __iomem *const *iomap;
int r;
imgu = devm_kzalloc(&pci_dev->dev, sizeof(*imgu), GFP_KERNEL);
if (!imgu)
return -ENOMEM;
imgu->pci_dev = pci_dev;
r = pcim_enable_device(pci_dev);
if (r) {
dev_err(&pci_dev->dev, "failed to enable device (%d)\n", r);
return r;
}
dev_info(&pci_dev->dev, "device 0x%x (rev: 0x%x)\n",
pci_dev->device, pci_dev->revision);
phys = pci_resource_start(pci_dev, IMGU_PCI_BAR);
phys_len = pci_resource_len(pci_dev, IMGU_PCI_BAR);
r = pcim_iomap_regions(pci_dev, 1 << IMGU_PCI_BAR, pci_name(pci_dev));
if (r) {
dev_err(&pci_dev->dev, "failed to remap I/O memory (%d)\n", r);
return r;
}
dev_info(&pci_dev->dev, "physical base address %pap, %lu bytes\n",
&phys, phys_len);
iomap = pcim_iomap_table(pci_dev);
if (!iomap) {
dev_err(&pci_dev->dev, "failed to iomap table\n");
return -ENODEV;
}
imgu->base = iomap[IMGU_PCI_BAR];
pci_set_drvdata(pci_dev, imgu);
pci_set_master(pci_dev);
r = dma_coerce_mask_and_coherent(&pci_dev->dev, IMGU_DMA_MASK);
if (r) {
dev_err(&pci_dev->dev, "failed to set DMA mask (%d)\n", r);
return -ENODEV;
}
r = imgu_pci_config_setup(pci_dev);
if (r)
return r;
mutex_init(&imgu->lock);
atomic_set(&imgu->qbuf_barrier, 0);
init_waitqueue_head(&imgu->buf_drain_wq);
r = ipu3_css_set_powerup(&pci_dev->dev, imgu->base);
if (r) {
dev_err(&pci_dev->dev,
"failed to power up CSS (%d)\n", r);
goto out_mutex_destroy;
}
imgu->mmu = ipu3_mmu_init(&pci_dev->dev, imgu->base);
if (IS_ERR(imgu->mmu)) {
r = PTR_ERR(imgu->mmu);
dev_err(&pci_dev->dev, "failed to initialize MMU (%d)\n", r);
goto out_css_powerdown;
}
r = ipu3_dmamap_init(imgu);
if (r) {
dev_err(&pci_dev->dev,
"failed to initialize DMA mapping (%d)\n", r);
goto out_mmu_exit;
}
/* ISP programming */
r = ipu3_css_init(&pci_dev->dev, &imgu->css, imgu->base, phys_len);
if (r) {
dev_err(&pci_dev->dev, "failed to initialize CSS (%d)\n", r);
goto out_dmamap_exit;
}
/* v4l2 sub-device registration */
r = imgu_video_nodes_init(imgu);
if (r) {
dev_err(&pci_dev->dev, "failed to create V4L2 devices (%d)\n",
r);
goto out_css_cleanup;
}
r = devm_request_threaded_irq(&pci_dev->dev, pci_dev->irq,
imgu_isr, imgu_isr_threaded,
IRQF_SHARED, IMGU_NAME, imgu);
if (r) {
dev_err(&pci_dev->dev, "failed to request IRQ (%d)\n", r);
goto out_video_exit;
}
pm_runtime_put_noidle(&pci_dev->dev);
pm_runtime_allow(&pci_dev->dev);
return 0;
out_video_exit:
imgu_video_nodes_exit(imgu);
out_css_cleanup:
ipu3_css_cleanup(&imgu->css);
out_dmamap_exit:
ipu3_dmamap_exit(imgu);
out_mmu_exit:
ipu3_mmu_exit(imgu->mmu);
out_css_powerdown:
ipu3_css_set_powerdown(&pci_dev->dev, imgu->base);
out_mutex_destroy:
mutex_destroy(&imgu->lock);
return r;
}
static void imgu_pci_remove(struct pci_dev *pci_dev)
{
struct imgu_device *imgu = pci_get_drvdata(pci_dev);
pm_runtime_forbid(&pci_dev->dev);
pm_runtime_get_noresume(&pci_dev->dev);
imgu_video_nodes_exit(imgu);
ipu3_css_cleanup(&imgu->css);
ipu3_css_set_powerdown(&pci_dev->dev, imgu->base);
ipu3_dmamap_exit(imgu);
ipu3_mmu_exit(imgu->mmu);
mutex_destroy(&imgu->lock);
}
static int __maybe_unused imgu_suspend(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct imgu_device *imgu = pci_get_drvdata(pci_dev);
dev_dbg(dev, "enter %s\n", __func__);
imgu->suspend_in_stream = ipu3_css_is_streaming(&imgu->css);
if (!imgu->suspend_in_stream)
goto out;
/* Block new buffers to be queued to CSS. */
atomic_set(&imgu->qbuf_barrier, 1);
/*
* Wait for currently running irq handler to be done so that
* no new buffers will be queued to fw later.
*/
synchronize_irq(pci_dev->irq);
/* Wait until all buffers in CSS are done. */
if (!wait_event_timeout(imgu->buf_drain_wq,
ipu3_css_queue_empty(&imgu->css), msecs_to_jiffies(1000)))
dev_err(dev, "wait buffer drain timeout.\n");
ipu3_css_stop_streaming(&imgu->css);
atomic_set(&imgu->qbuf_barrier, 0);
imgu_powerdown(imgu);
pm_runtime_force_suspend(dev);
out:
dev_dbg(dev, "leave %s\n", __func__);
return 0;
}
static int __maybe_unused imgu_resume(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct imgu_device *imgu = pci_get_drvdata(pci_dev);
int r = 0;
unsigned int pipe;
dev_dbg(dev, "enter %s\n", __func__);
if (!imgu->suspend_in_stream)
goto out;
pm_runtime_force_resume(dev);
r = imgu_powerup(imgu);
if (r) {
dev_err(dev, "failed to power up imgu\n");
goto out;
}
/* Start CSS streaming */
r = ipu3_css_start_streaming(&imgu->css);
if (r) {
dev_err(dev, "failed to resume css streaming (%d)", r);
goto out;
}
for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM) {
r = imgu_queue_buffers(imgu, true, pipe);
if (r)
dev_err(dev, "failed to queue buffers to pipe %d (%d)",
pipe, r);
}
out:
dev_dbg(dev, "leave %s\n", __func__);
return r;
}
/*
* PCI rpm framework checks the existence of driver rpm callbacks.
* Place a dummy callback here to avoid rpm going into error state.
*/
static int imgu_rpm_dummy_cb(struct device *dev)
{
return 0;
}
static const struct dev_pm_ops imgu_pm_ops = {
SET_RUNTIME_PM_OPS(&imgu_rpm_dummy_cb, &imgu_rpm_dummy_cb, NULL)
SET_SYSTEM_SLEEP_PM_OPS(&imgu_suspend, &imgu_resume)
};
static const struct pci_device_id imgu_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, IMGU_PCI_ID) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, imgu_pci_tbl);
static struct pci_driver imgu_pci_driver = {
.name = IMGU_NAME,
.id_table = imgu_pci_tbl,
.probe = imgu_pci_probe,
.remove = imgu_pci_remove,
.driver = {
.pm = &imgu_pm_ops,
},
};
module_pci_driver(imgu_pci_driver);
MODULE_AUTHOR("Tuukka Toivonen <tuukka.toivonen@intel.com>");
MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
MODULE_AUTHOR("Jian Xu Zheng <jian.xu.zheng@intel.com>");
MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel ipu3_imgu PCI driver");

View File

@ -0,0 +1,168 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018 Intel Corporation */
#ifndef __IPU3_H
#define __IPU3_H
#include <linux/iova.h>
#include <linux/pci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/videobuf2-dma-sg.h>
#include "ipu3-css.h"
#define IMGU_NAME "ipu3-imgu"
/*
* The semantics of the driver is that whenever there is a buffer available in
* master queue, the driver queues a buffer also to all other active nodes.
* If user space hasn't provided a buffer to all other video nodes first,
* the driver gets an internal dummy buffer and queues it.
*/
#define IMGU_QUEUE_MASTER IPU3_CSS_QUEUE_IN
#define IMGU_QUEUE_FIRST_INPUT IPU3_CSS_QUEUE_OUT
#define IMGU_MAX_QUEUE_DEPTH (2 + 2)
#define IMGU_NODE_IN 0 /* Input RAW image */
#define IMGU_NODE_PARAMS 1 /* Input parameters */
#define IMGU_NODE_OUT 2 /* Main output for still or video */
#define IMGU_NODE_VF 3 /* Preview */
#define IMGU_NODE_STAT_3A 4 /* 3A statistics */
#define IMGU_NODE_NUM 5
#define file_to_intel_ipu3_node(__file) \
container_of(video_devdata(__file), struct imgu_video_device, vdev)
#define IPU3_INPUT_MIN_WIDTH 0U
#define IPU3_INPUT_MIN_HEIGHT 0U
#define IPU3_INPUT_MAX_WIDTH 5120U
#define IPU3_INPUT_MAX_HEIGHT 38404U
#define IPU3_OUTPUT_MIN_WIDTH 2U
#define IPU3_OUTPUT_MIN_HEIGHT 2U
#define IPU3_OUTPUT_MAX_WIDTH 4480U
#define IPU3_OUTPUT_MAX_HEIGHT 34004U
struct ipu3_vb2_buffer {
/* Public fields */
struct vb2_v4l2_buffer vbb; /* Must be the first field */
/* Private fields */
struct list_head list;
};
struct imgu_buffer {
struct ipu3_vb2_buffer vid_buf; /* Must be the first field */
struct ipu3_css_buffer css_buf;
struct ipu3_css_map map;
};
struct imgu_node_mapping {
unsigned int css_queue;
const char *name;
};
/**
* struct imgu_video_device
* each node registers as video device and maintains its
* own vb2_queue.
*/
struct imgu_video_device {
const char *name;
bool output;
bool enabled;
struct v4l2_format vdev_fmt; /* Currently set format */
/* Private fields */
struct video_device vdev;
struct media_pad vdev_pad;
struct v4l2_mbus_framefmt pad_fmt;
struct vb2_queue vbq;
struct list_head buffers;
/* Protect vb2_queue and vdev structs*/
struct mutex lock;
atomic_t sequence;
unsigned int id;
unsigned int pipe;
};
struct imgu_v4l2_subdev {
unsigned int pipe;
struct v4l2_subdev subdev;
struct media_pad subdev_pads[IMGU_NODE_NUM];
struct {
struct v4l2_rect eff; /* effective resolution */
struct v4l2_rect bds; /* bayer-domain scaled resolution*/
struct v4l2_rect gdc; /* gdc output resolution */
} rect;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_ctrl *ctrl;
atomic_t running_mode;
bool active;
};
struct imgu_media_pipe {
unsigned int pipe;
/* Internally enabled queues */
struct {
struct ipu3_css_map dmap;
struct ipu3_css_buffer dummybufs[IMGU_MAX_QUEUE_DEPTH];
} queues[IPU3_CSS_QUEUES];
struct imgu_video_device nodes[IMGU_NODE_NUM];
bool queue_enabled[IMGU_NODE_NUM];
struct media_pipeline pipeline;
struct imgu_v4l2_subdev imgu_sd;
};
/*
* imgu_device -- ImgU (Imaging Unit) driver
*/
struct imgu_device {
struct pci_dev *pci_dev;
void __iomem *base;
/* Public fields, fill before registering */
unsigned int buf_struct_size;
bool streaming; /* Public read only */
struct imgu_media_pipe imgu_pipe[IMGU_MAX_PIPE_NUM];
/* Private fields */
struct v4l2_device v4l2_dev;
struct media_device media_dev;
struct v4l2_file_operations v4l2_file_ops;
/* MMU driver for css */
struct ipu3_mmu_info *mmu;
struct iova_domain iova_domain;
/* css - Camera Sub-System */
struct ipu3_css css;
/*
* Coarse-grained lock to protect
* vid_buf.list and css->queue
*/
struct mutex lock;
/* Forbit streaming and buffer queuing during system suspend. */
atomic_t qbuf_barrier;
/* Indicate if system suspend take place while imgu is streaming. */
bool suspend_in_stream;
/* Used to wait for FW buffer queue drain. */
wait_queue_head_t buf_drain_wq;
};
unsigned int imgu_node_to_queue(unsigned int node);
unsigned int imgu_map_node(struct imgu_device *imgu, unsigned int css_queue);
int imgu_queue_buffers(struct imgu_device *imgu, bool initial,
unsigned int pipe);
int imgu_v4l2_register(struct imgu_device *dev);
int imgu_v4l2_unregister(struct imgu_device *dev);
void imgu_v4l2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state);
int imgu_s_stream(struct imgu_device *imgu, int enable);
#endif

View File

@ -48,6 +48,9 @@ struct v4l2_fh;
* @vidioc_enum_fmt_meta_cap: pointer to the function that implements
* :ref:`VIDIOC_ENUM_FMT <vidioc_enum_fmt>` ioctl logic
* for metadata capture
* @vidioc_enum_fmt_meta_out: pointer to the function that implements
* :ref:`VIDIOC_ENUM_FMT <vidioc_enum_fmt>` ioctl logic
* for metadata output
* @vidioc_g_fmt_vid_cap: pointer to the function that implements
* :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video capture
* in single plane mode
@ -80,6 +83,8 @@ struct v4l2_fh;
* Radio output
* @vidioc_g_fmt_meta_cap: pointer to the function that implements
* :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for metadata capture
* @vidioc_g_fmt_meta_out: pointer to the function that implements
* :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for metadata output
* @vidioc_s_fmt_vid_cap: pointer to the function that implements
* :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video capture
* in single plane mode
@ -112,6 +117,8 @@ struct v4l2_fh;
* Radio output
* @vidioc_s_fmt_meta_cap: pointer to the function that implements
* :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for metadata capture
* @vidioc_s_fmt_meta_out: pointer to the function that implements
* :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for metadata output
* @vidioc_try_fmt_vid_cap: pointer to the function that implements
* :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video capture
* in single plane mode
@ -146,6 +153,8 @@ struct v4l2_fh;
* Radio output
* @vidioc_try_fmt_meta_cap: pointer to the function that implements
* :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for metadata capture
* @vidioc_try_fmt_meta_out: pointer to the function that implements
* :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for metadata output
* @vidioc_reqbufs: pointer to the function that implements
* :ref:`VIDIOC_REQBUFS <vidioc_reqbufs>` ioctl
* @vidioc_querybuf: pointer to the function that implements
@ -314,6 +323,8 @@ struct v4l2_ioctl_ops {
struct v4l2_fmtdesc *f);
int (*vidioc_enum_fmt_meta_cap)(struct file *file, void *fh,
struct v4l2_fmtdesc *f);
int (*vidioc_enum_fmt_meta_out)(struct file *file, void *fh,
struct v4l2_fmtdesc *f);
/* VIDIOC_G_FMT handlers */
int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
@ -342,6 +353,8 @@ struct v4l2_ioctl_ops {
struct v4l2_format *f);
int (*vidioc_g_fmt_meta_cap)(struct file *file, void *fh,
struct v4l2_format *f);
int (*vidioc_g_fmt_meta_out)(struct file *file, void *fh,
struct v4l2_format *f);
/* VIDIOC_S_FMT handlers */
int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
@ -370,6 +383,8 @@ struct v4l2_ioctl_ops {
struct v4l2_format *f);
int (*vidioc_s_fmt_meta_cap)(struct file *file, void *fh,
struct v4l2_format *f);
int (*vidioc_s_fmt_meta_out)(struct file *file, void *fh,
struct v4l2_format *f);
/* VIDIOC_TRY_FMT handlers */
int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
@ -398,6 +413,8 @@ struct v4l2_ioctl_ops {
struct v4l2_format *f);
int (*vidioc_try_fmt_meta_cap)(struct file *file, void *fh,
struct v4l2_format *f);
int (*vidioc_try_fmt_meta_out)(struct file *file, void *fh,
struct v4l2_format *f);
/* Buffer handlers */
int (*vidioc_reqbufs)(struct file *file, void *fh,

View File

@ -145,6 +145,7 @@ enum v4l2_buf_type {
V4L2_BUF_TYPE_SDR_CAPTURE = 11,
V4L2_BUF_TYPE_SDR_OUTPUT = 12,
V4L2_BUF_TYPE_META_CAPTURE = 13,
V4L2_BUF_TYPE_META_OUTPUT = 14,
/* Deprecated, do not use */
V4L2_BUF_TYPE_PRIVATE = 0x80,
};
@ -469,6 +470,7 @@ struct v4l2_capability {
#define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */
#define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */
#define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */
#define V4L2_CAP_META_OUTPUT 0x08000000 /* Is a metadata output device */
#define V4L2_CAP_TOUCH 0x10000000 /* Is a touch device */