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:
commit
996680d461
|
@ -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`.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -39,4 +39,6 @@ source "drivers/staging/media/tegra-vde/Kconfig"
|
|||
|
||||
source "drivers/staging/media/zoran/Kconfig"
|
||||
|
||||
source "drivers/staging/media/ipu3/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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
|
@ -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");
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
Loading…
Reference in New Issue