media updates for v6.10-rc1
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+QmuaPwR3wnBdVwACF8+vY7k4RUFAmZFpvgACgkQCF8+vY7k 4RXffg//UOFGd12GwhBtkU1a3cBqT1DAUG8GRnmhLnGypRaiP7ypRhI/LV1ZZ0SQ vjKuDuXrbk+JJ4hxNTH8GoisYpnRqqC2vIm5cnjCiMxN/pY/GkzPm7MU5zEhuWMB Rtz5RS4UrTtpJ95XxuDhXY5rRb3uPXMF2LUHLUbYq3IoUGz8x/ta1aKE56B35vY+ jDg9JQugR1ciIf0OL7kvDJJfDUKkGGsr/u4gRWBxntYHtVMdUJXso3tYa78F1mBX oTWKc8IFms1JgA7NdDnKttOCO0Ykb0IJxE0qO094xuOPW50wLsLByJXdxJtOBj/Q iLvSIVrk//U+re0j6xLJgKES6ldZvDKn5AU3O22lbm9cgeXrbONIHQOSqLumYPCi HLnuc0eq4oED1UHj695pNyjgigUmZL9mDMB31AU92r0pfOKpGFRnexT1tyhqFonN 88HMKInudnLsE7lVPzbUSVZxJfhOFj7jf8LILnRzqzy0HOD7te5KhxdjxtBmXvoN lpQ3Cs+i/n3Fe510mO0rcpeR73nYkNnX7EoJWOjojCK+Cz7/GnXICF53T0yAYANA W6ZGKNCEEgs8ce6dFrRG33jv0I8b/u6L5BVuWT/Ndam+KwMw59OjKlNPDiTvtwSR OZDL9eifturMuMUe0HT6k6k3u6VYWWjn2cvMFHg4g7Y6JOrllfQ= =JM5r -----END PGP SIGNATURE----- Merge tag 'media/v6.10-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media Pull media updates from Mauro Carvalho Chehab: - New V4L2 ioctl VIDIOC_REMOVE_BUFS - experimental support for using generic metaformats on V4L2 core - New drivers: Intel IPU6 controller driver, Broadcom BCM283x/BCM271x - More cleanups at atomisp driver - Usual bunch of driver cleanups, improvements and fixes * tag 'media/v6.10-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (328 commits) media: bcm2835-unicam: Depend on COMMON_CLK Revert "media: v4l2-ctrls: show all owned controls in log_status" media: ov2740: Ensure proper reset sequence on probe() media: intel/ipu6: Don't print user-triggerable errors to kernel log media: bcm2835-unicam: Fix driver path in MAINTAINERS media: bcm2835-unicam: Fix a NULL vs IS_ERR() check media: bcm2835-unicam: Do not print error when irq not found media: bcm2835-unicam: Do not replace IRQ retcode during probe media: bcm2835-unicam: Convert to platform remove callback returning void media: media: intel/ipu6: Fix spelling mistake "remappinp" -> "remapping" media: intel/ipu6: explicitly include vmalloc.h media: cec.h: Fix kerneldoc media: uvcvideo: Refactor iterators media: v4l: async: refactor v4l2_async_create_ancillary_links media: intel/ipu6: Don't re-allocate memory for firmware media: dvb-frontends: tda10048: Fix integer overflow media: tc358746: Use the correct div_ function media: i2c: st-mipid02: Use the correct div function media: tegra-vde: Refactor timeout handling media: stk1160: Use min macro ...
This commit is contained in:
commit
6fd600d742
161
Documentation/admin-guide/media/ipu6-isys.rst
Normal file
161
Documentation/admin-guide/media/ipu6-isys.rst
Normal file
@ -0,0 +1,161 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. include:: <isonum.txt>
|
||||
|
||||
========================================================
|
||||
Intel Image Processing Unit 6 (IPU6) Input System driver
|
||||
========================================================
|
||||
|
||||
Copyright |copy| 2023--2024 Intel Corporation
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
This file documents the Intel IPU6 (6th generation Image Processing Unit)
|
||||
Input System (MIPI CSI2 receiver) drivers located under
|
||||
drivers/media/pci/intel/ipu6.
|
||||
|
||||
The Intel IPU6 can be found in certain Intel SoCs but not in all SKUs:
|
||||
|
||||
* Tiger Lake
|
||||
* Jasper Lake
|
||||
* Alder Lake
|
||||
* Raptor Lake
|
||||
* Meteor Lake
|
||||
|
||||
Intel IPU6 is made up of two components - Input System (ISYS) and Processing
|
||||
System (PSYS).
|
||||
|
||||
The Input System mainly works as MIPI CSI-2 receiver which receives and
|
||||
processes the image data from the sensors and outputs the frames to memory.
|
||||
|
||||
There are 2 driver modules - intel-ipu6 and intel-ipu6-isys. intel-ipu6 is an
|
||||
IPU6 common driver which does PCI configuration, firmware loading and parsing,
|
||||
firmware authentication, DMA mapping and IPU-MMU (internal Memory mapping Unit)
|
||||
configuration. intel_ipu6_isys implements V4L2, Media Controller and V4L2
|
||||
sub-device interfaces. The IPU6 ISYS driver supports camera sensors connected
|
||||
to the IPU6 ISYS through V4L2 sub-device sensor drivers.
|
||||
|
||||
.. Note:: See Documentation/driver-api/media/drivers/ipu6.rst for more
|
||||
information about the IPU6 hardware.
|
||||
|
||||
Input system driver
|
||||
===================
|
||||
|
||||
The Input System driver mainly configures CSI-2 D-PHY, constructs the firmware
|
||||
stream configuration, sends commands to firmware, gets response from hardware
|
||||
and firmware and then returns buffers to user. The ISYS is represented as
|
||||
several V4L2 sub-devices as well as video nodes.
|
||||
|
||||
.. kernel-figure:: ipu6_isys_graph.svg
|
||||
:alt: ipu6 isys media graph with multiple streams support
|
||||
|
||||
IPU6 ISYS media graph with multiple streams support
|
||||
|
||||
The graph has been produced using the following command:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
fdp -Gsplines=true -Tsvg < dot > dot.svg
|
||||
|
||||
Capturing frames with IPU6 ISYS
|
||||
-------------------------------
|
||||
|
||||
IPU6 ISYS is used to capture frames from the camera sensors connected to the
|
||||
CSI2 ports. The supported input formats of ISYS are listed in table below:
|
||||
|
||||
.. tabularcolumns:: |p{0.8cm}|p{4.0cm}|p{4.0cm}|
|
||||
|
||||
.. flat-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - IPU6 ISYS supported input formats
|
||||
|
||||
* - RGB565, RGB888
|
||||
|
||||
* - UYVY8, YUYV8
|
||||
|
||||
* - RAW8, RAW10, RAW12
|
||||
|
||||
.. _ipu6_isys_capture_examples:
|
||||
|
||||
Examples
|
||||
~~~~~~~~
|
||||
|
||||
Here is an example of IPU6 ISYS raw capture on Dell XPS 9315 laptop. On this
|
||||
machine, ov01a10 sensor is connected to IPU ISYS CSI-2 port 2, which can
|
||||
generate images at sBGGR10 with resolution 1280x800.
|
||||
|
||||
Using the media controller APIs, we can configure ov01a10 sensor by
|
||||
media-ctl [#f1]_ and yavta [#f2]_ to transmit frames to IPU6 ISYS.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
# Example 1 capture frame from ov01a10 camera sensor
|
||||
# This example assumes /dev/media0 as the IPU ISYS media device
|
||||
export MDEV=/dev/media0
|
||||
|
||||
# Establish the link for the media devices using media-ctl
|
||||
media-ctl -d $MDEV -l "\"ov01a10 3-0036\":0 -> \"Intel IPU6 CSI2 2\":0[1]"
|
||||
|
||||
# Set the format for the media devices
|
||||
media-ctl -d $MDEV -V "ov01a10:0 [fmt:SBGGR10/1280x800]"
|
||||
media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:0 [fmt:SBGGR10/1280x800]"
|
||||
media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:1 [fmt:SBGGR10/1280x800]"
|
||||
|
||||
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
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
# and that ov01a10 sensor is connected to i2c bus 3 with address 0x36
|
||||
export SDEV=$(media-ctl -d $MDEV -e "ov01a10 3-0036")
|
||||
|
||||
yavta -w 0x009e0903 400 $SDEV
|
||||
yavta -w 0x009e0913 1000 $SDEV
|
||||
yavta -w 0x009e0911 2000 $SDEV
|
||||
|
||||
Once the desired sensor settings are set, frame captures can be done as below.
|
||||
|
||||
e.g
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
yavta --data-prefix -u -c10 -n5 -I -s 1280x800 --file=/tmp/frame-#.bin \
|
||||
-f SBGGR10 $(media-ctl -d $MDEV -e "Intel IPU6 ISYS Capture 0")
|
||||
|
||||
With the above command, 10 frames are captured at 1280x800 resolution with
|
||||
sBGGR10 format. The captured frames are available as /tmp/frame-#.bin files.
|
||||
|
||||
Here is another example of IPU6 ISYS RAW and metadata capture from camera
|
||||
sensor ov2740 on Lenovo X1 Yoga laptop.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
media-ctl -l "\"ov2740 14-0036\":0 -> \"Intel IPU6 CSI2 1\":0[1]"
|
||||
media-ctl -l "\"Intel IPU6 CSI2 1\":1 -> \"Intel IPU6 ISYS Capture 0\":0[5]"
|
||||
media-ctl -l "\"Intel IPU6 CSI2 1\":2 -> \"Intel IPU6 ISYS Capture 1\":0[5]"
|
||||
|
||||
# set routing
|
||||
media-ctl -v -R "\"Intel IPU6 CSI2 1\" [0/0->1/0[1],0/1->2/1[1]]"
|
||||
|
||||
media-ctl -v "\"Intel IPU6 CSI2 1\":0/0 [fmt:SGRBG10/1932x1092]"
|
||||
media-ctl -v "\"Intel IPU6 CSI2 1\":0/1 [fmt:GENERIC_8/97x1]"
|
||||
media-ctl -v "\"Intel IPU6 CSI2 1\":1/0 [fmt:SGRBG10/1932x1092]"
|
||||
media-ctl -v "\"Intel IPU6 CSI2 1\":2/1 [fmt:GENERIC_8/97x1]"
|
||||
|
||||
CAPTURE_DEV=$(media-ctl -e "Intel IPU6 ISYS Capture 0")
|
||||
./yavta --data-prefix -c100 -n5 -I -s1932x1092 --file=/tmp/frame-#.bin \
|
||||
-f SGRBG10 ${CAPTURE_DEV}
|
||||
|
||||
CAPTURE_META=$(media-ctl -e "Intel IPU6 ISYS Capture 1")
|
||||
./yavta --data-prefix -c100 -n5 -I -s97x1 -B meta-capture \
|
||||
--file=/tmp/meta-#.bin -f GENERIC_8 ${CAPTURE_META}
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [#f1] https://git.ideasonboard.org/media-ctl.git
|
||||
.. [#f2] https://git.ideasonboard.org/yavta.git
|
548
Documentation/admin-guide/media/ipu6_isys_graph.svg
Normal file
548
Documentation/admin-guide/media/ipu6_isys_graph.svg
Normal file
@ -0,0 +1,548 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.43.0 (0)
|
||||
-->
|
||||
<!-- Title: board Pages: 1 -->
|
||||
<svg width="1703pt" height="1473pt"
|
||||
viewBox="0.00 0.00 1703.00 1473.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1469)">
|
||||
<title>board</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-1469 1699,-1469 1699,4 -4,4"/>
|
||||
<!-- n00000001 -->
|
||||
<g id="node1" class="node">
|
||||
<title>n00000001</title>
|
||||
<polygon fill="yellow" stroke="black" points="832.99,-750.08 629.99,-750.08 629.99,-712.08 832.99,-712.08 832.99,-750.08"/>
|
||||
<text text-anchor="middle" x="731.49" y="-734.88" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 0</text>
|
||||
<text text-anchor="middle" x="731.49" y="-719.88" font-family="Times,serif" font-size="14.00">/dev/video0</text>
|
||||
</g>
|
||||
<!-- n00000005 -->
|
||||
<g id="node2" class="node">
|
||||
<title>n00000005</title>
|
||||
<polygon fill="yellow" stroke="black" points="1396.59,-771.88 1193.59,-771.88 1193.59,-733.88 1396.59,-733.88 1396.59,-771.88"/>
|
||||
<text text-anchor="middle" x="1295.09" y="-756.68" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 1</text>
|
||||
<text text-anchor="middle" x="1295.09" y="-741.68" font-family="Times,serif" font-size="14.00">/dev/video1</text>
|
||||
</g>
|
||||
<!-- n00000009 -->
|
||||
<g id="node3" class="node">
|
||||
<title>n00000009</title>
|
||||
<polygon fill="yellow" stroke="black" points="1118.52,-690.47 915.52,-690.47 915.52,-652.47 1118.52,-652.47 1118.52,-690.47"/>
|
||||
<text text-anchor="middle" x="1017.02" y="-675.27" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 2</text>
|
||||
<text text-anchor="middle" x="1017.02" y="-660.27" font-family="Times,serif" font-size="14.00">/dev/video2</text>
|
||||
</g>
|
||||
<!-- n0000000d -->
|
||||
<g id="node4" class="node">
|
||||
<title>n0000000d</title>
|
||||
<polygon fill="yellow" stroke="black" points="1105.89,-838.84 902.89,-838.84 902.89,-800.84 1105.89,-800.84 1105.89,-838.84"/>
|
||||
<text text-anchor="middle" x="1004.39" y="-823.64" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 3</text>
|
||||
<text text-anchor="middle" x="1004.39" y="-808.64" font-family="Times,serif" font-size="14.00">/dev/video3</text>
|
||||
</g>
|
||||
<!-- n00000011 -->
|
||||
<g id="node5" class="node">
|
||||
<title>n00000011</title>
|
||||
<polygon fill="yellow" stroke="black" points="1279.22,-992.95 1076.22,-992.95 1076.22,-954.95 1279.22,-954.95 1279.22,-992.95"/>
|
||||
<text text-anchor="middle" x="1177.72" y="-977.75" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 4</text>
|
||||
<text text-anchor="middle" x="1177.72" y="-962.75" font-family="Times,serif" font-size="14.00">/dev/video4</text>
|
||||
</g>
|
||||
<!-- n00000015 -->
|
||||
<g id="node6" class="node">
|
||||
<title>n00000015</title>
|
||||
<polygon fill="yellow" stroke="black" points="939.18,-984.91 736.18,-984.91 736.18,-946.91 939.18,-946.91 939.18,-984.91"/>
|
||||
<text text-anchor="middle" x="837.68" y="-969.71" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 5</text>
|
||||
<text text-anchor="middle" x="837.68" y="-954.71" font-family="Times,serif" font-size="14.00">/dev/video5</text>
|
||||
</g>
|
||||
<!-- n00000019 -->
|
||||
<g id="node7" class="node">
|
||||
<title>n00000019</title>
|
||||
<polygon fill="yellow" stroke="black" points="957.87,-527.99 754.87,-527.99 754.87,-489.99 957.87,-489.99 957.87,-527.99"/>
|
||||
<text text-anchor="middle" x="856.37" y="-512.79" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 6</text>
|
||||
<text text-anchor="middle" x="856.37" y="-497.79" font-family="Times,serif" font-size="14.00">/dev/video6</text>
|
||||
</g>
|
||||
<!-- n0000001d -->
|
||||
<g id="node8" class="node">
|
||||
<title>n0000001d</title>
|
||||
<polygon fill="yellow" stroke="black" points="1291.02,-542.15 1088.02,-542.15 1088.02,-504.15 1291.02,-504.15 1291.02,-542.15"/>
|
||||
<text text-anchor="middle" x="1189.52" y="-526.95" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 7</text>
|
||||
<text text-anchor="middle" x="1189.52" y="-511.95" font-family="Times,serif" font-size="14.00">/dev/video7</text>
|
||||
</g>
|
||||
<!-- n00000021 -->
|
||||
<g id="node9" class="node">
|
||||
<title>n00000021</title>
|
||||
<polygon fill="yellow" stroke="black" points="202.74,-611.46 -0.26,-611.46 -0.26,-573.46 202.74,-573.46 202.74,-611.46"/>
|
||||
<text text-anchor="middle" x="101.24" y="-596.26" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 8</text>
|
||||
<text text-anchor="middle" x="101.24" y="-581.26" font-family="Times,serif" font-size="14.00">/dev/video8</text>
|
||||
</g>
|
||||
<!-- n00000025 -->
|
||||
<g id="node10" class="node">
|
||||
<title>n00000025</title>
|
||||
<polygon fill="yellow" stroke="black" points="764.86,-637.89 561.86,-637.89 561.86,-599.89 764.86,-599.89 764.86,-637.89"/>
|
||||
<text text-anchor="middle" x="663.36" y="-622.69" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 9</text>
|
||||
<text text-anchor="middle" x="663.36" y="-607.69" font-family="Times,serif" font-size="14.00">/dev/video9</text>
|
||||
</g>
|
||||
<!-- n00000029 -->
|
||||
<g id="node11" class="node">
|
||||
<title>n00000029</title>
|
||||
<polygon fill="yellow" stroke="black" points="358.62,-519.5 146.62,-519.5 146.62,-481.5 358.62,-481.5 358.62,-519.5"/>
|
||||
<text text-anchor="middle" x="252.62" y="-504.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 10</text>
|
||||
<text text-anchor="middle" x="252.62" y="-489.3" font-family="Times,serif" font-size="14.00">/dev/video10</text>
|
||||
</g>
|
||||
<!-- n0000002d -->
|
||||
<g id="node12" class="node">
|
||||
<title>n0000002d</title>
|
||||
<polygon fill="yellow" stroke="black" points="481.4,-662.59 269.4,-662.59 269.4,-624.59 481.4,-624.59 481.4,-662.59"/>
|
||||
<text text-anchor="middle" x="375.4" y="-647.39" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 11</text>
|
||||
<text text-anchor="middle" x="375.4" y="-632.39" font-family="Times,serif" font-size="14.00">/dev/video11</text>
|
||||
</g>
|
||||
<!-- n00000031 -->
|
||||
<g id="node13" class="node">
|
||||
<title>n00000031</title>
|
||||
<polygon fill="yellow" stroke="black" points="637.17,-837.47 425.17,-837.47 425.17,-799.47 637.17,-799.47 637.17,-837.47"/>
|
||||
<text text-anchor="middle" x="531.17" y="-822.27" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 12</text>
|
||||
<text text-anchor="middle" x="531.17" y="-807.27" font-family="Times,serif" font-size="14.00">/dev/video12</text>
|
||||
</g>
|
||||
<!-- n00000035 -->
|
||||
<g id="node14" class="node">
|
||||
<title>n00000035</title>
|
||||
<polygon fill="yellow" stroke="black" points="337.75,-833.67 125.75,-833.67 125.75,-795.67 337.75,-795.67 337.75,-833.67"/>
|
||||
<text text-anchor="middle" x="231.75" y="-818.47" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 13</text>
|
||||
<text text-anchor="middle" x="231.75" y="-803.47" font-family="Times,serif" font-size="14.00">/dev/video13</text>
|
||||
</g>
|
||||
<!-- n00000039 -->
|
||||
<g id="node15" class="node">
|
||||
<title>n00000039</title>
|
||||
<polygon fill="yellow" stroke="black" points="393.07,-317.96 181.07,-317.96 181.07,-279.96 393.07,-279.96 393.07,-317.96"/>
|
||||
<text text-anchor="middle" x="287.07" y="-302.76" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 14</text>
|
||||
<text text-anchor="middle" x="287.07" y="-287.76" font-family="Times,serif" font-size="14.00">/dev/video14</text>
|
||||
</g>
|
||||
<!-- n0000003d -->
|
||||
<g id="node16" class="node">
|
||||
<title>n0000003d</title>
|
||||
<polygon fill="yellow" stroke="black" points="701.46,-391.04 489.46,-391.04 489.46,-353.04 701.46,-353.04 701.46,-391.04"/>
|
||||
<text text-anchor="middle" x="595.46" y="-375.84" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 15</text>
|
||||
<text text-anchor="middle" x="595.46" y="-360.84" font-family="Times,serif" font-size="14.00">/dev/video15</text>
|
||||
</g>
|
||||
<!-- n00000041 -->
|
||||
<g id="node17" class="node">
|
||||
<title>n00000041</title>
|
||||
<polygon fill="yellow" stroke="black" points="212.45,-1228.8 0.45,-1228.8 0.45,-1190.8 212.45,-1190.8 212.45,-1228.8"/>
|
||||
<text text-anchor="middle" x="106.45" y="-1213.6" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 16</text>
|
||||
<text text-anchor="middle" x="106.45" y="-1198.6" font-family="Times,serif" font-size="14.00">/dev/video16</text>
|
||||
</g>
|
||||
<!-- n00000045 -->
|
||||
<g id="node18" class="node">
|
||||
<title>n00000045</title>
|
||||
<polygon fill="yellow" stroke="black" points="784.86,-1252.38 572.86,-1252.38 572.86,-1214.38 784.86,-1214.38 784.86,-1252.38"/>
|
||||
<text text-anchor="middle" x="678.86" y="-1237.18" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 17</text>
|
||||
<text text-anchor="middle" x="678.86" y="-1222.18" font-family="Times,serif" font-size="14.00">/dev/video17</text>
|
||||
</g>
|
||||
<!-- n00000049 -->
|
||||
<g id="node19" class="node">
|
||||
<title>n00000049</title>
|
||||
<polygon fill="yellow" stroke="black" points="503.14,-1169.96 291.14,-1169.96 291.14,-1131.96 503.14,-1131.96 503.14,-1169.96"/>
|
||||
<text text-anchor="middle" x="397.14" y="-1154.76" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 18</text>
|
||||
<text text-anchor="middle" x="397.14" y="-1139.76" font-family="Times,serif" font-size="14.00">/dev/video18</text>
|
||||
</g>
|
||||
<!-- n0000004d -->
|
||||
<g id="node20" class="node">
|
||||
<title>n0000004d</title>
|
||||
<polygon fill="yellow" stroke="black" points="492.62,-1319.4 280.62,-1319.4 280.62,-1281.4 492.62,-1281.4 492.62,-1319.4"/>
|
||||
<text text-anchor="middle" x="386.62" y="-1304.2" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 19</text>
|
||||
<text text-anchor="middle" x="386.62" y="-1289.2" font-family="Times,serif" font-size="14.00">/dev/video19</text>
|
||||
</g>
|
||||
<!-- n00000051 -->
|
||||
<g id="node21" class="node">
|
||||
<title>n00000051</title>
|
||||
<polygon fill="yellow" stroke="black" points="680.74,-1464.66 468.74,-1464.66 468.74,-1426.66 680.74,-1426.66 680.74,-1464.66"/>
|
||||
<text text-anchor="middle" x="574.74" y="-1449.46" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 20</text>
|
||||
<text text-anchor="middle" x="574.74" y="-1434.46" font-family="Times,serif" font-size="14.00">/dev/video20</text>
|
||||
</g>
|
||||
<!-- n00000055 -->
|
||||
<g id="node22" class="node">
|
||||
<title>n00000055</title>
|
||||
<polygon fill="yellow" stroke="black" points="302.42,-1452.56 90.42,-1452.56 90.42,-1414.56 302.42,-1414.56 302.42,-1452.56"/>
|
||||
<text text-anchor="middle" x="196.42" y="-1437.36" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 21</text>
|
||||
<text text-anchor="middle" x="196.42" y="-1422.36" font-family="Times,serif" font-size="14.00">/dev/video21</text>
|
||||
</g>
|
||||
<!-- n00000059 -->
|
||||
<g id="node23" class="node">
|
||||
<title>n00000059</title>
|
||||
<polygon fill="yellow" stroke="black" points="319.89,-1018.32 107.89,-1018.32 107.89,-980.32 319.89,-980.32 319.89,-1018.32"/>
|
||||
<text text-anchor="middle" x="213.89" y="-1003.12" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 22</text>
|
||||
<text text-anchor="middle" x="213.89" y="-988.12" font-family="Times,serif" font-size="14.00">/dev/video22</text>
|
||||
</g>
|
||||
<!-- n0000005d -->
|
||||
<g id="node24" class="node">
|
||||
<title>n0000005d</title>
|
||||
<polygon fill="yellow" stroke="black" points="692.62,-1031.39 480.62,-1031.39 480.62,-993.39 692.62,-993.39 692.62,-1031.39"/>
|
||||
<text text-anchor="middle" x="586.62" y="-1016.19" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 23</text>
|
||||
<text text-anchor="middle" x="586.62" y="-1001.19" font-family="Times,serif" font-size="14.00">/dev/video23</text>
|
||||
</g>
|
||||
<!-- n00000061 -->
|
||||
<g id="node25" class="node">
|
||||
<title>n00000061</title>
|
||||
<polygon fill="yellow" stroke="black" points="1122.45,-248.8 910.45,-248.8 910.45,-210.8 1122.45,-210.8 1122.45,-248.8"/>
|
||||
<text text-anchor="middle" x="1016.45" y="-233.6" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 24</text>
|
||||
<text text-anchor="middle" x="1016.45" y="-218.6" font-family="Times,serif" font-size="14.00">/dev/video24</text>
|
||||
</g>
|
||||
<!-- n00000065 -->
|
||||
<g id="node26" class="node">
|
||||
<title>n00000065</title>
|
||||
<polygon fill="yellow" stroke="black" points="1694.86,-272.38 1482.86,-272.38 1482.86,-234.38 1694.86,-234.38 1694.86,-272.38"/>
|
||||
<text text-anchor="middle" x="1588.86" y="-257.18" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 25</text>
|
||||
<text text-anchor="middle" x="1588.86" y="-242.18" font-family="Times,serif" font-size="14.00">/dev/video25</text>
|
||||
</g>
|
||||
<!-- n00000069 -->
|
||||
<g id="node27" class="node">
|
||||
<title>n00000069</title>
|
||||
<polygon fill="yellow" stroke="black" points="1413.14,-189.96 1201.14,-189.96 1201.14,-151.96 1413.14,-151.96 1413.14,-189.96"/>
|
||||
<text text-anchor="middle" x="1307.14" y="-174.76" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 26</text>
|
||||
<text text-anchor="middle" x="1307.14" y="-159.76" font-family="Times,serif" font-size="14.00">/dev/video26</text>
|
||||
</g>
|
||||
<!-- n0000006d -->
|
||||
<g id="node28" class="node">
|
||||
<title>n0000006d</title>
|
||||
<polygon fill="yellow" stroke="black" points="1402.62,-339.4 1190.62,-339.4 1190.62,-301.4 1402.62,-301.4 1402.62,-339.4"/>
|
||||
<text text-anchor="middle" x="1296.62" y="-324.2" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 27</text>
|
||||
<text text-anchor="middle" x="1296.62" y="-309.2" font-family="Times,serif" font-size="14.00">/dev/video27</text>
|
||||
</g>
|
||||
<!-- n00000071 -->
|
||||
<g id="node29" class="node">
|
||||
<title>n00000071</title>
|
||||
<polygon fill="yellow" stroke="black" points="1590.74,-484.66 1378.74,-484.66 1378.74,-446.66 1590.74,-446.66 1590.74,-484.66"/>
|
||||
<text text-anchor="middle" x="1484.74" y="-469.46" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 28</text>
|
||||
<text text-anchor="middle" x="1484.74" y="-454.46" font-family="Times,serif" font-size="14.00">/dev/video28</text>
|
||||
</g>
|
||||
<!-- n00000075 -->
|
||||
<g id="node30" class="node">
|
||||
<title>n00000075</title>
|
||||
<polygon fill="yellow" stroke="black" points="1212.42,-472.56 1000.42,-472.56 1000.42,-434.56 1212.42,-434.56 1212.42,-472.56"/>
|
||||
<text text-anchor="middle" x="1106.42" y="-457.36" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 29</text>
|
||||
<text text-anchor="middle" x="1106.42" y="-442.36" font-family="Times,serif" font-size="14.00">/dev/video29</text>
|
||||
</g>
|
||||
<!-- n00000079 -->
|
||||
<g id="node31" class="node">
|
||||
<title>n00000079</title>
|
||||
<polygon fill="yellow" stroke="black" points="1229.89,-38.32 1017.89,-38.32 1017.89,-0.32 1229.89,-0.32 1229.89,-38.32"/>
|
||||
<text text-anchor="middle" x="1123.89" y="-23.12" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 30</text>
|
||||
<text text-anchor="middle" x="1123.89" y="-8.12" font-family="Times,serif" font-size="14.00">/dev/video30</text>
|
||||
</g>
|
||||
<!-- n0000007d -->
|
||||
<g id="node32" class="node">
|
||||
<title>n0000007d</title>
|
||||
<polygon fill="yellow" stroke="black" points="1602.62,-51.39 1390.62,-51.39 1390.62,-13.39 1602.62,-13.39 1602.62,-51.39"/>
|
||||
<text text-anchor="middle" x="1496.62" y="-36.19" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 31</text>
|
||||
<text text-anchor="middle" x="1496.62" y="-21.19" font-family="Times,serif" font-size="14.00">/dev/video31</text>
|
||||
</g>
|
||||
<!-- n00000081 -->
|
||||
<g id="node33" class="node">
|
||||
<title>n00000081</title>
|
||||
<path fill="green" stroke="black" d="M924.28,-700.28C924.28,-700.28 1108.28,-700.28 1108.28,-700.28 1114.28,-700.28 1120.28,-706.28 1120.28,-712.28 1120.28,-712.28 1120.28,-772.28 1120.28,-772.28 1120.28,-778.28 1114.28,-784.28 1108.28,-784.28 1108.28,-784.28 924.28,-784.28 924.28,-784.28 918.28,-784.28 912.28,-778.28 912.28,-772.28 912.28,-772.28 912.28,-712.28 912.28,-712.28 912.28,-706.28 918.28,-700.28 924.28,-700.28"/>
|
||||
<text text-anchor="middle" x="1016.28" y="-769.08" font-family="Times,serif" font-size="14.00">0</text>
|
||||
<polyline fill="none" stroke="black" points="912.28,-761.28 1120.28,-761.28 "/>
|
||||
<text text-anchor="middle" x="1016.28" y="-746.08" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 0</text>
|
||||
<text text-anchor="middle" x="1016.28" y="-731.08" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev0</text>
|
||||
<polyline fill="none" stroke="black" points="912.28,-723.28 1120.28,-723.28 "/>
|
||||
<text text-anchor="middle" x="925.28" y="-708.08" font-family="Times,serif" font-size="14.00">1</text>
|
||||
<polyline fill="none" stroke="black" points="938.28,-700.28 938.28,-723.28 "/>
|
||||
<text text-anchor="middle" x="951.28" y="-708.08" font-family="Times,serif" font-size="14.00">2</text>
|
||||
<polyline fill="none" stroke="black" points="964.28,-700.28 964.28,-723.28 "/>
|
||||
<text text-anchor="middle" x="977.28" y="-708.08" font-family="Times,serif" font-size="14.00">3</text>
|
||||
<polyline fill="none" stroke="black" points="990.28,-700.28 990.28,-723.28 "/>
|
||||
<text text-anchor="middle" x="1003.28" y="-708.08" font-family="Times,serif" font-size="14.00">4</text>
|
||||
<polyline fill="none" stroke="black" points="1016.28,-700.28 1016.28,-723.28 "/>
|
||||
<text text-anchor="middle" x="1029.28" y="-708.08" font-family="Times,serif" font-size="14.00">5</text>
|
||||
<polyline fill="none" stroke="black" points="1042.28,-700.28 1042.28,-723.28 "/>
|
||||
<text text-anchor="middle" x="1055.28" y="-708.08" font-family="Times,serif" font-size="14.00">6</text>
|
||||
<polyline fill="none" stroke="black" points="1068.28,-700.28 1068.28,-723.28 "/>
|
||||
<text text-anchor="middle" x="1081.28" y="-708.08" font-family="Times,serif" font-size="14.00">7</text>
|
||||
<polyline fill="none" stroke="black" points="1094.28,-700.28 1094.28,-723.28 "/>
|
||||
<text text-anchor="middle" x="1107.28" y="-708.08" font-family="Times,serif" font-size="14.00">8</text>
|
||||
</g>
|
||||
<!-- n00000081->n00000001 -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>n00000081:port1->n00000001</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M912.28,-711.28C912.28,-711.28 880.33,-714.78 843.28,-718.84"/>
|
||||
<polygon fill="black" stroke="black" points="842.81,-715.37 833.25,-719.94 843.57,-722.33 842.81,-715.37"/>
|
||||
</g>
|
||||
<!-- n00000081->n00000005 -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>n00000081:port2->n00000005</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M951.38,-700.28C951.38,-700.28 1086.18,-688.61 1123.48,-697.08 1155.93,-704.45 1158.99,-719.67 1190.39,-730.68 1190.49,-730.71 1190.59,-730.75 1190.69,-730.78"/>
|
||||
<polygon fill="black" stroke="black" points="1189.45,-734.06 1200.05,-733.86 1191.64,-727.41 1189.45,-734.06"/>
|
||||
</g>
|
||||
<!-- n00000081->n00000009 -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>n00000081:port3->n00000009</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M977.28,-700.28C977.28,-700.28 979.31,-698.81 982.45,-696.54"/>
|
||||
<polygon fill="black" stroke="black" points="984.7,-699.23 990.74,-690.53 980.59,-693.56 984.7,-699.23"/>
|
||||
</g>
|
||||
<!-- n00000081->n0000000d -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>n00000081:port4->n0000000d</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1003.38,-700.26C1003.38,-700.26 916.62,-689.8 909.08,-697.08 880.2,-725.01 885.68,-754.82 909.08,-787.48 910.88,-789.99 918.96,-793.59 929.7,-797.47"/>
|
||||
<polygon fill="black" stroke="black" points="928.69,-800.82 939.28,-800.79 930.98,-794.21 928.69,-800.82"/>
|
||||
</g>
|
||||
<!-- n00000081->n00000011 -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>n00000081:port5->n00000011</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1029.19,-700.26C1029.19,-700.26 1115.28,-690.56 1123.48,-697.08 1198.37,-756.64 1190.55,-886.51 1182.64,-944.71"/>
|
||||
<polygon fill="black" stroke="black" points="1179.16,-944.31 1181.18,-954.71 1186.09,-945.32 1179.16,-944.31"/>
|
||||
</g>
|
||||
<!-- n00000081->n00000015 -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>n00000081:port6->n00000015</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1055.18,-700.28C1055.18,-700.28 915.57,-692.2 909.08,-697.08 834.02,-753.51 831.79,-879.34 835.06,-936.56"/>
|
||||
<polygon fill="black" stroke="black" points="831.58,-936.99 835.74,-946.73 838.56,-936.52 831.58,-936.99"/>
|
||||
</g>
|
||||
<!-- n00000081->n00000019 -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>n00000081:port7->n00000019</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1081.28,-700.28C1081.28,-700.28 916.04,-696.54 912.32,-693.67 864.52,-656.73 856.3,-580.22 855.62,-538.2"/>
|
||||
<polygon fill="black" stroke="black" points="859.11,-538.05 855.59,-528.06 852.11,-538.07 859.11,-538.05"/>
|
||||
</g>
|
||||
<!-- n00000081->n0000001d -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>n00000081:port8->n0000001d</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1107.28,-700.28C1107.28,-700.28 1119.29,-696.23 1121.72,-693.67 1159.76,-653.62 1177.38,-589.6 1184.78,-552.46"/>
|
||||
<polygon fill="black" stroke="black" points="1188.29,-552.76 1186.69,-542.29 1181.41,-551.47 1188.29,-552.76"/>
|
||||
</g>
|
||||
<!-- n0000008b -->
|
||||
<g id="node34" class="node">
|
||||
<title>n0000008b</title>
|
||||
<path fill="green" stroke="black" d="M293.1,-532.08C293.1,-532.08 477.1,-532.08 477.1,-532.08 483.1,-532.08 489.1,-538.08 489.1,-544.08 489.1,-544.08 489.1,-604.08 489.1,-604.08 489.1,-610.08 483.1,-616.08 477.1,-616.08 477.1,-616.08 293.1,-616.08 293.1,-616.08 287.1,-616.08 281.1,-610.08 281.1,-604.08 281.1,-604.08 281.1,-544.08 281.1,-544.08 281.1,-538.08 287.1,-532.08 293.1,-532.08"/>
|
||||
<text text-anchor="middle" x="385.1" y="-600.88" font-family="Times,serif" font-size="14.00">0</text>
|
||||
<polyline fill="none" stroke="black" points="281.1,-593.08 489.1,-593.08 "/>
|
||||
<text text-anchor="middle" x="385.1" y="-577.88" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 1</text>
|
||||
<text text-anchor="middle" x="385.1" y="-562.88" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev1</text>
|
||||
<polyline fill="none" stroke="black" points="281.1,-555.08 489.1,-555.08 "/>
|
||||
<text text-anchor="middle" x="294.1" y="-539.88" font-family="Times,serif" font-size="14.00">1</text>
|
||||
<polyline fill="none" stroke="black" points="307.1,-532.08 307.1,-555.08 "/>
|
||||
<text text-anchor="middle" x="320.1" y="-539.88" font-family="Times,serif" font-size="14.00">2</text>
|
||||
<polyline fill="none" stroke="black" points="333.1,-532.08 333.1,-555.08 "/>
|
||||
<text text-anchor="middle" x="346.1" y="-539.88" font-family="Times,serif" font-size="14.00">3</text>
|
||||
<polyline fill="none" stroke="black" points="359.1,-532.08 359.1,-555.08 "/>
|
||||
<text text-anchor="middle" x="372.1" y="-539.88" font-family="Times,serif" font-size="14.00">4</text>
|
||||
<polyline fill="none" stroke="black" points="385.1,-532.08 385.1,-555.08 "/>
|
||||
<text text-anchor="middle" x="398.1" y="-539.88" font-family="Times,serif" font-size="14.00">5</text>
|
||||
<polyline fill="none" stroke="black" points="411.1,-532.08 411.1,-555.08 "/>
|
||||
<text text-anchor="middle" x="424.1" y="-539.88" font-family="Times,serif" font-size="14.00">6</text>
|
||||
<polyline fill="none" stroke="black" points="437.1,-532.08 437.1,-555.08 "/>
|
||||
<text text-anchor="middle" x="450.1" y="-539.88" font-family="Times,serif" font-size="14.00">7</text>
|
||||
<polyline fill="none" stroke="black" points="463.1,-532.08 463.1,-555.08 "/>
|
||||
<text text-anchor="middle" x="476.1" y="-539.88" font-family="Times,serif" font-size="14.00">8</text>
|
||||
</g>
|
||||
<!-- n0000008b->n00000021 -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>n0000008b:port1->n00000021</title>
|
||||
<path fill="none" stroke="black" d="M281.1,-543.08C281.1,-543.08 240.1,-560.51 205.94,-570.26 205.35,-570.43 204.77,-570.59 204.18,-570.76"/>
|
||||
<polygon fill="black" stroke="black" points="203.2,-567.39 194.47,-573.39 205.03,-574.15 203.2,-567.39"/>
|
||||
</g>
|
||||
<!-- n0000008b->n00000025 -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>n0000008b:port2->n00000025</title>
|
||||
<path fill="none" stroke="black" d="M320.2,-532.07C320.2,-532.07 456.9,-514.37 492.3,-528.88 528.42,-543.68 522.86,-571.78 556.11,-594.53"/>
|
||||
<polygon fill="black" stroke="black" points="554.54,-597.67 564.9,-599.88 558.18,-591.69 554.54,-597.67"/>
|
||||
</g>
|
||||
<!-- n0000008b->n00000029 -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>n0000008b:port3->n00000029</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M346.1,-532.08C346.1,-532.08 333.93,-527.96 318.37,-522.71"/>
|
||||
<polygon fill="black" stroke="black" points="319.48,-519.39 308.88,-519.5 317.24,-526.02 319.48,-519.39"/>
|
||||
</g>
|
||||
<!-- n0000008b->n0000002d -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>n0000008b:port4->n0000002d</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M372.19,-532.05C372.19,-532.05 292.97,-514.3 277.9,-528.88 249.01,-556.8 253.16,-587.62 277.9,-619.28 278.34,-619.85 280.33,-620.69 283.45,-621.71"/>
|
||||
<polygon fill="black" stroke="black" points="282.71,-625.14 293.29,-624.58 284.67,-618.42 282.71,-625.14"/>
|
||||
</g>
|
||||
<!-- n0000008b->n00000031 -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>n0000008b:port5->n00000031</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M398,-532.05C398,-532.05 476.28,-515.34 492.3,-528.88 568.49,-593.29 550.55,-729.67 538.14,-789.41"/>
|
||||
<polygon fill="black" stroke="black" points="534.69,-788.79 535.99,-799.31 541.53,-790.28 534.69,-788.79"/>
|
||||
</g>
|
||||
<!-- n0000008b->n00000035 -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>n0000008b:port6->n00000035</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M424,-532.07C424,-532.07 290.37,-518.48 277.9,-528.88 202.27,-591.86 215.34,-725.69 225.66,-785.15"/>
|
||||
<polygon fill="black" stroke="black" points="222.29,-786.14 227.54,-795.35 229.17,-784.88 222.29,-786.14"/>
|
||||
</g>
|
||||
<!-- n0000008b->n00000039 -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>n0000008b:port7->n00000039</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M450.1,-532.08C450.1,-532.08 395.22,-528.13 383.45,-518.65 375.46,-512.21 322.64,-385.46 298.76,-327.47"/>
|
||||
<polygon fill="black" stroke="black" points="301.96,-326.05 294.92,-318.14 295.49,-328.72 301.96,-326.05"/>
|
||||
</g>
|
||||
<!-- n0000008b->n0000003d -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>n0000008b:port8->n0000003d</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M476.1,-532.08C476.1,-532.08 522.37,-522.39 526.85,-518.65 563.15,-488.33 581.38,-434.52 589.6,-401.2"/>
|
||||
<polygon fill="black" stroke="black" points="593.08,-401.69 591.93,-391.16 586.26,-400.11 593.08,-401.69"/>
|
||||
</g>
|
||||
<!-- n00000095 -->
|
||||
<g id="node35" class="node">
|
||||
<title>n00000095</title>
|
||||
<path fill="green" stroke="black" d="M301.38,-1180.11C301.38,-1180.11 485.38,-1180.11 485.38,-1180.11 491.38,-1180.11 497.38,-1186.11 497.38,-1192.11 497.38,-1192.11 497.38,-1252.11 497.38,-1252.11 497.38,-1258.11 491.38,-1264.11 485.38,-1264.11 485.38,-1264.11 301.38,-1264.11 301.38,-1264.11 295.38,-1264.11 289.38,-1258.11 289.38,-1252.11 289.38,-1252.11 289.38,-1192.11 289.38,-1192.11 289.38,-1186.11 295.38,-1180.11 301.38,-1180.11"/>
|
||||
<text text-anchor="middle" x="393.38" y="-1248.91" font-family="Times,serif" font-size="14.00">0</text>
|
||||
<polyline fill="none" stroke="black" points="289.38,-1241.11 497.38,-1241.11 "/>
|
||||
<text text-anchor="middle" x="393.38" y="-1225.91" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 2</text>
|
||||
<text text-anchor="middle" x="393.38" y="-1210.91" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev2</text>
|
||||
<polyline fill="none" stroke="black" points="289.38,-1203.11 497.38,-1203.11 "/>
|
||||
<text text-anchor="middle" x="302.38" y="-1187.91" font-family="Times,serif" font-size="14.00">1</text>
|
||||
<polyline fill="none" stroke="black" points="315.38,-1180.11 315.38,-1203.11 "/>
|
||||
<text text-anchor="middle" x="328.38" y="-1187.91" font-family="Times,serif" font-size="14.00">2</text>
|
||||
<polyline fill="none" stroke="black" points="341.38,-1180.11 341.38,-1203.11 "/>
|
||||
<text text-anchor="middle" x="354.38" y="-1187.91" font-family="Times,serif" font-size="14.00">3</text>
|
||||
<polyline fill="none" stroke="black" points="367.38,-1180.11 367.38,-1203.11 "/>
|
||||
<text text-anchor="middle" x="380.38" y="-1187.91" font-family="Times,serif" font-size="14.00">4</text>
|
||||
<polyline fill="none" stroke="black" points="393.38,-1180.11 393.38,-1203.11 "/>
|
||||
<text text-anchor="middle" x="406.38" y="-1187.91" font-family="Times,serif" font-size="14.00">5</text>
|
||||
<polyline fill="none" stroke="black" points="419.38,-1180.11 419.38,-1203.11 "/>
|
||||
<text text-anchor="middle" x="432.38" y="-1187.91" font-family="Times,serif" font-size="14.00">6</text>
|
||||
<polyline fill="none" stroke="black" points="445.38,-1180.11 445.38,-1203.11 "/>
|
||||
<text text-anchor="middle" x="458.38" y="-1187.91" font-family="Times,serif" font-size="14.00">7</text>
|
||||
<polyline fill="none" stroke="black" points="471.38,-1180.11 471.38,-1203.11 "/>
|
||||
<text text-anchor="middle" x="484.38" y="-1187.91" font-family="Times,serif" font-size="14.00">8</text>
|
||||
</g>
|
||||
<!-- n00000095->n00000041 -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>n00000095:port1->n00000041</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M289.38,-1191.11C289.38,-1191.11 258.94,-1194.22 222.89,-1197.91"/>
|
||||
<polygon fill="black" stroke="black" points="222.19,-1194.46 212.6,-1198.96 222.9,-1201.42 222.19,-1194.46"/>
|
||||
</g>
|
||||
<!-- n00000095->n00000045 -->
|
||||
<g id="edge18" class="edge">
|
||||
<title>n00000095:port2->n00000045</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M328.48,-1180.11C328.48,-1180.11 463.26,-1168.53 500.58,-1176.91 534.02,-1184.43 537.24,-1200.06 569.66,-1211.18 569.76,-1211.22 569.86,-1211.25 569.96,-1211.29"/>
|
||||
<polygon fill="black" stroke="black" points="568.86,-1214.61 579.45,-1214.34 571,-1207.95 568.86,-1214.61"/>
|
||||
</g>
|
||||
<!-- n00000095->n00000049 -->
|
||||
<g id="edge19" class="edge">
|
||||
<title>n00000095:port3->n00000049</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M354.38,-1180.11C354.38,-1180.11 356.8,-1178.46 360.49,-1175.94"/>
|
||||
<polygon fill="black" stroke="black" points="362.56,-1178.77 368.86,-1170.24 358.62,-1172.98 362.56,-1178.77"/>
|
||||
</g>
|
||||
<!-- n00000095->n0000004d -->
|
||||
<g id="edge20" class="edge">
|
||||
<title>n00000095:port4->n0000004d</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M380.47,-1180.09C380.47,-1180.09 293.71,-1169.63 286.18,-1176.91 257.29,-1204.84 262.63,-1234.76 286.18,-1267.31 288.16,-1270.05 297.33,-1273.96 309.38,-1278.13"/>
|
||||
<polygon fill="black" stroke="black" points="308.49,-1281.53 319.09,-1281.36 310.7,-1274.88 308.49,-1281.53"/>
|
||||
</g>
|
||||
<!-- n00000095->n00000051 -->
|
||||
<g id="edge21" class="edge">
|
||||
<title>n00000095:port5->n00000051</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M406.28,-1180.09C406.28,-1180.09 492.13,-1170.7 500.58,-1176.91 576.41,-1232.66 579.83,-1358.79 577.09,-1416.2"/>
|
||||
<polygon fill="black" stroke="black" points="573.59,-1416.23 576.51,-1426.41 580.58,-1416.63 573.59,-1416.23"/>
|
||||
</g>
|
||||
<!-- n00000095->n00000055 -->
|
||||
<g id="edge22" class="edge">
|
||||
<title>n00000095:port6->n00000055</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M432.28,-1180.11C432.28,-1180.11 292.85,-1172.29 286.18,-1176.91 211.26,-1228.86 198.3,-1348.49 196.45,-1404.12"/>
|
||||
<polygon fill="black" stroke="black" points="192.94,-1404.28 196.21,-1414.36 199.94,-1404.44 192.94,-1404.28"/>
|
||||
</g>
|
||||
<!-- n00000095->n00000059 -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>n00000095:port7->n00000059</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M458.38,-1180.11C458.38,-1180.11 291.84,-1175.85 287.94,-1173.16 239.87,-1139.96 222.85,-1068.83 216.94,-1028.6"/>
|
||||
<polygon fill="black" stroke="black" points="220.39,-1028.06 215.6,-1018.61 213.46,-1028.98 220.39,-1028.06"/>
|
||||
</g>
|
||||
<!-- n00000095->n0000005d -->
|
||||
<g id="edge24" class="edge">
|
||||
<title>n00000095:port8->n0000005d</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M484.38,-1180.11C484.38,-1180.11 502.45,-1176.49 506.34,-1173.16 547.25,-1138.2 569.47,-1077.38 579.62,-1041.41"/>
|
||||
<polygon fill="black" stroke="black" points="583.06,-1042.09 582.28,-1031.53 576.3,-1040.27 583.06,-1042.09"/>
|
||||
</g>
|
||||
<!-- n0000009f -->
|
||||
<g id="node36" class="node">
|
||||
<title>n0000009f</title>
|
||||
<path fill="green" stroke="black" d="M1211.38,-200.11C1211.38,-200.11 1395.38,-200.11 1395.38,-200.11 1401.38,-200.11 1407.38,-206.11 1407.38,-212.11 1407.38,-212.11 1407.38,-272.11 1407.38,-272.11 1407.38,-278.11 1401.38,-284.11 1395.38,-284.11 1395.38,-284.11 1211.38,-284.11 1211.38,-284.11 1205.38,-284.11 1199.38,-278.11 1199.38,-272.11 1199.38,-272.11 1199.38,-212.11 1199.38,-212.11 1199.38,-206.11 1205.38,-200.11 1211.38,-200.11"/>
|
||||
<text text-anchor="middle" x="1303.38" y="-268.91" font-family="Times,serif" font-size="14.00">0</text>
|
||||
<polyline fill="none" stroke="black" points="1199.38,-261.11 1407.38,-261.11 "/>
|
||||
<text text-anchor="middle" x="1303.38" y="-245.91" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 3</text>
|
||||
<text text-anchor="middle" x="1303.38" y="-230.91" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev3</text>
|
||||
<polyline fill="none" stroke="black" points="1199.38,-223.11 1407.38,-223.11 "/>
|
||||
<text text-anchor="middle" x="1212.38" y="-207.91" font-family="Times,serif" font-size="14.00">1</text>
|
||||
<polyline fill="none" stroke="black" points="1225.38,-200.11 1225.38,-223.11 "/>
|
||||
<text text-anchor="middle" x="1238.38" y="-207.91" font-family="Times,serif" font-size="14.00">2</text>
|
||||
<polyline fill="none" stroke="black" points="1251.38,-200.11 1251.38,-223.11 "/>
|
||||
<text text-anchor="middle" x="1264.38" y="-207.91" font-family="Times,serif" font-size="14.00">3</text>
|
||||
<polyline fill="none" stroke="black" points="1277.38,-200.11 1277.38,-223.11 "/>
|
||||
<text text-anchor="middle" x="1290.38" y="-207.91" font-family="Times,serif" font-size="14.00">4</text>
|
||||
<polyline fill="none" stroke="black" points="1303.38,-200.11 1303.38,-223.11 "/>
|
||||
<text text-anchor="middle" x="1316.38" y="-207.91" font-family="Times,serif" font-size="14.00">5</text>
|
||||
<polyline fill="none" stroke="black" points="1329.38,-200.11 1329.38,-223.11 "/>
|
||||
<text text-anchor="middle" x="1342.38" y="-207.91" font-family="Times,serif" font-size="14.00">6</text>
|
||||
<polyline fill="none" stroke="black" points="1355.38,-200.11 1355.38,-223.11 "/>
|
||||
<text text-anchor="middle" x="1368.38" y="-207.91" font-family="Times,serif" font-size="14.00">7</text>
|
||||
<polyline fill="none" stroke="black" points="1381.38,-200.11 1381.38,-223.11 "/>
|
||||
<text text-anchor="middle" x="1394.38" y="-207.91" font-family="Times,serif" font-size="14.00">8</text>
|
||||
</g>
|
||||
<!-- n0000009f->n00000061 -->
|
||||
<g id="edge25" class="edge">
|
||||
<title>n0000009f:port1->n00000061</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1199.38,-211.11C1199.38,-211.11 1168.94,-214.22 1132.89,-217.91"/>
|
||||
<polygon fill="black" stroke="black" points="1132.19,-214.46 1122.6,-218.96 1132.9,-221.42 1132.19,-214.46"/>
|
||||
</g>
|
||||
<!-- n0000009f->n00000065 -->
|
||||
<g id="edge26" class="edge">
|
||||
<title>n0000009f:port2->n00000065</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1238.48,-200.11C1238.48,-200.11 1373.26,-188.53 1410.58,-196.91 1444.02,-204.43 1447.24,-220.06 1479.66,-231.18 1479.76,-231.22 1479.86,-231.25 1479.96,-231.29"/>
|
||||
<polygon fill="black" stroke="black" points="1478.86,-234.61 1489.45,-234.34 1481,-227.95 1478.86,-234.61"/>
|
||||
</g>
|
||||
<!-- n0000009f->n00000069 -->
|
||||
<g id="edge27" class="edge">
|
||||
<title>n0000009f:port3->n00000069</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1264.38,-200.11C1264.38,-200.11 1266.8,-198.46 1270.49,-195.94"/>
|
||||
<polygon fill="black" stroke="black" points="1272.56,-198.77 1278.86,-190.24 1268.62,-192.98 1272.56,-198.77"/>
|
||||
</g>
|
||||
<!-- n0000009f->n0000006d -->
|
||||
<g id="edge28" class="edge">
|
||||
<title>n0000009f:port4->n0000006d</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1290.47,-200.09C1290.47,-200.09 1203.71,-189.63 1196.18,-196.91 1167.29,-224.84 1172.63,-254.76 1196.18,-287.31 1198.16,-290.05 1207.33,-293.96 1219.38,-298.13"/>
|
||||
<polygon fill="black" stroke="black" points="1218.49,-301.53 1229.09,-301.36 1220.7,-294.88 1218.49,-301.53"/>
|
||||
</g>
|
||||
<!-- n0000009f->n00000071 -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>n0000009f:port5->n00000071</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1316.28,-200.09C1316.28,-200.09 1402.13,-190.7 1410.58,-196.91 1486.41,-252.66 1489.83,-378.79 1487.09,-436.2"/>
|
||||
<polygon fill="black" stroke="black" points="1483.59,-436.23 1486.51,-446.41 1490.58,-436.63 1483.59,-436.23"/>
|
||||
</g>
|
||||
<!-- n0000009f->n00000075 -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>n0000009f:port6->n00000075</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1342.28,-200.11C1342.28,-200.11 1202.85,-192.29 1196.18,-196.91 1121.26,-248.86 1108.3,-368.49 1106.45,-424.12"/>
|
||||
<polygon fill="black" stroke="black" points="1102.94,-424.28 1106.21,-434.36 1109.94,-424.44 1102.94,-424.28"/>
|
||||
</g>
|
||||
<!-- n0000009f->n00000079 -->
|
||||
<g id="edge31" class="edge">
|
||||
<title>n0000009f:port7->n00000079</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1368.38,-200.11C1368.38,-200.11 1201.84,-195.85 1197.94,-193.16 1149.87,-159.96 1132.85,-88.83 1126.94,-48.6"/>
|
||||
<polygon fill="black" stroke="black" points="1130.39,-48.06 1125.6,-38.61 1123.46,-48.98 1130.39,-48.06"/>
|
||||
</g>
|
||||
<!-- n0000009f->n0000007d -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>n0000009f:port8->n0000007d</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1394.38,-200.11C1394.38,-200.11 1412.45,-196.49 1416.34,-193.16 1457.25,-158.2 1479.47,-97.38 1489.62,-61.41"/>
|
||||
<polygon fill="black" stroke="black" points="1493.06,-62.09 1492.28,-51.53 1486.3,-60.27 1493.06,-62.09"/>
|
||||
</g>
|
||||
<!-- n000000e9 -->
|
||||
<g id="node37" class="node">
|
||||
<title>n000000e9</title>
|
||||
<path fill="green" stroke="black" d="M398.65,-431.45C398.65,-431.45 511.65,-431.45 511.65,-431.45 517.65,-431.45 523.65,-437.45 523.65,-443.45 523.65,-443.45 523.65,-503.45 523.65,-503.45 523.65,-509.45 517.65,-515.45 511.65,-515.45 511.65,-515.45 398.65,-515.45 398.65,-515.45 392.65,-515.45 386.65,-509.45 386.65,-503.45 386.65,-503.45 386.65,-443.45 386.65,-443.45 386.65,-437.45 392.65,-431.45 398.65,-431.45"/>
|
||||
<text text-anchor="middle" x="420.65" y="-500.25" font-family="Times,serif" font-size="14.00">1</text>
|
||||
<polyline fill="none" stroke="black" points="454.65,-492.45 454.65,-515.45 "/>
|
||||
<text text-anchor="middle" x="489.15" y="-500.25" font-family="Times,serif" font-size="14.00">2</text>
|
||||
<polyline fill="none" stroke="black" points="386.65,-492.45 523.65,-492.45 "/>
|
||||
<text text-anchor="middle" x="455.15" y="-477.25" font-family="Times,serif" font-size="14.00">ov2740 4-0036</text>
|
||||
<text text-anchor="middle" x="455.15" y="-462.25" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev4</text>
|
||||
<polyline fill="none" stroke="black" points="386.65,-454.45 523.65,-454.45 "/>
|
||||
<text text-anchor="middle" x="455.15" y="-439.25" font-family="Times,serif" font-size="14.00">0</text>
|
||||
</g>
|
||||
<!-- n000000e9->n0000008b -->
|
||||
<g id="edge33" class="edge">
|
||||
<title>n000000e9:port0->n0000008b:port0</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M386.14,-442.55C386.14,-442.55 361.11,-493.23 383.45,-518.65 391.47,-527.78 484.31,-519.72 492.3,-528.88 508.64,-547.6 499.26,-579.87 493.12,-595.68"/>
|
||||
<polygon fill="black" stroke="black" stroke-width="2" points="489.86,-594.41 489.11,-604.98 496.29,-597.19 489.86,-594.41"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 38 KiB |
@ -1,8 +1,10 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
====================
|
||||
mgb4 sysfs interface
|
||||
====================
|
||||
The mgb4 driver
|
||||
===============
|
||||
|
||||
sysfs interface
|
||||
---------------
|
||||
|
||||
The mgb4 driver provides a sysfs interface, that is used to configure video
|
||||
stream related parameters (some of them must be set properly before the v4l2
|
||||
@ -12,9 +14,8 @@ There are two types of parameters - global / PCI card related, found under
|
||||
``/sys/class/video4linux/videoX/device`` and module specific found under
|
||||
``/sys/class/video4linux/videoX``.
|
||||
|
||||
|
||||
Global (PCI card) parameters
|
||||
============================
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**module_type** (R):
|
||||
Module type.
|
||||
@ -42,9 +43,8 @@ Global (PCI card) parameters
|
||||
|
||||
where each component is a 8b number.
|
||||
|
||||
|
||||
Common FPDL3/GMSL input parameters
|
||||
==================================
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**input_id** (R):
|
||||
Input number ID, zero based.
|
||||
@ -190,9 +190,8 @@ Common FPDL3/GMSL input parameters
|
||||
*Note: This parameter can not be changed while the input v4l2 device is
|
||||
open.*
|
||||
|
||||
|
||||
Common FPDL3/GMSL output parameters
|
||||
===================================
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**output_id** (R):
|
||||
Output number ID, zero based.
|
||||
@ -282,9 +281,8 @@ Common FPDL3/GMSL output parameters
|
||||
Number of video lines between the end of the last valid pixel line (marked
|
||||
by DE=1) and assertion of the VSYNC signal. The default value is 2.
|
||||
|
||||
|
||||
FPDL3 specific input parameters
|
||||
===============================
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**fpdl3_input_width** (RW):
|
||||
Number of deserializer input lines.
|
||||
@ -294,7 +292,7 @@ FPDL3 specific input parameters
|
||||
| 2 - dual
|
||||
|
||||
FPDL3 specific output parameters
|
||||
================================
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**fpdl3_output_width** (RW):
|
||||
Number of serializer output lines.
|
||||
@ -304,7 +302,7 @@ FPDL3 specific output parameters
|
||||
| 2 - dual
|
||||
|
||||
GMSL specific input parameters
|
||||
==============================
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**gmsl_mode** (RW):
|
||||
GMSL speed mode.
|
||||
@ -328,10 +326,8 @@ GMSL specific input parameters
|
||||
| 0 - disabled
|
||||
| 1 - enabled (default)
|
||||
|
||||
|
||||
====================
|
||||
mgb4 mtd partitions
|
||||
====================
|
||||
MTD partitions
|
||||
--------------
|
||||
|
||||
The mgb4 driver creates a MTD device with two partitions:
|
||||
- mgb4-fw.X - FPGA firmware.
|
||||
@ -344,9 +340,8 @@ also have a third partition named *mgb4-flash* available in the system. This
|
||||
partition represents the whole, unpartitioned, card's FLASH memory and one should
|
||||
not fiddle with it...
|
||||
|
||||
====================
|
||||
mgb4 iio (triggers)
|
||||
====================
|
||||
IIO (triggers)
|
||||
--------------
|
||||
|
||||
The mgb4 driver creates an Industrial I/O (IIO) device that provides trigger and
|
||||
signal level status capability. The following scan elements are available:
|
||||
|
@ -16,6 +16,7 @@ Video4Linux (V4L) driver-specific documentation
|
||||
imx
|
||||
imx7
|
||||
ipu3
|
||||
ipu6-isys
|
||||
ivtv
|
||||
mgb4
|
||||
omap3isp
|
||||
|
127
Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml
Normal file
127
Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml
Normal file
@ -0,0 +1,127 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/media/brcm,bcm2835-unicam.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom BCM283x Camera Interface (Unicam)
|
||||
|
||||
maintainers:
|
||||
- Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
|
||||
|
||||
description: |-
|
||||
The Unicam block on BCM283x SoCs is the receiver for either
|
||||
CSI-2 or CCP2 data from image sensors or similar devices.
|
||||
|
||||
The main platform using this SoC is the Raspberry Pi family of boards. On
|
||||
the Pi the VideoCore firmware can also control this hardware block, and
|
||||
driving it from two different processors will cause issues. To avoid this,
|
||||
the firmware checks the device tree configuration during boot. If it finds
|
||||
device tree nodes whose name starts with 'csi' then it will stop the firmware
|
||||
accessing the block, and it can then safely be used via the device tree
|
||||
binding.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: brcm,bcm2835-unicam
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: Unicam block.
|
||||
- description: Clock Manager Image (CMI) block.
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: unicam
|
||||
- const: cmi
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Clock to drive the LP state machine of Unicam.
|
||||
- description: Clock for the VPU (core clock).
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: lp
|
||||
- const: vpu
|
||||
|
||||
power-domains:
|
||||
items:
|
||||
- description: Unicam power domain
|
||||
|
||||
brcm,num-data-lanes:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [ 2, 4 ]
|
||||
description: |
|
||||
Number of CSI-2 data lanes supported by this Unicam instance. The number
|
||||
of data lanes actively used is specified with the data-lanes endpoint
|
||||
property.
|
||||
|
||||
port:
|
||||
$ref: /schemas/graph.yaml#/$defs/port-base
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
endpoint:
|
||||
$ref: /schemas/media/video-interfaces.yaml#
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
bus-type:
|
||||
enum: [ 3, 4 ]
|
||||
|
||||
clock-noncontinuous: true
|
||||
data-lanes: true
|
||||
remote-endpoint: true
|
||||
|
||||
required:
|
||||
- bus-type
|
||||
- data-lanes
|
||||
- remote-endpoint
|
||||
|
||||
required:
|
||||
- endpoint
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- brcm,num-data-lanes
|
||||
- port
|
||||
|
||||
additionalProperties: False
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/bcm2835.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/media/video-interfaces.h>
|
||||
#include <dt-bindings/power/raspberrypi-power.h>
|
||||
|
||||
csi1: csi@7e801000 {
|
||||
compatible = "brcm,bcm2835-unicam";
|
||||
reg = <0x7e801000 0x800>,
|
||||
<0x7e802004 0x4>;
|
||||
reg-names = "unicam", "cmi";
|
||||
interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clocks BCM2835_CLOCK_CAM1>,
|
||||
<&firmware_clocks 4>;
|
||||
clock-names = "lp", "vpu";
|
||||
power-domains = <&power RPI_POWER_DOMAIN_UNICAM1>;
|
||||
brcm,num-data-lanes = <2>;
|
||||
port {
|
||||
csi1_ep: endpoint {
|
||||
remote-endpoint = <&imx219_0>;
|
||||
bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
|
||||
data-lanes = <1 2>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -15,7 +15,7 @@ description: |
|
||||
They include an ISP capable of auto exposure and auto white balance.
|
||||
|
||||
allOf:
|
||||
- $ref: ../video-interface-devices.yaml#
|
||||
- $ref: /schemas/media/video-interface-devices.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -19,7 +19,7 @@ description:
|
||||
either through a parallel interface or through MIPI CSI-2.
|
||||
|
||||
allOf:
|
||||
- $ref: ../video-interface-devices.yaml#
|
||||
- $ref: /schemas/media/video-interface-devices.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -37,31 +37,45 @@ properties:
|
||||
active low.
|
||||
maxItems: 1
|
||||
|
||||
dovdd-supply:
|
||||
DOVDD-supply:
|
||||
description:
|
||||
Definition of the regulator used as interface power supply.
|
||||
|
||||
avdd-supply:
|
||||
AVDD-supply:
|
||||
description:
|
||||
Definition of the regulator used as analog power supply.
|
||||
|
||||
dvdd-supply:
|
||||
DVDD-supply:
|
||||
description:
|
||||
Definition of the regulator used as digital power supply.
|
||||
|
||||
port:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description:
|
||||
A node containing an output port node.
|
||||
$ref: /schemas/graph.yaml#/$defs/port-base
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
endpoint:
|
||||
$ref: /schemas/media/video-interfaces.yaml#
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
link-frequencies: true
|
||||
|
||||
remote-endpoint: true
|
||||
|
||||
required:
|
||||
- link-frequencies
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- dovdd-supply
|
||||
- avdd-supply
|
||||
- dvdd-supply
|
||||
- DOVDD-supply
|
||||
- AVDD-supply
|
||||
- DVDD-supply
|
||||
- reset-gpios
|
||||
- port
|
||||
|
||||
@ -82,13 +96,14 @@ examples:
|
||||
clock-names = "xvclk";
|
||||
reset-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
|
||||
|
||||
dovdd-supply = <&sw2_reg>;
|
||||
dvdd-supply = <&sw2_reg>;
|
||||
avdd-supply = <®_peri_3p15v>;
|
||||
DOVDD-supply = <&sw2_reg>;
|
||||
DVDD-supply = <&sw2_reg>;
|
||||
AVDD-supply = <®_peri_3p15v>;
|
||||
|
||||
port {
|
||||
ov2680_to_mipi: endpoint {
|
||||
remote-endpoint = <&mipi_from_sensor>;
|
||||
link-frequencies = /bits/ 64 <330000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Copyright (c) 2019 MediaTek Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/media/i2c/ov8856.yaml#
|
||||
$id: http://devicetree.org/schemas/media/i2c/ovti,ov8856.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Omnivision OV8856 CMOS Sensor
|
@ -16,7 +16,7 @@ description: |
|
||||
maximum throughput of 1.2Gbps/lane.
|
||||
|
||||
allOf:
|
||||
- $ref: ../video-interface-devices.yaml#
|
||||
- $ref: /schemas/media/video-interface-devices.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -23,6 +23,9 @@ description: |-
|
||||
is treated the same as this as it was the original compatible string.
|
||||
imx290llr is the mono version of the sensor.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/media/video-interface-devices.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
@ -101,7 +104,7 @@ required:
|
||||
- vdddo-supply
|
||||
- port
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@ -18,7 +18,7 @@ description: |-
|
||||
available via CSI-2 serial data output (two or four lanes).
|
||||
|
||||
allOf:
|
||||
- $ref: ../video-interface-devices.yaml#
|
||||
- $ref: /schemas/media/video-interface-devices.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -84,6 +84,7 @@ allOf:
|
||||
properties:
|
||||
port@0:
|
||||
description: MIPI CSI-2 RX
|
||||
port@1: false
|
||||
required:
|
||||
- port@0
|
||||
|
||||
|
@ -31,6 +31,11 @@ properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: AXI DMA engine clock for fetching JPEG bitstream from memory (per)
|
||||
- description: IP bus clock for register access (ipg)
|
||||
|
||||
interrupts:
|
||||
description: |
|
||||
There are 4 slots available in the IP, which the driver may use
|
||||
@ -49,6 +54,7 @@ properties:
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- interrupts
|
||||
- power-domains
|
||||
|
||||
@ -56,12 +62,15 @@ additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/imx8-lpcg.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/firmware/imx/rsrc.h>
|
||||
|
||||
jpegdec: jpegdec@58400000 {
|
||||
compatible = "nxp,imx8qxp-jpgdec";
|
||||
reg = <0x58400000 0x00050000 >;
|
||||
clocks = <&img_jpeg_dec_lpcg IMX_LPCG_CLK_0>,
|
||||
<&img_jpeg_dec_lpcg IMX_LPCG_CLK_4>;
|
||||
interrupts = <GIC_SPI 309 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 310 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 311 IRQ_TYPE_LEVEL_HIGH>,
|
||||
@ -76,6 +85,8 @@ examples:
|
||||
jpegenc: jpegenc@58450000 {
|
||||
compatible = "nxp,imx8qm-jpgenc", "nxp,imx8qxp-jpgenc";
|
||||
reg = <0x58450000 0x00050000 >;
|
||||
clocks = <&img_jpeg_enc_lpcg IMX_LPCG_CLK_0>,
|
||||
<&img_jpeg__lpcg IMX_LPCG_CLK_4>;
|
||||
interrupts = <GIC_SPI 305 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 306 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 307 IRQ_TYPE_LEVEL_HIGH>,
|
||||
|
512
Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml
Normal file
512
Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml
Normal file
@ -0,0 +1,512 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/media/qcom,sc8280xp-camss.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm SC8280XP Camera Subsystem (CAMSS)
|
||||
|
||||
maintainers:
|
||||
- Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||
|
||||
description: |
|
||||
The CAMSS IP is a CSI decoder and ISP present on Qualcomm platforms.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,sc8280xp-camss
|
||||
|
||||
clocks:
|
||||
maxItems: 40
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: camnoc_axi
|
||||
- const: cpas_ahb
|
||||
- const: csiphy0
|
||||
- const: csiphy0_timer
|
||||
- const: csiphy1
|
||||
- const: csiphy1_timer
|
||||
- const: csiphy2
|
||||
- const: csiphy2_timer
|
||||
- const: csiphy3
|
||||
- const: csiphy3_timer
|
||||
- const: vfe0_axi
|
||||
- const: vfe0
|
||||
- const: vfe0_cphy_rx
|
||||
- const: vfe0_csid
|
||||
- const: vfe1_axi
|
||||
- const: vfe1
|
||||
- const: vfe1_cphy_rx
|
||||
- const: vfe1_csid
|
||||
- const: vfe2_axi
|
||||
- const: vfe2
|
||||
- const: vfe2_cphy_rx
|
||||
- const: vfe2_csid
|
||||
- const: vfe3_axi
|
||||
- const: vfe3
|
||||
- const: vfe3_cphy_rx
|
||||
- const: vfe3_csid
|
||||
- const: vfe_lite0
|
||||
- const: vfe_lite0_cphy_rx
|
||||
- const: vfe_lite0_csid
|
||||
- const: vfe_lite1
|
||||
- const: vfe_lite1_cphy_rx
|
||||
- const: vfe_lite1_csid
|
||||
- const: vfe_lite2
|
||||
- const: vfe_lite2_cphy_rx
|
||||
- const: vfe_lite2_csid
|
||||
- const: vfe_lite3
|
||||
- const: vfe_lite3_cphy_rx
|
||||
- const: vfe_lite3_csid
|
||||
- const: gcc_axi_hf
|
||||
- const: gcc_axi_sf
|
||||
|
||||
interrupts:
|
||||
maxItems: 20
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: csid1_lite
|
||||
- const: vfe_lite1
|
||||
- const: csiphy3
|
||||
- const: csid0
|
||||
- const: vfe0
|
||||
- const: csid1
|
||||
- const: vfe1
|
||||
- const: csid0_lite
|
||||
- const: vfe_lite0
|
||||
- const: csiphy0
|
||||
- const: csiphy1
|
||||
- const: csiphy2
|
||||
- const: csid2
|
||||
- const: vfe2
|
||||
- const: csid3_lite
|
||||
- const: csid2_lite
|
||||
- const: vfe_lite3
|
||||
- const: vfe_lite2
|
||||
- const: csid3
|
||||
- const: vfe3
|
||||
|
||||
iommus:
|
||||
maxItems: 16
|
||||
|
||||
interconnects:
|
||||
maxItems: 4
|
||||
|
||||
interconnect-names:
|
||||
items:
|
||||
- const: cam_ahb
|
||||
- const: cam_hf_mnoc
|
||||
- const: cam_sf_mnoc
|
||||
- const: cam_sf_icp_mnoc
|
||||
|
||||
power-domains:
|
||||
items:
|
||||
- description: IFE0 GDSC - Image Front End, Global Distributed Switch Controller.
|
||||
- description: IFE1 GDSC - Image Front End, Global Distributed Switch Controller.
|
||||
- description: IFE2 GDSC - Image Front End, Global Distributed Switch Controller.
|
||||
- description: IFE3 GDSC - Image Front End, Global Distributed Switch Controller.
|
||||
- description: Titan Top GDSC - Titan ISP Block, Global Distributed Switch Controller.
|
||||
|
||||
power-domain-names:
|
||||
items:
|
||||
- const: ife0
|
||||
- const: ife1
|
||||
- const: ife2
|
||||
- const: ife3
|
||||
- const: top
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
description:
|
||||
CSI input ports.
|
||||
|
||||
properties:
|
||||
port@0:
|
||||
$ref: /schemas/graph.yaml#/$defs/port-base
|
||||
unevaluatedProperties: false
|
||||
description:
|
||||
Input port for receiving CSI data from CSIPHY0.
|
||||
|
||||
properties:
|
||||
endpoint:
|
||||
$ref: video-interfaces.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
clock-lanes:
|
||||
maxItems: 1
|
||||
|
||||
data-lanes:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
required:
|
||||
- clock-lanes
|
||||
- data-lanes
|
||||
|
||||
port@1:
|
||||
$ref: /schemas/graph.yaml#/$defs/port-base
|
||||
unevaluatedProperties: false
|
||||
description:
|
||||
Input port for receiving CSI data from CSIPHY1.
|
||||
|
||||
properties:
|
||||
endpoint:
|
||||
$ref: video-interfaces.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
clock-lanes:
|
||||
maxItems: 1
|
||||
|
||||
data-lanes:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
required:
|
||||
- clock-lanes
|
||||
- data-lanes
|
||||
|
||||
port@2:
|
||||
$ref: /schemas/graph.yaml#/$defs/port-base
|
||||
unevaluatedProperties: false
|
||||
description:
|
||||
Input port for receiving CSI data from CSIPHY2.
|
||||
|
||||
properties:
|
||||
endpoint:
|
||||
$ref: video-interfaces.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
clock-lanes:
|
||||
maxItems: 1
|
||||
|
||||
data-lanes:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
required:
|
||||
- clock-lanes
|
||||
- data-lanes
|
||||
|
||||
port@3:
|
||||
$ref: /schemas/graph.yaml#/$defs/port-base
|
||||
unevaluatedProperties: false
|
||||
description:
|
||||
Input port for receiving CSI data from CSIPHY3.
|
||||
|
||||
properties:
|
||||
endpoint:
|
||||
$ref: video-interfaces.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
clock-lanes:
|
||||
maxItems: 1
|
||||
|
||||
data-lanes:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
required:
|
||||
- clock-lanes
|
||||
- data-lanes
|
||||
|
||||
reg:
|
||||
maxItems: 20
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: csiphy2
|
||||
- const: csiphy3
|
||||
- const: csiphy0
|
||||
- const: csiphy1
|
||||
- const: vfe0
|
||||
- const: csid0
|
||||
- const: vfe1
|
||||
- const: csid1
|
||||
- const: vfe2
|
||||
- const: csid2
|
||||
- const: vfe_lite0
|
||||
- const: csid0_lite
|
||||
- const: vfe_lite1
|
||||
- const: csid1_lite
|
||||
- const: vfe_lite2
|
||||
- const: csid2_lite
|
||||
- const: vfe_lite3
|
||||
- const: csid3_lite
|
||||
- const: vfe3
|
||||
- const: csid3
|
||||
|
||||
vdda-phy-supply:
|
||||
description:
|
||||
Phandle to a regulator supply to PHY core block.
|
||||
|
||||
vdda-pll-supply:
|
||||
description:
|
||||
Phandle to 1.8V regulator supply to PHY refclk pll block.
|
||||
|
||||
required:
|
||||
- clock-names
|
||||
- clocks
|
||||
- compatible
|
||||
- interconnects
|
||||
- interconnect-names
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- iommus
|
||||
- power-domains
|
||||
- power-domain-names
|
||||
- reg
|
||||
- reg-names
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/qcom,gcc-sc8280xp.h>
|
||||
#include <dt-bindings/clock/qcom,sc8280xp-camcc.h>
|
||||
#include <dt-bindings/interconnect/qcom,sc8280xp.h>
|
||||
#include <dt-bindings/power/qcom-rpmpd.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
camss: camss@ac5a000 {
|
||||
compatible = "qcom,sc8280xp-camss";
|
||||
|
||||
reg = <0 0x0ac5a000 0 0x2000>,
|
||||
<0 0x0ac5c000 0 0x2000>,
|
||||
<0 0x0ac65000 0 0x2000>,
|
||||
<0 0x0ac67000 0 0x2000>,
|
||||
<0 0x0acaf000 0 0x4000>,
|
||||
<0 0x0acb3000 0 0x1000>,
|
||||
<0 0x0acb6000 0 0x4000>,
|
||||
<0 0x0acba000 0 0x1000>,
|
||||
<0 0x0acbd000 0 0x4000>,
|
||||
<0 0x0acc1000 0 0x1000>,
|
||||
<0 0x0acc4000 0 0x4000>,
|
||||
<0 0x0acc8000 0 0x1000>,
|
||||
<0 0x0accb000 0 0x4000>,
|
||||
<0 0x0accf000 0 0x1000>,
|
||||
<0 0x0acd2000 0 0x4000>,
|
||||
<0 0x0acd6000 0 0x1000>,
|
||||
<0 0x0acd9000 0 0x4000>,
|
||||
<0 0x0acdd000 0 0x1000>,
|
||||
<0 0x0ace0000 0 0x4000>,
|
||||
<0 0x0ace4000 0 0x1000>;
|
||||
|
||||
reg-names = "csiphy2",
|
||||
"csiphy3",
|
||||
"csiphy0",
|
||||
"csiphy1",
|
||||
"vfe0",
|
||||
"csid0",
|
||||
"vfe1",
|
||||
"csid1",
|
||||
"vfe2",
|
||||
"csid2",
|
||||
"vfe_lite0",
|
||||
"csid0_lite",
|
||||
"vfe_lite1",
|
||||
"csid1_lite",
|
||||
"vfe_lite2",
|
||||
"csid2_lite",
|
||||
"vfe_lite3",
|
||||
"csid3_lite",
|
||||
"vfe3",
|
||||
"csid3";
|
||||
|
||||
vdda-phy-supply = <&vreg_l6d>;
|
||||
vdda-pll-supply = <&vreg_l4d>;
|
||||
|
||||
interrupts = <GIC_SPI 359 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 360 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 448 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 464 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 465 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 466 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 467 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 468 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 469 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 477 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 478 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 479 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 640 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 641 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 758 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 759 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 760 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 761 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 762 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 764 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
interrupt-names = "csid1_lite",
|
||||
"vfe_lite1",
|
||||
"csiphy3",
|
||||
"csid0",
|
||||
"vfe0",
|
||||
"csid1",
|
||||
"vfe1",
|
||||
"csid0_lite",
|
||||
"vfe_lite0",
|
||||
"csiphy0",
|
||||
"csiphy1",
|
||||
"csiphy2",
|
||||
"csid2",
|
||||
"vfe2",
|
||||
"csid3_lite",
|
||||
"csid2_lite",
|
||||
"vfe_lite3",
|
||||
"vfe_lite2",
|
||||
"csid3",
|
||||
"vfe3";
|
||||
|
||||
power-domains = <&camcc IFE_0_GDSC>,
|
||||
<&camcc IFE_1_GDSC>,
|
||||
<&camcc IFE_2_GDSC>,
|
||||
<&camcc IFE_3_GDSC>,
|
||||
<&camcc TITAN_TOP_GDSC>;
|
||||
|
||||
power-domain-names = "ife0",
|
||||
"ife1",
|
||||
"ife2",
|
||||
"ife3",
|
||||
"top";
|
||||
|
||||
clocks = <&camcc CAMCC_CAMNOC_AXI_CLK>,
|
||||
<&camcc CAMCC_CPAS_AHB_CLK>,
|
||||
<&camcc CAMCC_CSIPHY0_CLK>,
|
||||
<&camcc CAMCC_CSI0PHYTIMER_CLK>,
|
||||
<&camcc CAMCC_CSIPHY1_CLK>,
|
||||
<&camcc CAMCC_CSI1PHYTIMER_CLK>,
|
||||
<&camcc CAMCC_CSIPHY2_CLK>,
|
||||
<&camcc CAMCC_CSI2PHYTIMER_CLK>,
|
||||
<&camcc CAMCC_CSIPHY3_CLK>,
|
||||
<&camcc CAMCC_CSI3PHYTIMER_CLK>,
|
||||
<&camcc CAMCC_IFE_0_AXI_CLK>,
|
||||
<&camcc CAMCC_IFE_0_CLK>,
|
||||
<&camcc CAMCC_IFE_0_CPHY_RX_CLK>,
|
||||
<&camcc CAMCC_IFE_0_CSID_CLK>,
|
||||
<&camcc CAMCC_IFE_1_AXI_CLK>,
|
||||
<&camcc CAMCC_IFE_1_CLK>,
|
||||
<&camcc CAMCC_IFE_1_CPHY_RX_CLK>,
|
||||
<&camcc CAMCC_IFE_1_CSID_CLK>,
|
||||
<&camcc CAMCC_IFE_2_AXI_CLK>,
|
||||
<&camcc CAMCC_IFE_2_CLK>,
|
||||
<&camcc CAMCC_IFE_2_CPHY_RX_CLK>,
|
||||
<&camcc CAMCC_IFE_2_CSID_CLK>,
|
||||
<&camcc CAMCC_IFE_3_AXI_CLK>,
|
||||
<&camcc CAMCC_IFE_3_CLK>,
|
||||
<&camcc CAMCC_IFE_3_CPHY_RX_CLK>,
|
||||
<&camcc CAMCC_IFE_3_CSID_CLK>,
|
||||
<&camcc CAMCC_IFE_LITE_0_CLK>,
|
||||
<&camcc CAMCC_IFE_LITE_0_CPHY_RX_CLK>,
|
||||
<&camcc CAMCC_IFE_LITE_0_CSID_CLK>,
|
||||
<&camcc CAMCC_IFE_LITE_1_CLK>,
|
||||
<&camcc CAMCC_IFE_LITE_1_CPHY_RX_CLK>,
|
||||
<&camcc CAMCC_IFE_LITE_1_CSID_CLK>,
|
||||
<&camcc CAMCC_IFE_LITE_2_CLK>,
|
||||
<&camcc CAMCC_IFE_LITE_2_CPHY_RX_CLK>,
|
||||
<&camcc CAMCC_IFE_LITE_2_CSID_CLK>,
|
||||
<&camcc CAMCC_IFE_LITE_3_CLK>,
|
||||
<&camcc CAMCC_IFE_LITE_3_CPHY_RX_CLK>,
|
||||
<&camcc CAMCC_IFE_LITE_3_CSID_CLK>,
|
||||
<&gcc GCC_CAMERA_HF_AXI_CLK>,
|
||||
<&gcc GCC_CAMERA_SF_AXI_CLK>;
|
||||
|
||||
clock-names = "camnoc_axi",
|
||||
"cpas_ahb",
|
||||
"csiphy0",
|
||||
"csiphy0_timer",
|
||||
"csiphy1",
|
||||
"csiphy1_timer",
|
||||
"csiphy2",
|
||||
"csiphy2_timer",
|
||||
"csiphy3",
|
||||
"csiphy3_timer",
|
||||
"vfe0_axi",
|
||||
"vfe0",
|
||||
"vfe0_cphy_rx",
|
||||
"vfe0_csid",
|
||||
"vfe1_axi",
|
||||
"vfe1",
|
||||
"vfe1_cphy_rx",
|
||||
"vfe1_csid",
|
||||
"vfe2_axi",
|
||||
"vfe2",
|
||||
"vfe2_cphy_rx",
|
||||
"vfe2_csid",
|
||||
"vfe3_axi",
|
||||
"vfe3",
|
||||
"vfe3_cphy_rx",
|
||||
"vfe3_csid",
|
||||
"vfe_lite0",
|
||||
"vfe_lite0_cphy_rx",
|
||||
"vfe_lite0_csid",
|
||||
"vfe_lite1",
|
||||
"vfe_lite1_cphy_rx",
|
||||
"vfe_lite1_csid",
|
||||
"vfe_lite2",
|
||||
"vfe_lite2_cphy_rx",
|
||||
"vfe_lite2_csid",
|
||||
"vfe_lite3",
|
||||
"vfe_lite3_cphy_rx",
|
||||
"vfe_lite3_csid",
|
||||
"gcc_axi_hf",
|
||||
"gcc_axi_sf";
|
||||
|
||||
|
||||
iommus = <&apps_smmu 0x2000 0x4e0>,
|
||||
<&apps_smmu 0x2020 0x4e0>,
|
||||
<&apps_smmu 0x2040 0x4e0>,
|
||||
<&apps_smmu 0x2060 0x4e0>,
|
||||
<&apps_smmu 0x2080 0x4e0>,
|
||||
<&apps_smmu 0x20e0 0x4e0>,
|
||||
<&apps_smmu 0x20c0 0x4e0>,
|
||||
<&apps_smmu 0x20a0 0x4e0>,
|
||||
<&apps_smmu 0x2400 0x4e0>,
|
||||
<&apps_smmu 0x2420 0x4e0>,
|
||||
<&apps_smmu 0x2440 0x4e0>,
|
||||
<&apps_smmu 0x2460 0x4e0>,
|
||||
<&apps_smmu 0x2480 0x4e0>,
|
||||
<&apps_smmu 0x24e0 0x4e0>,
|
||||
<&apps_smmu 0x24c0 0x4e0>,
|
||||
<&apps_smmu 0x24a0 0x4e0>;
|
||||
|
||||
interconnects = <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_CAMERA_CFG 0>,
|
||||
<&mmss_noc MASTER_CAMNOC_HF 0 &mc_virt SLAVE_EBI1 0>,
|
||||
<&mmss_noc MASTER_CAMNOC_SF 0 &mc_virt SLAVE_EBI1 0>,
|
||||
<&mmss_noc MASTER_CAMNOC_ICP 0 &mc_virt SLAVE_EBI1 0>;
|
||||
interconnect-names = "cam_ahb",
|
||||
"cam_hf_mnoc",
|
||||
"cam_sf_mnoc",
|
||||
"cam_sf_icp_mnoc";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
csiphy_ep0: endpoint@0 {
|
||||
reg = <0>;
|
||||
clock-lanes = <7>;
|
||||
data-lanes = <0 1>;
|
||||
remote-endpoint = <&sensor_ep>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -26,6 +26,7 @@ Video4Linux (V4L) drivers
|
||||
vimc-devel
|
||||
zoran
|
||||
ccs/ccs
|
||||
ipu6
|
||||
|
||||
|
||||
Digital TV drivers
|
||||
|
205
Documentation/driver-api/media/drivers/ipu6.rst
Normal file
205
Documentation/driver-api/media/drivers/ipu6.rst
Normal file
@ -0,0 +1,205 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
==================
|
||||
Intel IPU6 Driver
|
||||
==================
|
||||
|
||||
Author: Bingbu Cao <bingbu.cao@intel.com>
|
||||
|
||||
Overview
|
||||
=========
|
||||
|
||||
Intel IPU6 is the sixth generation of Intel Image Processing Unit used in some
|
||||
Intel Chipsets such as Tiger Lake, Jasper Lake, Alder Lake, Raptor Lake and
|
||||
Meteor Lake. IPU6 consists of two major systems: Input System (ISYS) and
|
||||
Processing System (PSYS). IPU6 are visible on the PCI bus as a single device, it
|
||||
can be found by ``lspci``:
|
||||
|
||||
``0000:00:05.0 Multimedia controller: Intel Corporation Device xxxx (rev xx)``
|
||||
|
||||
IPU6 has a 16 MB BAR in PCI configuration Space for MMIO registers which is
|
||||
visible for driver.
|
||||
|
||||
Buttress
|
||||
=========
|
||||
|
||||
The IPU6 is connecting to the system fabric with Buttress which is enabling host
|
||||
driver to control the IPU6, it also allows IPU6 access the system memory to
|
||||
store and load frame pixel streams and any other metadata.
|
||||
|
||||
Buttress mainly manages several system functionalities: power management,
|
||||
interrupt handling, firmware authentication and global timer sync.
|
||||
|
||||
ISYS and PSYS Power flow
|
||||
------------------------
|
||||
|
||||
IPU6 driver initialize the ISYS and PSYS power up or down request by setting the
|
||||
Buttress frequency control register for ISYS and PSYS
|
||||
(``IPU6_BUTTRESS_REG_IS_FREQ_CTL`` and ``IPU6_BUTTRESS_REG_PS_FREQ_CTL``) in
|
||||
function:
|
||||
|
||||
.. c:function:: int ipu6_buttress_power(...)
|
||||
|
||||
Buttress forwards the request to Punit, after Punit execute the power up flow,
|
||||
Buttress indicates driver that ISYS or PSYS is powered up by updating the power
|
||||
status registers.
|
||||
|
||||
.. Note:: ISYS power up needs take place prior to PSYS power up, ISYS power down
|
||||
needs take place after PSYS power down due to hardware limitation.
|
||||
|
||||
Interrupt
|
||||
---------
|
||||
|
||||
IPU6 interrupt can be generated as MSI or INTA, interrupt will be triggered when
|
||||
ISYS, PSYS, Buttress event or error happen, driver can get the interrupt cause
|
||||
by reading the interrupt status register ``BUTTRESS_REG_ISR_STATUS``, driver
|
||||
clears the irq status and then calls specific ISYS or PSYS irq handler.
|
||||
|
||||
.. c:function:: irqreturn_t ipu6_buttress_isr(int irq, ...)
|
||||
|
||||
Security and firmware authentication
|
||||
-------------------------------------
|
||||
|
||||
To address the IPU6 firmware security concerns, the IPU6 firmware needs to
|
||||
undergo an authentication process before it is allowed to executed on the IPU6
|
||||
internal processors. The IPU6 driver will work with Converged Security Engine
|
||||
(CSE) to complete authentication process. The CSE is responsible of
|
||||
authenticating the IPU6 firmware. The authenticated firmware binary is copied
|
||||
into an isolated memory region. Firmware authentication process is implemented
|
||||
by CSE following an IPC handshake with the IPU6 driver. There are some Buttress
|
||||
registers used by the CSE and the IPU6 driver to communicate with each other via
|
||||
IPC.
|
||||
|
||||
.. c:function:: int ipu6_buttress_authenticate(...)
|
||||
|
||||
Global timer sync
|
||||
-----------------
|
||||
|
||||
The IPU6 driver initiates a Hammock Harbor synchronization flow each time it
|
||||
starts camera operation. The IPU6 will synchronizes an internal counter in the
|
||||
Buttress with a copy of the SoC time, this counter maintains the up-to-date time
|
||||
until camera operation is stopped. The IPU6 driver can use this time counter to
|
||||
calibrate the timestamp based on the timestamp in response event from firmware.
|
||||
|
||||
.. c:function:: int ipu6_buttress_start_tsc_sync(...)
|
||||
|
||||
DMA and MMU
|
||||
============
|
||||
|
||||
The IPU6 has its own scalar processor where the firmware run at and an internal
|
||||
32-bit virtual address space. The IPU6 has MMU address translation hardware to
|
||||
allow that scalar processors to access the internal memory and external system
|
||||
memory through IPU6 virtual address. The address translation is based on two
|
||||
levels of page lookup tables stored in system memory which are maintained by the
|
||||
IPU6 driver. The IPU6 driver sets the level-1 page table base address to MMU
|
||||
register and allows MMU to perform page table lookups.
|
||||
|
||||
The IPU6 driver exports its own DMA operations. The IPU6 driver will update the
|
||||
page table entries for each DMA operation and invalidate the MMU TLB after each
|
||||
unmap and free.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
const struct dma_map_ops ipu6_dma_ops = {
|
||||
.alloc = ipu6_dma_alloc,
|
||||
.free = ipu6_dma_free,
|
||||
.mmap = ipu6_dma_mmap,
|
||||
.map_sg = ipu6_dma_map_sg,
|
||||
.unmap_sg = ipu6_dma_unmap_sg,
|
||||
...
|
||||
};
|
||||
|
||||
.. Note:: IPU6 MMU works behind IOMMU so for each IPU6 DMA ops, driver will call
|
||||
generic PCI DMA ops to ask IOMMU to do the additional mapping if VT-d
|
||||
enabled.
|
||||
|
||||
Firmware file format
|
||||
====================
|
||||
|
||||
The IPU6 firmware is in Code Partition Directory (CPD) file format. The CPD
|
||||
firmware contains a CPD header, several CPD entries and components. The CPD
|
||||
component includes 3 entries - manifest, metadata and module data. Manifest and
|
||||
metadata are defined by CSE and used by CSE for authentication. Module data is
|
||||
specific to IPU6 which holds the binary data of firmware called package
|
||||
directory. The IPU6 driver (``ipu6-cpd.c`` in particular) parses and validates
|
||||
the CPD firmware file and gets the package directory binary data of the IPU6
|
||||
firmware, copies it to specific DMA buffer and sets its base address to Buttress
|
||||
``FW_SOURCE_BASE`` register. Finally the CSE will do authentication for this
|
||||
firmware binary.
|
||||
|
||||
|
||||
Syscom interface
|
||||
================
|
||||
|
||||
The IPU6 driver communicates with firmware via the Syscom ABI. Syscom is an
|
||||
inter-processor communication mechanism between the IPU scalar processors and
|
||||
the CPU. There are a number of resources shared between firmware and software.
|
||||
A system memory region where the message queues reside, firmware can access the
|
||||
memory region via the IPU MMU. The Syscom queues are FIFO fixed depth queues
|
||||
with a configurable number of tokens (messages). There are also common IPU6 MMIO
|
||||
registers where the queue read and write indices reside. Software and firmware
|
||||
function as producer and consumer of tokens in the queues and update the write
|
||||
and read indices separately when sending or receiving each message.
|
||||
|
||||
The IPU6 driver must prepare and configure the number of input and output
|
||||
queues, configure the count of tokens per queue and the size of per token before
|
||||
initiating and starting the communication with firmware. Firmware and software
|
||||
must use same configurations. The IPU6 Buttress has a number of firmware boot
|
||||
parameter registers which can be used to store the address of configuration and
|
||||
initialise the Syscom state, then driver can request firmware to start and run via
|
||||
setting the scalar processor control status register.
|
||||
|
||||
Input System
|
||||
============
|
||||
|
||||
IPU6 input system consists of MIPI D-PHY and several CSI-2 receivers. It can
|
||||
capture image pixel data from camera sensors or other MIPI CSI-2 output devices.
|
||||
|
||||
D-PHYs and CSI-2 ports lane mapping
|
||||
-----------------------------------
|
||||
|
||||
The IPU6 integrates different D-PHY IPs on different SoCs, on Tiger Lake and
|
||||
Alder Lake, IPU6 integrates MCD10 D-PHY, IPU6SE on Jasper Lake integrates JSL
|
||||
D-PHY and IPU6EP on Meteor Lake integrates a Synopsys DWC D-PHY. There is an
|
||||
adaptional layer between D-PHY and CSI-2 receiver controller which includes port
|
||||
configuration, PHY wrapper or private test interfaces for D-PHY. There are 3
|
||||
D-PHY drivers ``ipu6-isys-mcd-phy.c``, ``ipu6-isys-jsl-phy.c`` and
|
||||
``ipu6-isys-dwc-phy.c`` program the above 3 D-PHYs in IPU6.
|
||||
|
||||
Different IPU6 versions have different D-PHY lanes mappings, On Tiger Lake,
|
||||
there are 12 data lanes and 8 clock lanes, IPU6 support maximum 8 CSI-2 ports,
|
||||
see the PPI mmapping in ``ipu6-isys-mcd-phy.c`` for more information. On Jasper
|
||||
Lake and Alder Lake, D-PHY has 8 data lanes and 4 clock lanes, the IPU6 supports
|
||||
maximum 4 CSI-2 ports. For Meteor Lake, D-PHY has 12 data lanes and 6 clock
|
||||
lanes so IPU6 support maximum 6 CSI-2 ports.
|
||||
|
||||
.. Note:: Each pair of CSI-2 two ports is a single unit that can share the data
|
||||
lanes. For example, for CSI-2 port 0 and 1, CSI-2 port 0 support
|
||||
maximum 4 data lanes, CSI-2 port 1 support maximum 2 data lanes, CSI-2
|
||||
port 0 with 2 data lanes can work together with CSI-2 port 1 with 2
|
||||
data lanes. If trying to use CSI-2 port 0 with 4 lanes, CSI-2 port 1
|
||||
will not be available as the 4 data lanes are shared by CSI-2 port 0
|
||||
and 1. The same applies to CSI ports 2/3, 4/5 and 7/8.
|
||||
|
||||
ISYS firmware ABIs
|
||||
------------------
|
||||
|
||||
The IPU6 firmware implements a series of ABIs for software access. In general,
|
||||
software firstly prepares the stream configuration ``struct
|
||||
ipu6_fw_isys_stream_cfg_data_abi`` and sends the configuration to firmware via
|
||||
sending ``STREAM_OPEN`` command. Stream configuration includes input pins and
|
||||
output pins, input pin ``struct ipu6_fw_isys_input_pin_info_abi`` defines the
|
||||
resolution and data type of input source, output pin ``struct
|
||||
ipu6_fw_isys_output_pin_info_abi`` defines the output resolution, stride and
|
||||
frame format, etc.
|
||||
|
||||
Once the driver gets the interrupt from firmware that indicates stream open
|
||||
successfully, the driver will send the ``STREAM_START`` and ``STREAM_CAPTURE``
|
||||
command to request firmware to start capturing image frames. ``STREAM_CAPTURE``
|
||||
command queues the buffers to firmware with ``struct
|
||||
ipu6_fw_isys_frame_buff_set``, software then waits for the interrupt and
|
||||
response from firmware, ``PIN_DATA_READY`` means a buffer is ready on a specific
|
||||
output pin and then software can return the buffer to user.
|
||||
|
||||
.. Note:: See :ref:`Examples<ipu6_isys_capture_examples>` about how to do
|
||||
capture by IPU6 ISYS driver.
|
@ -70,5 +70,5 @@ include:
|
||||
``ENOMEM``
|
||||
Insufficient kernel memory was available.
|
||||
|
||||
``ENXIO``
|
||||
No device corresponding to this device special file exists.
|
||||
``ENODEV``
|
||||
Device not found or was removed.
|
||||
|
@ -91,7 +91,7 @@ appropriately.
|
||||
- The caller has no permission to access the device.
|
||||
|
||||
- - ``EBUSY``
|
||||
- The the device driver is already in use.
|
||||
- The device driver is already in use.
|
||||
|
||||
- - ``EMFILE``
|
||||
- The process already has the maximum number of files open.
|
||||
|
@ -25,6 +25,13 @@ Glossary
|
||||
|
||||
See :ref:`cec`.
|
||||
|
||||
Data Unit
|
||||
|
||||
Unit of data transported by a bus. On parallel buses, the data unit
|
||||
consists of one or more related samples while on serial buses the data
|
||||
unit is logical. If the data unit is image data, it may also be called a
|
||||
pixel.
|
||||
|
||||
Device Driver
|
||||
Part of the Linux Kernel that implements support for a hardware
|
||||
component.
|
||||
@ -173,6 +180,11 @@ Glossary
|
||||
An integrated circuit that integrates all components of a computer
|
||||
or other electronic systems.
|
||||
|
||||
Stream
|
||||
A distinct flow of data (image data or metadata) from an initial source
|
||||
to a final sink. The initial source may be e.g. an image sensor and the
|
||||
final sink e.g. a memory buffer.
|
||||
|
||||
V4L2 API
|
||||
**V4L2 userspace API**
|
||||
|
||||
|
@ -47,6 +47,12 @@ 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.
|
||||
|
||||
Devices that capture metadata by line have the struct v4l2_fmtdesc
|
||||
``V4L2_FMT_FLAG_META_LINE_BASED`` flag set for :c:func:`VIDIOC_ENUM_FMT`. Such
|
||||
devices can typically also :ref:`capture image data <capture>`. This primarily
|
||||
involves devices that receive the data from a different devices such as a camera
|
||||
sensor.
|
||||
|
||||
.. c:type:: v4l2_meta_format
|
||||
|
||||
.. tabularcolumns:: |p{1.4cm}|p{2.4cm}|p{13.5cm}|
|
||||
@ -65,3 +71,18 @@ to 0.
|
||||
- ``buffersize``
|
||||
- Maximum buffer size in bytes required for data. The value is set by the
|
||||
driver.
|
||||
* - __u32
|
||||
- ``width``
|
||||
- Width of a line of metadata in Data Units. Valid when
|
||||
:c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is set,
|
||||
otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`.
|
||||
* - __u32
|
||||
- ``height``
|
||||
- Number of rows of metadata. Valid when :c:type`v4l2_fmtdesc` flag
|
||||
``V4L2_FMT_FLAG_META_LINE_BASED`` is set, otherwise zero. See
|
||||
:c:func:`VIDIOC_ENUM_FMT`.
|
||||
* - __u32
|
||||
- ``bytesperline``
|
||||
- Offset in bytes between the beginning of two consecutive lines. Valid
|
||||
when :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is
|
||||
set, otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`.
|
||||
|
@ -506,6 +506,8 @@ source pads.
|
||||
|
||||
subdev-formats
|
||||
|
||||
.. _subdev-routing:
|
||||
|
||||
Streams, multiplexed media pads and internal routing
|
||||
----------------------------------------------------
|
||||
|
||||
@ -527,9 +529,9 @@ the its sink pad and allows to route them individually to one of its source
|
||||
pads.
|
||||
|
||||
Subdevice drivers that support multiplexed streams are compatible with
|
||||
non-multiplexed subdev drivers, but, of course, require a routing configuration
|
||||
where the link between those two types of drivers contains only a single
|
||||
stream.
|
||||
non-multiplexed subdev drivers. However, if the driver at the sink end of a link
|
||||
does not support streams, then only stream 0 of source end may be captured.
|
||||
There may be additional limitations specific to the sink device.
|
||||
|
||||
Understanding streams
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -570,6 +572,29 @@ Any configurations of a stream within a pad, such as format or selections,
|
||||
are independent of similar configurations on other streams. This is
|
||||
subject to change in the future.
|
||||
|
||||
Device types and routing setup
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Different kinds of sub-devices have differing behaviour for route activation,
|
||||
depending on the hardware. In all cases, however, only routes that have the
|
||||
``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag set are active.
|
||||
|
||||
Devices generating the streams may allow enabling and disabling some of the
|
||||
routes or have a fixed routing configuration. If the routes can be disabled, not
|
||||
declaring the routes (or declaring them without
|
||||
``VIDIOC_SUBDEV_STREAM_FL_ACTIVE`` flag set) in ``VIDIOC_SUBDEV_S_ROUTING`` will
|
||||
disable the routes. ``VIDIOC_SUBDEV_S_ROUTING`` will still return such routes
|
||||
back to the user in the routes array, with the ``V4L2_SUBDEV_STREAM_FL_ACTIVE``
|
||||
flag unset.
|
||||
|
||||
Devices transporting the streams almost always have more configurability with
|
||||
respect to routing. Typically any route between the sub-device's sink and source
|
||||
pads is possible, and multiple routes (usually up to certain limited number) may
|
||||
be active simultaneously. For such devices, no routes are created by the driver
|
||||
and user-created routes are fully replaced when ``VIDIOC_SUBDEV_S_ROUTING`` is
|
||||
called on the sub-device. Such newly created routes have the device's default
|
||||
configuration for format and selection rectangles.
|
||||
|
||||
Configuring streams
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -65,8 +65,8 @@ EBUSY
|
||||
The driver does not support multiple opens and the device is already
|
||||
in use.
|
||||
|
||||
ENXIO
|
||||
No device corresponding to this device special file exists.
|
||||
ENODEV
|
||||
Device not found or was removed.
|
||||
|
||||
ENOMEM
|
||||
Not enough kernel memory was available to complete the request.
|
||||
|
@ -13,9 +13,10 @@ These formats are used for the :ref:`metadata` interface only.
|
||||
:maxdepth: 1
|
||||
|
||||
metafmt-d4xx
|
||||
metafmt-generic
|
||||
metafmt-intel-ipu3
|
||||
metafmt-rkisp1
|
||||
metafmt-uvc
|
||||
metafmt-vivid
|
||||
metafmt-vsp1-hgo
|
||||
metafmt-vsp1-hgt
|
||||
metafmt-vivid
|
||||
|
340
Documentation/userspace-api/media/v4l/metafmt-generic.rst
Normal file
340
Documentation/userspace-api/media/v4l/metafmt-generic.rst
Normal file
@ -0,0 +1,340 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later
|
||||
|
||||
********************************************************************************************************************************************************************************************************************************************************************************
|
||||
V4L2_META_FMT_GENERIC_8 ('MET8'), V4L2_META_FMT_GENERIC_CSI2_10 ('MC1A'), V4L2_META_FMT_GENERIC_CSI2_12 ('MC1C'), V4L2_META_FMT_GENERIC_CSI2_14 ('MC1E'), V4L2_META_FMT_GENERIC_CSI2_16 ('MC1G'), V4L2_META_FMT_GENERIC_CSI2_20 ('MC1K'), V4L2_META_FMT_GENERIC_CSI2_24 ('MC1O')
|
||||
********************************************************************************************************************************************************************************************************************************************************************************
|
||||
|
||||
|
||||
Generic line-based metadata formats
|
||||
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
These generic line-based metadata formats define the memory layout of the data
|
||||
without defining the format or meaning of the metadata itself.
|
||||
|
||||
.. _v4l2-meta-fmt-generic-8:
|
||||
|
||||
V4L2_META_FMT_GENERIC_8
|
||||
-----------------------
|
||||
|
||||
The V4L2_META_FMT_GENERIC_8 format is a plain 8-bit metadata format. This format
|
||||
is used on CSI-2 for 8 bits per :term:`Data Unit`.
|
||||
|
||||
Additionally it is used for 16 bits per Data Unit when two bytes of metadata are
|
||||
packed into one 16-bit Data Unit. Otherwise the 16 bits per pixel dataformat is
|
||||
:ref:`V4L2_META_FMT_GENERIC_CSI2_16 <v4l2-meta-fmt-generic-csi2-16>`.
|
||||
|
||||
**Byte Order Of V4L2_META_FMT_GENERIC_8.**
|
||||
Each cell is one byte. "M" denotes a byte of metadata.
|
||||
|
||||
.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|
|
||||
|
||||
.. flat-table:: Sample 4x2 Metadata Frame
|
||||
:header-rows: 0
|
||||
:stub-columns: 0
|
||||
:widths: 12 8 8 8 8
|
||||
|
||||
* - start + 0:
|
||||
- M\ :sub:`00`
|
||||
- M\ :sub:`10`
|
||||
- M\ :sub:`20`
|
||||
- M\ :sub:`30`
|
||||
* - start + 4:
|
||||
- M\ :sub:`01`
|
||||
- M\ :sub:`11`
|
||||
- M\ :sub:`21`
|
||||
- M\ :sub:`31`
|
||||
|
||||
.. _v4l2-meta-fmt-generic-csi2-10:
|
||||
|
||||
V4L2_META_FMT_GENERIC_CSI2_10
|
||||
-----------------------------
|
||||
|
||||
V4L2_META_FMT_GENERIC_CSI2_10 contains 8-bit generic metadata packed in 10-bit
|
||||
Data Units, with one padding byte after every four bytes of metadata. This
|
||||
format is typically used by CSI-2 receivers with a source that transmits
|
||||
MEDIA_BUS_FMT_META_10 and the CSI-2 receiver writes the received data to memory
|
||||
as-is.
|
||||
|
||||
The packing of the data follows the MIPI CSI-2 specification and the padding of
|
||||
the data is defined in the MIPI CCS specification.
|
||||
|
||||
This format is also used in conjunction with 20 bits per :term:`Data Unit`
|
||||
formats that pack two bytes of metadata into one Data Unit. Otherwise the
|
||||
20 bits per pixel dataformat is :ref:`V4L2_META_FMT_GENERIC_CSI2_20
|
||||
<v4l2-meta-fmt-generic-csi2-20>`.
|
||||
|
||||
This format is little endian.
|
||||
|
||||
**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_10.**
|
||||
Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
|
||||
|
||||
.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|
|
||||
|
||||
.. flat-table:: Sample 4x2 Metadata Frame
|
||||
:header-rows: 0
|
||||
:stub-columns: 0
|
||||
:widths: 12 8 8 8 8 8
|
||||
|
||||
* - start + 0:
|
||||
- M\ :sub:`00`
|
||||
- M\ :sub:`10`
|
||||
- M\ :sub:`20`
|
||||
- M\ :sub:`30`
|
||||
- x
|
||||
* - start + 5:
|
||||
- M\ :sub:`01`
|
||||
- M\ :sub:`11`
|
||||
- M\ :sub:`21`
|
||||
- M\ :sub:`31`
|
||||
- x
|
||||
|
||||
.. _v4l2-meta-fmt-generic-csi2-12:
|
||||
|
||||
V4L2_META_FMT_GENERIC_CSI2_12
|
||||
-----------------------------
|
||||
|
||||
V4L2_META_FMT_GENERIC_CSI2_12 contains 8-bit generic metadata packed in 12-bit
|
||||
Data Units, with one padding byte after every two bytes of metadata. This format
|
||||
is typically used by CSI-2 receivers with a source that transmits
|
||||
MEDIA_BUS_FMT_META_12 and the CSI-2 receiver writes the received data to memory
|
||||
as-is.
|
||||
|
||||
The packing of the data follows the MIPI CSI-2 specification and the padding of
|
||||
the data is defined in the MIPI CCS specification.
|
||||
|
||||
This format is also used in conjunction with 24 bits per :term:`Data Unit`
|
||||
formats that pack two bytes of metadata into one Data Unit. Otherwise the
|
||||
24 bits per pixel dataformat is :ref:`V4L2_META_FMT_GENERIC_CSI2_24
|
||||
<v4l2-meta-fmt-generic-csi2-24>`.
|
||||
|
||||
This format is little endian.
|
||||
|
||||
**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_12.**
|
||||
Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
|
||||
|
||||
.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
|
||||
|
||||
.. flat-table:: Sample 4x2 Metadata Frame
|
||||
:header-rows: 0
|
||||
:stub-columns: 0
|
||||
:widths: 12 8 8 8 8 8 8
|
||||
|
||||
* - start + 0:
|
||||
- M\ :sub:`00`
|
||||
- M\ :sub:`10`
|
||||
- x
|
||||
- M\ :sub:`20`
|
||||
- M\ :sub:`30`
|
||||
- x
|
||||
* - start + 6:
|
||||
- M\ :sub:`01`
|
||||
- M\ :sub:`11`
|
||||
- x
|
||||
- M\ :sub:`21`
|
||||
- M\ :sub:`31`
|
||||
- x
|
||||
|
||||
.. _v4l2-meta-fmt-generic-csi2-14:
|
||||
|
||||
V4L2_META_FMT_GENERIC_CSI2_14
|
||||
-----------------------------
|
||||
|
||||
V4L2_META_FMT_GENERIC_CSI2_14 contains 8-bit generic metadata packed in 14-bit
|
||||
Data Units, with three padding bytes after every four bytes of metadata. This
|
||||
format is typically used by CSI-2 receivers with a source that transmits
|
||||
MEDIA_BUS_FMT_META_14 and the CSI-2 receiver writes the received data to memory
|
||||
as-is.
|
||||
|
||||
The packing of the data follows the MIPI CSI-2 specification and the padding of
|
||||
the data is defined in the MIPI CCS specification.
|
||||
|
||||
This format is little endian.
|
||||
|
||||
**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_14.**
|
||||
Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
|
||||
|
||||
.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{.8cm}|
|
||||
|
||||
.. flat-table:: Sample 4x2 Metadata Frame
|
||||
:header-rows: 0
|
||||
:stub-columns: 0
|
||||
:widths: 12 8 8 8 8 8 8 8
|
||||
|
||||
* - start + 0:
|
||||
- M\ :sub:`00`
|
||||
- M\ :sub:`10`
|
||||
- M\ :sub:`20`
|
||||
- M\ :sub:`30`
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
* - start + 7:
|
||||
- M\ :sub:`01`
|
||||
- M\ :sub:`11`
|
||||
- M\ :sub:`21`
|
||||
- M\ :sub:`31`
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
|
||||
.. _v4l2-meta-fmt-generic-csi2-16:
|
||||
|
||||
V4L2_META_FMT_GENERIC_CSI2_16
|
||||
-----------------------------
|
||||
|
||||
V4L2_META_FMT_GENERIC_CSI2_16 contains 8-bit generic metadata packed in 16-bit
|
||||
Data Units, with one padding byte after every byte of metadata. This format is
|
||||
typically used by CSI-2 receivers with a source that transmits
|
||||
MEDIA_BUS_FMT_META_16 and the CSI-2 receiver writes the received data to memory
|
||||
as-is.
|
||||
|
||||
The packing of the data follows the MIPI CSI-2 specification and the padding of
|
||||
the data is defined in the MIPI CCS specification.
|
||||
|
||||
Some devices support more efficient packing of metadata in conjunction with
|
||||
16-bit image data. In that case the dataformat is
|
||||
:ref:`V4L2_META_FMT_GENERIC_8 <v4l2-meta-fmt-generic-8>`.
|
||||
|
||||
This format is little endian.
|
||||
|
||||
**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_16.**
|
||||
Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
|
||||
|
||||
.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|
|
||||
|
||||
.. flat-table:: Sample 4x2 Metadata Frame
|
||||
:header-rows: 0
|
||||
:stub-columns: 0
|
||||
:widths: 12 8 8 8 8 8 8 8 8
|
||||
|
||||
* - start + 0:
|
||||
- M\ :sub:`00`
|
||||
- x
|
||||
- M\ :sub:`10`
|
||||
- x
|
||||
- M\ :sub:`20`
|
||||
- x
|
||||
- M\ :sub:`30`
|
||||
- x
|
||||
* - start + 8:
|
||||
- M\ :sub:`01`
|
||||
- x
|
||||
- M\ :sub:`11`
|
||||
- x
|
||||
- M\ :sub:`21`
|
||||
- x
|
||||
- M\ :sub:`31`
|
||||
- x
|
||||
|
||||
.. _v4l2-meta-fmt-generic-csi2-20:
|
||||
|
||||
V4L2_META_FMT_GENERIC_CSI2_20
|
||||
-----------------------------
|
||||
|
||||
V4L2_META_FMT_GENERIC_CSI2_20 contains 8-bit generic metadata packed in 20-bit
|
||||
Data Units, with alternating one or two padding bytes after every byte of
|
||||
metadata. This format is typically used by CSI-2 receivers with a source that
|
||||
transmits MEDIA_BUS_FMT_META_20 and the CSI-2 receiver writes the received data
|
||||
to memory as-is.
|
||||
|
||||
The packing of the data follows the MIPI CSI-2 specification and the padding of
|
||||
the data is defined in the MIPI CCS specification.
|
||||
|
||||
Some devices support more efficient packing of metadata in conjunction with
|
||||
16-bit image data. In that case the dataformat is
|
||||
:ref:`V4L2_META_FMT_GENERIC_CSI2_10 <v4l2-meta-fmt-generic-csi2-10>`.
|
||||
|
||||
This format is little endian.
|
||||
|
||||
**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_20.**
|
||||
Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
|
||||
|
||||
.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
|
||||
|
||||
.. flat-table:: Sample 4x2 Metadata Frame
|
||||
:header-rows: 0
|
||||
:stub-columns: 0
|
||||
:widths: 12 8 8 8 8 8 8 8 8 8 8
|
||||
|
||||
* - start + 0:
|
||||
- M\ :sub:`00`
|
||||
- x
|
||||
- M\ :sub:`10`
|
||||
- x
|
||||
- x
|
||||
- M\ :sub:`20`
|
||||
- x
|
||||
- M\ :sub:`30`
|
||||
- x
|
||||
- x
|
||||
* - start + 10:
|
||||
- M\ :sub:`01`
|
||||
- x
|
||||
- M\ :sub:`11`
|
||||
- x
|
||||
- x
|
||||
- M\ :sub:`21`
|
||||
- x
|
||||
- M\ :sub:`31`
|
||||
- x
|
||||
- x
|
||||
|
||||
.. _v4l2-meta-fmt-generic-csi2-24:
|
||||
|
||||
V4L2_META_FMT_GENERIC_CSI2_24
|
||||
-----------------------------
|
||||
|
||||
V4L2_META_FMT_GENERIC_CSI2_24 contains 8-bit generic metadata packed in 24-bit
|
||||
Data Units, with two padding bytes after every byte of metadata. This format is
|
||||
typically used by CSI-2 receivers with a source that transmits
|
||||
MEDIA_BUS_FMT_META_24 and the CSI-2 receiver writes the received data to memory
|
||||
as-is.
|
||||
|
||||
The packing of the data follows the MIPI CSI-2 specification and the padding of
|
||||
the data is defined in the MIPI CCS specification.
|
||||
|
||||
Some devices support more efficient packing of metadata in conjunction with
|
||||
16-bit image data. In that case the dataformat is
|
||||
:ref:`V4L2_META_FMT_GENERIC_CSI2_12 <v4l2-meta-fmt-generic-csi2-12>`.
|
||||
|
||||
This format is little endian.
|
||||
|
||||
**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_24.**
|
||||
Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
|
||||
|
||||
.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
|
||||
|
||||
.. flat-table:: Sample 4x2 Metadata Frame
|
||||
:header-rows: 0
|
||||
:stub-columns: 0
|
||||
:widths: 12 8 8 8 8 8 8 8 8 8 8 8 8
|
||||
|
||||
* - start + 0:
|
||||
- M\ :sub:`00`
|
||||
- x
|
||||
- x
|
||||
- M\ :sub:`10`
|
||||
- x
|
||||
- x
|
||||
- M\ :sub:`20`
|
||||
- x
|
||||
- x
|
||||
- M\ :sub:`30`
|
||||
- x
|
||||
- x
|
||||
* - start + 12:
|
||||
- M\ :sub:`01`
|
||||
- x
|
||||
- x
|
||||
- M\ :sub:`11`
|
||||
- x
|
||||
- x
|
||||
- M\ :sub:`21`
|
||||
- x
|
||||
- x
|
||||
- M\ :sub:`31`
|
||||
- x
|
||||
- x
|
@ -188,7 +188,7 @@ Example: Mapping buffers in the multi-planar API
|
||||
buffers[i].start[j] = mmap(NULL, buffer.m.planes[j].length,
|
||||
PROT_READ | PROT_WRITE, /* recommended */
|
||||
MAP_SHARED, /* recommended */
|
||||
fd, buffer.m.planes[j].m.offset);
|
||||
fd, buffer.m.planes[j].m.mem_offset);
|
||||
|
||||
if (MAP_FAILED == buffers[i].start[j]) {
|
||||
/* If you do not exit here you should unmap() and free()
|
||||
|
@ -36,6 +36,8 @@ are often referred to as greyscale formats.
|
||||
- Byte 2
|
||||
- Byte 3
|
||||
- Byte 4
|
||||
- Byte 5
|
||||
- Byte 6
|
||||
|
||||
* .. _V4L2-PIX-FMT-GREY:
|
||||
|
||||
@ -47,6 +49,8 @@ are often referred to as greyscale formats.
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
|
||||
* .. _V4L2-PIX-FMT-IPU3-Y10:
|
||||
|
||||
@ -58,6 +62,8 @@ are often referred to as greyscale formats.
|
||||
- Y'\ :sub:`2`\ [3:0] Y'\ :sub:`1`\ [9:6]
|
||||
- Y'\ :sub:`3`\ [1:0] Y'\ :sub:`2`\ [9:4]
|
||||
- Y'\ :sub:`3`\ [9:2]
|
||||
- ...
|
||||
- ...
|
||||
|
||||
* .. _V4L2-PIX-FMT-Y10:
|
||||
|
||||
@ -69,6 +75,8 @@ are often referred to as greyscale formats.
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
|
||||
* .. _V4L2-PIX-FMT-Y10BPACK:
|
||||
|
||||
@ -80,6 +88,8 @@ are often referred to as greyscale formats.
|
||||
- Y'\ :sub:`1`\ [3:0] Y'\ :sub:`2`\ [9:6]
|
||||
- Y'\ :sub:`2`\ [5:0] Y'\ :sub:`3`\ [9:8]
|
||||
- Y'\ :sub:`3`\ [7:0]
|
||||
- ...
|
||||
- ...
|
||||
|
||||
* .. _V4L2-PIX-FMT-Y10P:
|
||||
|
||||
@ -91,6 +101,8 @@ are often referred to as greyscale formats.
|
||||
- Y'\ :sub:`2`\ [9:2]
|
||||
- Y'\ :sub:`3`\ [9:2]
|
||||
- Y'\ :sub:`3`\ [1:0] Y'\ :sub:`2`\ [1:0] Y'\ :sub:`1`\ [1:0] Y'\ :sub:`0`\ [1:0]
|
||||
- ...
|
||||
- ...
|
||||
|
||||
* .. _V4L2-PIX-FMT-Y12:
|
||||
|
||||
@ -102,6 +114,8 @@ are often referred to as greyscale formats.
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
|
||||
* .. _V4L2-PIX-FMT-Y012:
|
||||
|
||||
@ -113,6 +127,21 @@ are often referred to as greyscale formats.
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
|
||||
* .. _V4L2-PIX-FMT-Y12P:
|
||||
|
||||
- ``V4L2_PIX_FMT_Y12P``
|
||||
- 'Y12P'
|
||||
|
||||
- Y'\ :sub:`0`\ [11:4]
|
||||
- Y'\ :sub:`1`\ [11:4]
|
||||
- Y'\ :sub:`1`\ [3:0] Y'\ :sub:`0`\ [3:0]
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
|
||||
* .. _V4L2-PIX-FMT-Y14:
|
||||
|
||||
@ -124,6 +153,21 @@ are often referred to as greyscale formats.
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
|
||||
* .. _V4L2-PIX-FMT-Y14P:
|
||||
|
||||
- ``V4L2_PIX_FMT_Y14P``
|
||||
- 'Y14P'
|
||||
|
||||
- Y'\ :sub:`0`\ [13:6]
|
||||
- Y'\ :sub:`1`\ [13:6]
|
||||
- Y'\ :sub:`2`\ [13:6]
|
||||
- Y'\ :sub:`3`\ [13:6]
|
||||
- Y'\ :sub:`1`\ [1:0] Y'\ :sub:`0`\ [5:0]
|
||||
- Y'\ :sub:`2`\ [3:0] Y'\ :sub:`1`\ [5:2]
|
||||
- Y'\ :sub:`3`\ [5:0] Y'\ :sub:`2`\ [5:4]
|
||||
|
||||
* .. _V4L2-PIX-FMT-Y16:
|
||||
|
||||
@ -135,6 +179,8 @@ are often referred to as greyscale formats.
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
|
||||
* .. _V4L2-PIX-FMT-Y16-BE:
|
||||
|
||||
@ -146,6 +192,8 @@ are often referred to as greyscale formats.
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
- ...
|
||||
|
||||
.. raw:: latex
|
||||
|
||||
|
@ -33,7 +33,7 @@ Media Bus Formats
|
||||
* - __u32
|
||||
- ``field``
|
||||
- Field order, from enum :c:type:`v4l2_field`. See
|
||||
:ref:`field-order` for details.
|
||||
:ref:`field-order` for details. Zero for metadata mbus codes.
|
||||
* - __u32
|
||||
- ``colorspace``
|
||||
- Image colorspace, from enum :c:type:`v4l2_colorspace`.
|
||||
@ -45,7 +45,7 @@ Media Bus Formats
|
||||
conversion is supported by setting the flag
|
||||
V4L2_SUBDEV_MBUS_CODE_CSC_COLORSPACE in the corresponding struct
|
||||
:c:type:`v4l2_subdev_mbus_code_enum` during enumeration.
|
||||
See :ref:`v4l2-subdev-mbus-code-flags`.
|
||||
See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes.
|
||||
* - union {
|
||||
- (anonymous)
|
||||
* - __u16
|
||||
@ -61,7 +61,7 @@ Media Bus Formats
|
||||
that ycbcr_enc conversion is supported by setting the flag
|
||||
V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC in the corresponding struct
|
||||
:c:type:`v4l2_subdev_mbus_code_enum` during enumeration.
|
||||
See :ref:`v4l2-subdev-mbus-code-flags`.
|
||||
See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes.
|
||||
* - __u16
|
||||
- ``hsv_enc``
|
||||
- HSV encoding, from enum :c:type:`v4l2_hsv_encoding`.
|
||||
@ -75,7 +75,7 @@ Media Bus Formats
|
||||
that hsv_enc conversion is supported by setting the flag
|
||||
V4L2_SUBDEV_MBUS_CODE_CSC_HSV_ENC in the corresponding struct
|
||||
:c:type:`v4l2_subdev_mbus_code_enum` during enumeration.
|
||||
See :ref:`v4l2-subdev-mbus-code-flags`
|
||||
See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes.
|
||||
* - }
|
||||
-
|
||||
* - __u16
|
||||
@ -90,8 +90,8 @@ Media Bus Formats
|
||||
The driver indicates that quantization conversion is supported by
|
||||
setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION in the
|
||||
corresponding struct :c:type:`v4l2_subdev_mbus_code_enum`
|
||||
during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`.
|
||||
|
||||
during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for
|
||||
metadata mbus codes.
|
||||
* - __u16
|
||||
- ``xfer_func``
|
||||
- Transfer function, from enum :c:type:`v4l2_xfer_func`.
|
||||
@ -104,7 +104,8 @@ Media Bus Formats
|
||||
The driver indicates that the transfer function conversion is supported by
|
||||
setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_XFER_FUNC in the
|
||||
corresponding struct :c:type:`v4l2_subdev_mbus_code_enum`
|
||||
during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`.
|
||||
during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for
|
||||
metadata mbus codes.
|
||||
* - __u16
|
||||
- ``flags``
|
||||
- flags See: :ref:v4l2-mbus-framefmt-flags
|
||||
@ -8306,3 +8307,257 @@ The following table lists the existing metadata formats.
|
||||
both sides of the link and the bus format is a fixed
|
||||
metadata format that is not configurable from userspace.
|
||||
Width and height will be set to 0 for this format.
|
||||
|
||||
Generic Serial Metadata Formats
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Generic serial metadata formats are used on serial buses where the actual data
|
||||
content is more or less device specific but the data is transmitted and received
|
||||
by multiple devices that do not process the data in any way, simply writing
|
||||
it to system memory for processing in software at the end of the pipeline.
|
||||
|
||||
"b" in an array cell signifies a byte of data, followed by the number of the bit
|
||||
and finally the bit number in subscript. "x" indicates a padding bit.
|
||||
|
||||
.. _media-bus-format-generic-meta:
|
||||
|
||||
.. cssclass: longtable
|
||||
|
||||
.. flat-table:: Generic Serial Metadata Formats
|
||||
:header-rows: 2
|
||||
:stub-columns: 0
|
||||
|
||||
* - Identifier
|
||||
- Code
|
||||
-
|
||||
- :cspan:`23` Data organization within bus :term:`Data Unit`
|
||||
* -
|
||||
-
|
||||
- Bit
|
||||
- 23
|
||||
- 22
|
||||
- 21
|
||||
- 20
|
||||
- 19
|
||||
- 18
|
||||
- 17
|
||||
- 16
|
||||
- 15
|
||||
- 14
|
||||
- 13
|
||||
- 12
|
||||
- 11
|
||||
- 10
|
||||
- 9
|
||||
- 8
|
||||
- 7
|
||||
- 6
|
||||
- 5
|
||||
- 4
|
||||
- 3
|
||||
- 2
|
||||
- 1
|
||||
- 0
|
||||
* .. _MEDIA-BUS-FMT-META-8:
|
||||
|
||||
- MEDIA_BUS_FMT_META_8
|
||||
- 0x8001
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
- b0\ :sub:`7`
|
||||
- b0\ :sub:`6`
|
||||
- b0\ :sub:`5`
|
||||
- b0\ :sub:`4`
|
||||
- b0\ :sub:`3`
|
||||
- b0\ :sub:`2`
|
||||
- b0\ :sub:`1`
|
||||
- b0\ :sub:`0`
|
||||
* .. _MEDIA-BUS-FMT-META-10:
|
||||
|
||||
- MEDIA_BUS_FMT_META_10
|
||||
- 0x8002
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
- b0\ :sub:`7`
|
||||
- b0\ :sub:`6`
|
||||
- b0\ :sub:`5`
|
||||
- b0\ :sub:`4`
|
||||
- b0\ :sub:`3`
|
||||
- b0\ :sub:`2`
|
||||
- b0\ :sub:`1`
|
||||
- b0\ :sub:`0`
|
||||
- x
|
||||
- x
|
||||
* .. _MEDIA-BUS-FMT-META-12:
|
||||
|
||||
- MEDIA_BUS_FMT_META_12
|
||||
- 0x8003
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
- b0\ :sub:`7`
|
||||
- b0\ :sub:`6`
|
||||
- b0\ :sub:`5`
|
||||
- b0\ :sub:`4`
|
||||
- b0\ :sub:`3`
|
||||
- b0\ :sub:`2`
|
||||
- b0\ :sub:`1`
|
||||
- b0\ :sub:`0`
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
* .. _MEDIA-BUS-FMT-META-14:
|
||||
|
||||
- MEDIA_BUS_FMT_META_14
|
||||
- 0x8004
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
- b0\ :sub:`7`
|
||||
- b0\ :sub:`6`
|
||||
- b0\ :sub:`5`
|
||||
- b0\ :sub:`4`
|
||||
- b0\ :sub:`3`
|
||||
- b0\ :sub:`2`
|
||||
- b0\ :sub:`1`
|
||||
- b0\ :sub:`0`
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
* .. _MEDIA-BUS-FMT-META-16:
|
||||
|
||||
- MEDIA_BUS_FMT_META_16
|
||||
- 0x8005
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
- b0\ :sub:`7`
|
||||
- b0\ :sub:`6`
|
||||
- b0\ :sub:`5`
|
||||
- b0\ :sub:`4`
|
||||
- b0\ :sub:`3`
|
||||
- b0\ :sub:`2`
|
||||
- b0\ :sub:`1`
|
||||
- b0\ :sub:`0`
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
* .. _MEDIA-BUS-FMT-META-20:
|
||||
|
||||
- MEDIA_BUS_FMT_META_20
|
||||
- 0x8006
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
- b0\ :sub:`7`
|
||||
- b0\ :sub:`6`
|
||||
- b0\ :sub:`5`
|
||||
- b0\ :sub:`4`
|
||||
- b0\ :sub:`3`
|
||||
- b0\ :sub:`2`
|
||||
- b0\ :sub:`1`
|
||||
- b0\ :sub:`0`
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
* .. _MEDIA-BUS-FMT-META-24:
|
||||
|
||||
- MEDIA_BUS_FMT_META_24
|
||||
- 0x8007
|
||||
-
|
||||
- b0\ :sub:`7`
|
||||
- b0\ :sub:`6`
|
||||
- b0\ :sub:`5`
|
||||
- b0\ :sub:`4`
|
||||
- b0\ :sub:`3`
|
||||
- b0\ :sub:`2`
|
||||
- b0\ :sub:`1`
|
||||
- b0\ :sub:`0`
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
- x
|
||||
|
@ -62,6 +62,7 @@ Function Reference
|
||||
vidioc-query-dv-timings
|
||||
vidioc-querystd
|
||||
vidioc-reqbufs
|
||||
vidioc-remove-bufs
|
||||
vidioc-s-hw-freq-seek
|
||||
vidioc-streamon
|
||||
vidioc-subdev-enum-frame-interval
|
||||
|
@ -227,6 +227,13 @@ the ``mbus_code`` field is handled differently:
|
||||
The application can ask to configure the quantization of the capture
|
||||
device when calling the :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl with
|
||||
:ref:`V4L2_PIX_FMT_FLAG_SET_CSC <v4l2-pix-fmt-flag-set-csc>` set.
|
||||
* - ``V4L2_FMT_FLAG_META_LINE_BASED``
|
||||
- 0x0200
|
||||
- The metadata format is line-based. In this case the ``width``,
|
||||
``height`` and ``bytesperline`` fields of :c:type:`v4l2_meta_format` are
|
||||
valid. The buffer consists of ``height`` lines, each having ``width``
|
||||
Data Units of data and the offset (in bytes) between the beginning of
|
||||
each two consecutive lines is ``bytesperline``.
|
||||
|
||||
Return Value
|
||||
============
|
||||
|
86
Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst
Normal file
86
Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst
Normal file
@ -0,0 +1,86 @@
|
||||
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
|
||||
.. c:namespace:: V4L
|
||||
|
||||
.. _VIDIOC_REMOVE_BUFS:
|
||||
|
||||
************************
|
||||
ioctl VIDIOC_REMOVE_BUFS
|
||||
************************
|
||||
|
||||
Name
|
||||
====
|
||||
|
||||
VIDIOC_REMOVE_BUFS - Removes buffers from a queue
|
||||
|
||||
Synopsis
|
||||
========
|
||||
|
||||
.. c:macro:: VIDIOC_REMOVE_BUFS
|
||||
|
||||
``int ioctl(int fd, VIDIOC_REMOVE_BUFS, struct v4l2_remove_buffers *argp)``
|
||||
|
||||
Arguments
|
||||
=========
|
||||
|
||||
``fd``
|
||||
File descriptor returned by :c:func:`open()`.
|
||||
|
||||
``argp``
|
||||
Pointer to struct :c:type:`v4l2_remove_buffers`.
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Applications can optionally call the :ref:`VIDIOC_REMOVE_BUFS` ioctl to
|
||||
remove buffers from a queue.
|
||||
:ref:`VIDIOC_CREATE_BUFS` ioctl support is mandatory to enable :ref:`VIDIOC_REMOVE_BUFS`.
|
||||
This ioctl is available if the ``V4L2_BUF_CAP_SUPPORTS_REMOVE_BUFS`` capability
|
||||
is set on the queue when :c:func:`VIDIOC_REQBUFS` or :c:func:`VIDIOC_CREATE_BUFS`
|
||||
are invoked.
|
||||
|
||||
.. c:type:: v4l2_remove_buffers
|
||||
|
||||
.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.5cm}|
|
||||
|
||||
.. flat-table:: struct v4l2_remove_buffers
|
||||
:header-rows: 0
|
||||
:stub-columns: 0
|
||||
:widths: 1 1 2
|
||||
|
||||
* - __u32
|
||||
- ``index``
|
||||
- The starting buffer index to remove. This field is ignored if count == 0.
|
||||
* - __u32
|
||||
- ``count``
|
||||
- The number of buffers to be removed with indices 'index' until 'index + count - 1'.
|
||||
All buffers in this range must be valid and in DEQUEUED state.
|
||||
:ref:`VIDIOC_REMOVE_BUFS` will always check the validity of ``type`, if it is
|
||||
invalid it returns ``EINVAL`` error code.
|
||||
If count is set to 0 :ref:`VIDIOC_REMOVE_BUFS` will do nothing and return 0.
|
||||
* - __u32
|
||||
- ``type``
|
||||
- Type of the stream or buffers, this is the same as the struct
|
||||
:c:type:`v4l2_format` ``type`` field. See
|
||||
:c:type:`v4l2_buf_type` for valid values.
|
||||
* - __u32
|
||||
- ``reserved``\ [13]
|
||||
- A place holder for future extensions. Drivers and applications
|
||||
must set the array to zero.
|
||||
|
||||
Return Value
|
||||
============
|
||||
|
||||
On success 0 is returned, on error -1 and the ``errno`` variable is set
|
||||
appropriately. The generic error codes are described at the
|
||||
:ref:`Generic Error Codes <gen-errors>` chapter. If an error occurs, no
|
||||
buffers will be freed and one of the error codes below will be returned:
|
||||
|
||||
EBUSY
|
||||
File I/O is in progress.
|
||||
One or more of the buffers in the range ``index`` to ``index + count - 1`` are not
|
||||
in DEQUEUED state.
|
||||
|
||||
EINVAL
|
||||
One or more of the buffers in the range ``index`` to ``index + count - 1`` do not
|
||||
exist in the queue.
|
||||
The buffer type (``type`` field) is not valid.
|
@ -121,6 +121,7 @@ aborting or finishing any DMA in progress, an implicit
|
||||
.. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF:
|
||||
.. _V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS:
|
||||
.. _V4L2-BUF-CAP-SUPPORTS-MAX-NUM-BUFFERS:
|
||||
.. _V4L2-BUF-CAP-SUPPORTS-REMOVE-BUFS:
|
||||
|
||||
.. raw:: latex
|
||||
|
||||
|
@ -37,9 +37,9 @@ Description
|
||||
|
||||
.. note::
|
||||
|
||||
This is an :ref:`obsolete` interface and may be removed
|
||||
in the future. It is superseded by
|
||||
:ref:`the selection API <VIDIOC_SUBDEV_G_SELECTION>`.
|
||||
This is an :ref:`obsolete` interface and may be removed in the future. It is
|
||||
superseded by :ref:`the selection API <VIDIOC_SUBDEV_G_SELECTION>`. No new
|
||||
extensions to the :c:type:`v4l2_subdev_crop` structure will be accepted.
|
||||
|
||||
To retrieve the current crop rectangle applications set the ``pad``
|
||||
field of a struct :c:type:`v4l2_subdev_crop` to the
|
||||
|
@ -43,23 +43,39 @@ The routing configuration determines the flows of data inside an entity.
|
||||
Drivers report their current routing tables using the
|
||||
``VIDIOC_SUBDEV_G_ROUTING`` ioctl and application may enable or disable routes
|
||||
with the ``VIDIOC_SUBDEV_S_ROUTING`` ioctl, by adding or removing routes and
|
||||
setting or clearing flags of the ``flags`` field of a
|
||||
struct :c:type:`v4l2_subdev_route`.
|
||||
setting or clearing flags of the ``flags`` field of a struct
|
||||
:c:type:`v4l2_subdev_route`. Similarly to ``VIDIOC_SUBDEV_G_ROUTING``, also
|
||||
``VIDIOC_SUBDEV_S_ROUTING`` returns the routes back to the user.
|
||||
|
||||
All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called. This
|
||||
means that the userspace must reconfigure all streams after calling the ioctl
|
||||
with e.g. ``VIDIOC_SUBDEV_S_FMT``.
|
||||
All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called.
|
||||
This means that the userspace must reconfigure all stream formats and selections
|
||||
after calling the ioctl with e.g. ``VIDIOC_SUBDEV_S_FMT``.
|
||||
|
||||
Only subdevices which have both sink and source pads can support routing.
|
||||
|
||||
When inspecting routes through ``VIDIOC_SUBDEV_G_ROUTING`` and the application
|
||||
provided ``num_routes`` is not big enough to contain all the available routes
|
||||
the subdevice exposes, drivers return the ENOSPC error code and adjust the
|
||||
value of the ``num_routes`` field. Application should then reserve enough memory
|
||||
for all the route entries and call ``VIDIOC_SUBDEV_G_ROUTING`` again.
|
||||
The ``len_routes`` field indicates the number of routes that can fit in the
|
||||
``routes`` array allocated by userspace. It is set by applications for both
|
||||
ioctls to indicate how many routes the kernel can return, and is never modified
|
||||
by the kernel.
|
||||
|
||||
On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the
|
||||
``num_routes`` field to reflect the actual number of routes returned.
|
||||
The ``num_routes`` field indicates the number of routes in the routing
|
||||
table. For ``VIDIOC_SUBDEV_S_ROUTING``, it is set by userspace to the number of
|
||||
routes that the application stored in the ``routes`` array. For both ioctls, it
|
||||
is returned by the kernel and indicates how many routes are stored in the
|
||||
subdevice routing table. This may be smaller or larger than the value of
|
||||
``num_routes`` set by the application for ``VIDIOC_SUBDEV_S_ROUTING``, as
|
||||
drivers may adjust the requested routing table.
|
||||
|
||||
The kernel can return a ``num_routes`` value larger than ``len_routes`` from
|
||||
both ioctls. This indicates thare are more routes in the routing table than fits
|
||||
the ``routes`` array. In this case, the ``routes`` array is filled by the kernel
|
||||
with the first ``len_routes`` entries of the subdevice routing table. This is
|
||||
not considered to be an error, and the ioctl call succeeds. If the applications
|
||||
wants to retrieve the missing routes, it can issue a new
|
||||
``VIDIOC_SUBDEV_G_ROUTING`` call with a large enough ``routes`` array.
|
||||
|
||||
``VIDIOC_SUBDEV_S_ROUTING`` may return more routes than the user provided in
|
||||
``num_routes`` field due to e.g. hardware properties.
|
||||
|
||||
.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}|
|
||||
|
||||
@ -74,6 +90,9 @@ On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the
|
||||
- ``which``
|
||||
- Routing table to be accessed, from enum
|
||||
:ref:`v4l2_subdev_format_whence <v4l2-subdev-format-whence>`.
|
||||
* - __u32
|
||||
- ``len_routes``
|
||||
- The length of the array (as in memory reserved for the array)
|
||||
* - struct :c:type:`v4l2_subdev_route`
|
||||
- ``routes[]``
|
||||
- Array of struct :c:type:`v4l2_subdev_route` entries
|
||||
@ -81,7 +100,7 @@ On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the
|
||||
- ``num_routes``
|
||||
- Number of entries of the routes array
|
||||
* - __u32
|
||||
- ``reserved``\ [5]
|
||||
- ``reserved``\ [11]
|
||||
- Reserved for future extensions. Applications and drivers must set
|
||||
the array to zero.
|
||||
|
||||
@ -135,10 +154,6 @@ On success 0 is returned, on error -1 and the ``errno`` variable is set
|
||||
appropriately. The generic error codes are described at the
|
||||
:ref:`Generic Error Codes <gen-errors>` chapter.
|
||||
|
||||
ENOSPC
|
||||
The application provided ``num_routes`` is not big enough to contain
|
||||
all the available routes the subdevice exposes.
|
||||
|
||||
EINVAL
|
||||
The sink or source pad identifiers reference a non-existing pad or reference
|
||||
pads of different types (ie. the sink_pad identifiers refers to a source
|
||||
|
@ -215,6 +215,7 @@ replace define V4L2_FMT_FLAG_CSC_XFER_FUNC fmtdesc-flags
|
||||
replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags
|
||||
replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags
|
||||
replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags
|
||||
replace define V4L2_FMT_FLAG_META_LINE_BASED fmtdesc-flags
|
||||
|
||||
# V4L2 timecode types
|
||||
replace define V4L2_TC_TYPE_24FPS timecode-type
|
||||
|
19
MAINTAINERS
19
MAINTAINERS
@ -4110,6 +4110,13 @@ N: bcm113*
|
||||
N: bcm216*
|
||||
N: kona
|
||||
|
||||
BROADCOM BCM2835 CAMERA DRIVERS
|
||||
M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml
|
||||
F: drivers/media/platform/broadcom/bcm2835-unicam*
|
||||
|
||||
BROADCOM BCM47XX MIPS ARCHITECTURE
|
||||
M: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
M: Rafał Miłecki <zajec5@gmail.com>
|
||||
@ -11091,6 +11098,16 @@ F: Documentation/admin-guide/media/ipu3_rcb.svg
|
||||
F: Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst
|
||||
F: drivers/staging/media/ipu3/
|
||||
|
||||
INTEL IPU6 INPUT SYSTEM DRIVER
|
||||
M: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
M: Bingbu Cao <bingbu.cao@intel.com>
|
||||
R: Tianshu Qiu <tian.shu.qiu@intel.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
F: Documentation/admin-guide/media/ipu6-isys.rst
|
||||
F: drivers/media/pci/intel/ipu6/
|
||||
|
||||
INTEL ISHTP ECLITE DRIVER
|
||||
M: Sumesh K Naduvalath <sumesh.k.naduvalath@intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
@ -16539,7 +16556,7 @@ M: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
F: Documentation/devicetree/bindings/media/i2c/ov8856.yaml
|
||||
F: Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml
|
||||
F: drivers/media/i2c/ov8856.c
|
||||
|
||||
OMNIVISION OV8858 SENSOR DRIVER
|
||||
|
@ -490,6 +490,15 @@ int cec_thread_func(void *_adap)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (adap->transmit_in_progress &&
|
||||
adap->transmit_in_progress_aborted) {
|
||||
if (adap->transmitting)
|
||||
cec_data_cancel(adap->transmitting,
|
||||
CEC_TX_STATUS_ABORTED, 0);
|
||||
adap->transmit_in_progress = false;
|
||||
adap->transmit_in_progress_aborted = false;
|
||||
goto unlock;
|
||||
}
|
||||
if (adap->transmit_in_progress && timeout) {
|
||||
/*
|
||||
* If we timeout, then log that. Normally this does
|
||||
@ -771,6 +780,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
|
||||
{
|
||||
struct cec_data *data;
|
||||
bool is_raw = msg_is_raw(msg);
|
||||
int err;
|
||||
|
||||
if (adap->devnode.unregistered)
|
||||
return -ENODEV;
|
||||
@ -935,11 +945,13 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
|
||||
* Release the lock and wait, retake the lock afterwards.
|
||||
*/
|
||||
mutex_unlock(&adap->lock);
|
||||
wait_for_completion_killable(&data->c);
|
||||
if (!data->completed)
|
||||
cancel_delayed_work_sync(&data->work);
|
||||
err = wait_for_completion_killable(&data->c);
|
||||
cancel_delayed_work_sync(&data->work);
|
||||
mutex_lock(&adap->lock);
|
||||
|
||||
if (err)
|
||||
adap->transmit_in_progress_aborted = true;
|
||||
|
||||
/* Cancel the transmit if it was interrupted */
|
||||
if (!data->completed) {
|
||||
if (data->msg.tx_status & CEC_TX_STATUS_OK)
|
||||
@ -1575,9 +1587,12 @@ unconfigure:
|
||||
*/
|
||||
static void cec_claim_log_addrs(struct cec_adapter *adap, bool block)
|
||||
{
|
||||
if (WARN_ON(adap->is_configuring || adap->is_configured))
|
||||
if (WARN_ON(adap->is_claiming_log_addrs ||
|
||||
adap->is_configuring || adap->is_configured))
|
||||
return;
|
||||
|
||||
adap->is_claiming_log_addrs = true;
|
||||
|
||||
init_completion(&adap->config_completion);
|
||||
|
||||
/* Ready to kick off the thread */
|
||||
@ -1592,6 +1607,7 @@ static void cec_claim_log_addrs(struct cec_adapter *adap, bool block)
|
||||
wait_for_completion(&adap->config_completion);
|
||||
mutex_lock(&adap->lock);
|
||||
}
|
||||
adap->is_claiming_log_addrs = false;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -178,7 +178,7 @@ static long cec_adap_s_log_addrs(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU |
|
||||
CEC_LOG_ADDRS_FL_CDC_ONLY;
|
||||
mutex_lock(&adap->lock);
|
||||
if (!adap->is_configuring &&
|
||||
if (!adap->is_claiming_log_addrs && !adap->is_configuring &&
|
||||
(!log_addrs.num_log_addrs || !adap->is_configured) &&
|
||||
!cec_is_busy(adap, fh)) {
|
||||
err = __cec_s_log_addrs(adap, &log_addrs, block);
|
||||
@ -664,6 +664,8 @@ static int cec_release(struct inode *inode, struct file *filp)
|
||||
list_del_init(&data->xfer_list);
|
||||
}
|
||||
mutex_unlock(&adap->lock);
|
||||
|
||||
mutex_lock(&fh->lock);
|
||||
while (!list_empty(&fh->msgs)) {
|
||||
struct cec_msg_entry *entry =
|
||||
list_first_entry(&fh->msgs, struct cec_msg_entry, list);
|
||||
@ -681,6 +683,7 @@ static int cec_release(struct inode *inode, struct file *filp)
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&fh->lock);
|
||||
kfree(fh);
|
||||
|
||||
cec_put_device(devnode);
|
||||
|
@ -62,12 +62,12 @@ int cec_get_device(struct cec_devnode *devnode)
|
||||
*/
|
||||
mutex_lock(&devnode->lock);
|
||||
/*
|
||||
* return ENXIO if the cec device has been removed
|
||||
* return ENODEV if the cec device has been removed
|
||||
* already or if it is not registered anymore.
|
||||
*/
|
||||
if (!devnode->registered) {
|
||||
mutex_unlock(&devnode->lock);
|
||||
return -ENXIO;
|
||||
return -ENODEV;
|
||||
}
|
||||
/* and increase the device refcount */
|
||||
get_device(&devnode->dev);
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/pci.h>
|
||||
@ -573,6 +574,12 @@ static void cros_ec_cec_remove(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
static const struct platform_device_id cros_ec_cec_id[] = {
|
||||
{ DRV_NAME, 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, cros_ec_cec_id);
|
||||
|
||||
static struct platform_driver cros_ec_cec_driver = {
|
||||
.probe = cros_ec_cec_probe,
|
||||
.remove_new = cros_ec_cec_remove,
|
||||
@ -580,6 +587,7 @@ static struct platform_driver cros_ec_cec_driver = {
|
||||
.name = DRV_NAME,
|
||||
.pm = &cros_ec_cec_pm_ops,
|
||||
},
|
||||
.id_table = cros_ec_cec_id,
|
||||
};
|
||||
|
||||
module_platform_driver(cros_ec_cec_driver);
|
||||
@ -587,4 +595,3 @@ module_platform_driver(cros_ec_cec_driver);
|
||||
MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
|
||||
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
|
@ -122,7 +122,7 @@ static int calculate_h_scale_registers(struct saa7146_dev *dev,
|
||||
xacm = 0;
|
||||
|
||||
/* set horizontal filter parameters (CXY = CXUV) */
|
||||
cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff;
|
||||
cxy = hps_h_coeff_tab[min(xpsc - 1, 63)].hps_coeff;
|
||||
cxuv = cxy;
|
||||
|
||||
/* calculate and set horizontal fine scale (xsci) */
|
||||
@ -151,7 +151,7 @@ static int calculate_h_scale_registers(struct saa7146_dev *dev,
|
||||
xacm = 0;
|
||||
/* get best match in the table of attenuations
|
||||
for horizontal scaling */
|
||||
h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum;
|
||||
h_atten = hps_h_coeff_tab[min(xpsc - 1, 63)].weight_sum;
|
||||
|
||||
for (i = 0; h_attenuation[i] != 0; i++) {
|
||||
if (h_attenuation[i] >= h_atten)
|
||||
@ -283,10 +283,10 @@ static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field
|
||||
}
|
||||
|
||||
/* get filter coefficients for cya, cyb from table hps_v_coeff_tab */
|
||||
cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff;
|
||||
cya_cyb = hps_v_coeff_tab[min(yacl, 63)].hps_coeff;
|
||||
|
||||
/* get best match in the table of attenuations for vertical scaling */
|
||||
v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum;
|
||||
v_atten = hps_v_coeff_tab[min(yacl, 63)].weight_sum;
|
||||
|
||||
for (i = 0; v_attenuation[i] != 0; i++) {
|
||||
if (v_attenuation[i] >= v_atten)
|
||||
|
@ -421,11 +421,12 @@ static void init_buffer_cache_hints(struct vb2_queue *q, struct vb2_buffer *vb)
|
||||
*/
|
||||
static void vb2_queue_add_buffer(struct vb2_queue *q, struct vb2_buffer *vb, unsigned int index)
|
||||
{
|
||||
WARN_ON(index >= q->max_num_buffers || q->bufs[index] || vb->vb2_queue);
|
||||
WARN_ON(index >= q->max_num_buffers || test_bit(index, q->bufs_bitmap) || vb->vb2_queue);
|
||||
|
||||
q->bufs[index] = vb;
|
||||
vb->index = index;
|
||||
vb->vb2_queue = q;
|
||||
set_bit(index, q->bufs_bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -434,6 +435,7 @@ static void vb2_queue_add_buffer(struct vb2_queue *q, struct vb2_buffer *vb, uns
|
||||
*/
|
||||
static void vb2_queue_remove_buffer(struct vb2_buffer *vb)
|
||||
{
|
||||
clear_bit(vb->index, vb->vb2_queue->bufs_bitmap);
|
||||
vb->vb2_queue->bufs[vb->index] = NULL;
|
||||
vb->vb2_queue = NULL;
|
||||
}
|
||||
@ -442,16 +444,19 @@ static void vb2_queue_remove_buffer(struct vb2_buffer *vb)
|
||||
* __vb2_queue_alloc() - allocate vb2 buffer structures and (for MMAP type)
|
||||
* video buffer memory for all buffers/planes on the queue and initializes the
|
||||
* queue
|
||||
* @first_index: index of the first created buffer, all newly allocated buffers
|
||||
* have indices in the range [first_index..first_index+count-1]
|
||||
*
|
||||
* Returns the number of buffers successfully allocated.
|
||||
*/
|
||||
static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
|
||||
unsigned int num_buffers, unsigned int num_planes,
|
||||
const unsigned plane_sizes[VB2_MAX_PLANES])
|
||||
const unsigned int plane_sizes[VB2_MAX_PLANES],
|
||||
unsigned int *first_index)
|
||||
{
|
||||
unsigned int q_num_buffers = vb2_get_num_buffers(q);
|
||||
unsigned int buffer, plane;
|
||||
struct vb2_buffer *vb;
|
||||
unsigned long index = q->max_num_buffers;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
@ -459,7 +464,25 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
|
||||
* in the queue is below q->max_num_buffers
|
||||
*/
|
||||
num_buffers = min_t(unsigned int, num_buffers,
|
||||
q->max_num_buffers - q_num_buffers);
|
||||
q->max_num_buffers - vb2_get_num_buffers(q));
|
||||
|
||||
while (num_buffers) {
|
||||
index = bitmap_find_next_zero_area(q->bufs_bitmap, q->max_num_buffers,
|
||||
0, num_buffers, 0);
|
||||
|
||||
if (index < q->max_num_buffers)
|
||||
break;
|
||||
/* Try to find free space for less buffers */
|
||||
num_buffers--;
|
||||
}
|
||||
|
||||
/* If there is no space left to allocate buffers return 0 to indicate the error */
|
||||
if (!num_buffers) {
|
||||
*first_index = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*first_index = index;
|
||||
|
||||
for (buffer = 0; buffer < num_buffers; ++buffer) {
|
||||
/* Allocate vb2 buffer structures */
|
||||
@ -479,7 +502,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
|
||||
vb->planes[plane].min_length = plane_sizes[plane];
|
||||
}
|
||||
|
||||
vb2_queue_add_buffer(q, vb, q_num_buffers + buffer);
|
||||
vb2_queue_add_buffer(q, vb, index++);
|
||||
call_void_bufop(q, init_buffer, vb);
|
||||
|
||||
/* Allocate video buffer memory for the MMAP type */
|
||||
@ -517,17 +540,16 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
|
||||
}
|
||||
|
||||
/*
|
||||
* __vb2_free_mem() - release all video buffer memory for a given queue
|
||||
* __vb2_free_mem() - release video buffer memory for a given range of
|
||||
* buffers in a given queue
|
||||
*/
|
||||
static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
|
||||
static void __vb2_free_mem(struct vb2_queue *q, unsigned int start, unsigned int count)
|
||||
{
|
||||
unsigned int buffer;
|
||||
unsigned int i;
|
||||
struct vb2_buffer *vb;
|
||||
unsigned int q_num_buffers = vb2_get_num_buffers(q);
|
||||
|
||||
for (buffer = q_num_buffers - buffers; buffer < q_num_buffers;
|
||||
++buffer) {
|
||||
vb = vb2_get_buffer(q, buffer);
|
||||
for (i = start; i < start + count; i++) {
|
||||
vb = vb2_get_buffer(q, i);
|
||||
if (!vb)
|
||||
continue;
|
||||
|
||||
@ -542,35 +564,33 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
|
||||
}
|
||||
|
||||
/*
|
||||
* __vb2_queue_free() - free buffers at the end of the queue - video memory and
|
||||
* __vb2_queue_free() - free @count buffers from @start index of the queue - video memory and
|
||||
* related information, if no buffers are left return the queue to an
|
||||
* uninitialized state. Might be called even if the queue has already been freed.
|
||||
*/
|
||||
static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
|
||||
static void __vb2_queue_free(struct vb2_queue *q, unsigned int start, unsigned int count)
|
||||
{
|
||||
unsigned int buffer;
|
||||
unsigned int q_num_buffers = vb2_get_num_buffers(q);
|
||||
unsigned int i;
|
||||
|
||||
lockdep_assert_held(&q->mmap_lock);
|
||||
|
||||
/* Call driver-provided cleanup function for each buffer, if provided */
|
||||
for (buffer = q_num_buffers - buffers; buffer < q_num_buffers;
|
||||
++buffer) {
|
||||
struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
|
||||
for (i = start; i < start + count; i++) {
|
||||
struct vb2_buffer *vb = vb2_get_buffer(q, i);
|
||||
|
||||
if (vb && vb->planes[0].mem_priv)
|
||||
call_void_vb_qop(vb, buf_cleanup, vb);
|
||||
}
|
||||
|
||||
/* Release video buffer memory */
|
||||
__vb2_free_mem(q, buffers);
|
||||
__vb2_free_mem(q, start, count);
|
||||
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
/*
|
||||
* Check that all the calls were balanced during the life-time of this
|
||||
* queue. If not then dump the counters to the kernel log.
|
||||
*/
|
||||
if (q_num_buffers) {
|
||||
if (vb2_get_num_buffers(q)) {
|
||||
bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming ||
|
||||
q->cnt_prepare_streaming != q->cnt_unprepare_streaming ||
|
||||
q->cnt_wait_prepare != q->cnt_wait_finish;
|
||||
@ -596,8 +616,8 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
|
||||
q->cnt_stop_streaming = 0;
|
||||
q->cnt_unprepare_streaming = 0;
|
||||
}
|
||||
for (buffer = 0; buffer < vb2_get_num_buffers(q); buffer++) {
|
||||
struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
|
||||
for (i = start; i < start + count; i++) {
|
||||
struct vb2_buffer *vb = vb2_get_buffer(q, i);
|
||||
bool unbalanced;
|
||||
|
||||
if (!vb)
|
||||
@ -614,7 +634,7 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
|
||||
|
||||
if (unbalanced) {
|
||||
pr_info("unbalanced counters for queue %p, buffer %d:\n",
|
||||
q, buffer);
|
||||
q, i);
|
||||
if (vb->cnt_buf_init != vb->cnt_buf_cleanup)
|
||||
pr_info(" buf_init: %u buf_cleanup: %u\n",
|
||||
vb->cnt_buf_init, vb->cnt_buf_cleanup);
|
||||
@ -648,9 +668,8 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
|
||||
#endif
|
||||
|
||||
/* Free vb2 buffers */
|
||||
for (buffer = q_num_buffers - buffers; buffer < q_num_buffers;
|
||||
++buffer) {
|
||||
struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
|
||||
for (i = start; i < start + count; i++) {
|
||||
struct vb2_buffer *vb = vb2_get_buffer(q, i);
|
||||
|
||||
if (!vb)
|
||||
continue;
|
||||
@ -659,7 +678,6 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
|
||||
kfree(vb);
|
||||
}
|
||||
|
||||
q->num_buffers -= buffers;
|
||||
if (!vb2_get_num_buffers(q)) {
|
||||
q->memory = VB2_MEMORY_UNKNOWN;
|
||||
INIT_LIST_HEAD(&q->queued_list);
|
||||
@ -691,7 +709,7 @@ EXPORT_SYMBOL(vb2_buffer_in_use);
|
||||
static bool __buffers_in_use(struct vb2_queue *q)
|
||||
{
|
||||
unsigned int buffer;
|
||||
for (buffer = 0; buffer < vb2_get_num_buffers(q); ++buffer) {
|
||||
for (buffer = 0; buffer < q->max_num_buffers; ++buffer) {
|
||||
struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
|
||||
|
||||
if (!vb)
|
||||
@ -813,6 +831,32 @@ static bool verify_coherency_flags(struct vb2_queue *q, bool non_coherent_mem)
|
||||
return true;
|
||||
}
|
||||
|
||||
static int vb2_core_allocated_buffers_storage(struct vb2_queue *q)
|
||||
{
|
||||
if (!q->bufs)
|
||||
q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL);
|
||||
if (!q->bufs)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!q->bufs_bitmap)
|
||||
q->bufs_bitmap = bitmap_zalloc(q->max_num_buffers, GFP_KERNEL);
|
||||
if (!q->bufs_bitmap) {
|
||||
kfree(q->bufs);
|
||||
q->bufs = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vb2_core_free_buffers_storage(struct vb2_queue *q)
|
||||
{
|
||||
kfree(q->bufs);
|
||||
q->bufs = NULL;
|
||||
bitmap_free(q->bufs_bitmap);
|
||||
q->bufs_bitmap = NULL;
|
||||
}
|
||||
|
||||
int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
unsigned int flags, unsigned int *count)
|
||||
{
|
||||
@ -820,7 +864,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
unsigned int q_num_bufs = vb2_get_num_buffers(q);
|
||||
unsigned plane_sizes[VB2_MAX_PLANES] = { };
|
||||
bool non_coherent_mem = flags & V4L2_MEMORY_FLAG_NON_COHERENT;
|
||||
unsigned int i;
|
||||
unsigned int i, first_index;
|
||||
int ret = 0;
|
||||
|
||||
if (q->streaming) {
|
||||
@ -851,9 +895,10 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
* queued without ever calling STREAMON.
|
||||
*/
|
||||
__vb2_queue_cancel(q);
|
||||
__vb2_queue_free(q, q_num_bufs);
|
||||
__vb2_queue_free(q, 0, q->max_num_buffers);
|
||||
mutex_unlock(&q->mmap_lock);
|
||||
|
||||
q->is_busy = 0;
|
||||
/*
|
||||
* In case of REQBUFS(0) return immediately without calling
|
||||
* driver's queue_setup() callback and allocating resources.
|
||||
@ -865,7 +910,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
/*
|
||||
* Make sure the requested values and current defaults are sane.
|
||||
*/
|
||||
num_buffers = max_t(unsigned int, *count, q->min_queued_buffers);
|
||||
num_buffers = max_t(unsigned int, *count, q->min_reqbufs_allocation);
|
||||
num_buffers = min_t(unsigned int, num_buffers, q->max_num_buffers);
|
||||
memset(q->alloc_devs, 0, sizeof(q->alloc_devs));
|
||||
/*
|
||||
@ -873,10 +918,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
* in the queue_setup op.
|
||||
*/
|
||||
mutex_lock(&q->mmap_lock);
|
||||
if (!q->bufs)
|
||||
q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL);
|
||||
if (!q->bufs)
|
||||
ret = -ENOMEM;
|
||||
ret = vb2_core_allocated_buffers_storage(q);
|
||||
q->memory = memory;
|
||||
mutex_unlock(&q->mmap_lock);
|
||||
if (ret)
|
||||
@ -906,8 +948,10 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
|
||||
/* Finally, allocate buffers and video memory */
|
||||
allocated_buffers =
|
||||
__vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes);
|
||||
__vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes, &first_index);
|
||||
if (allocated_buffers == 0) {
|
||||
/* There shouldn't be any buffers allocated, so first_index == 0 */
|
||||
WARN_ON(first_index);
|
||||
dprintk(q, 1, "memory allocation failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
@ -917,7 +961,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
* There is no point in continuing if we can't allocate the minimum
|
||||
* number of buffers needed by this vb2_queue.
|
||||
*/
|
||||
if (allocated_buffers < q->min_queued_buffers)
|
||||
if (allocated_buffers < q->min_reqbufs_allocation)
|
||||
ret = -ENOMEM;
|
||||
|
||||
/*
|
||||
@ -946,7 +990,6 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
}
|
||||
|
||||
mutex_lock(&q->mmap_lock);
|
||||
q->num_buffers = allocated_buffers;
|
||||
|
||||
if (ret < 0) {
|
||||
/*
|
||||
@ -954,7 +997,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
* from already queued buffers and it will reset q->memory to
|
||||
* VB2_MEMORY_UNKNOWN.
|
||||
*/
|
||||
__vb2_queue_free(q, allocated_buffers);
|
||||
__vb2_queue_free(q, first_index, allocated_buffers);
|
||||
mutex_unlock(&q->mmap_lock);
|
||||
return ret;
|
||||
}
|
||||
@ -966,6 +1009,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
*/
|
||||
*count = allocated_buffers;
|
||||
q->waiting_for_buffers = !q->is_output;
|
||||
q->is_busy = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -973,6 +1017,7 @@ error:
|
||||
mutex_lock(&q->mmap_lock);
|
||||
q->memory = VB2_MEMORY_UNKNOWN;
|
||||
mutex_unlock(&q->mmap_lock);
|
||||
vb2_core_free_buffers_storage(q);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vb2_core_reqbufs);
|
||||
@ -980,7 +1025,8 @@ EXPORT_SYMBOL_GPL(vb2_core_reqbufs);
|
||||
int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
unsigned int flags, unsigned int *count,
|
||||
unsigned int requested_planes,
|
||||
const unsigned int requested_sizes[])
|
||||
const unsigned int requested_sizes[],
|
||||
unsigned int *first_index)
|
||||
{
|
||||
unsigned int num_planes = 0, num_buffers, allocated_buffers;
|
||||
unsigned plane_sizes[VB2_MAX_PLANES] = { };
|
||||
@ -1005,11 +1051,8 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
* value in the queue_setup op.
|
||||
*/
|
||||
mutex_lock(&q->mmap_lock);
|
||||
ret = vb2_core_allocated_buffers_storage(q);
|
||||
q->memory = memory;
|
||||
if (!q->bufs)
|
||||
q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL);
|
||||
if (!q->bufs)
|
||||
ret = -ENOMEM;
|
||||
mutex_unlock(&q->mmap_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -1042,7 +1085,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
|
||||
/* Finally, allocate buffers and video memory */
|
||||
allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers,
|
||||
num_planes, plane_sizes);
|
||||
num_planes, plane_sizes, first_index);
|
||||
if (allocated_buffers == 0) {
|
||||
dprintk(q, 1, "memory allocation failed\n");
|
||||
ret = -ENOMEM;
|
||||
@ -1072,7 +1115,6 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
}
|
||||
|
||||
mutex_lock(&q->mmap_lock);
|
||||
q->num_buffers += allocated_buffers;
|
||||
|
||||
if (ret < 0) {
|
||||
/*
|
||||
@ -1080,7 +1122,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
* from already queued buffers and it will reset q->memory to
|
||||
* VB2_MEMORY_UNKNOWN.
|
||||
*/
|
||||
__vb2_queue_free(q, allocated_buffers);
|
||||
__vb2_queue_free(q, *first_index, allocated_buffers);
|
||||
mutex_unlock(&q->mmap_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
@ -1091,6 +1133,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
|
||||
* to the userspace.
|
||||
*/
|
||||
*count = allocated_buffers;
|
||||
q->is_busy = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1648,6 +1691,44 @@ int vb2_core_prepare_buf(struct vb2_queue *q, struct vb2_buffer *vb, void *pb)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vb2_core_prepare_buf);
|
||||
|
||||
int vb2_core_remove_bufs(struct vb2_queue *q, unsigned int start, unsigned int count)
|
||||
{
|
||||
unsigned int i, ret = 0;
|
||||
unsigned int q_num_bufs = vb2_get_num_buffers(q);
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
if (count > q_num_bufs)
|
||||
return -EINVAL;
|
||||
|
||||
if (start > q->max_num_buffers - count)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&q->mmap_lock);
|
||||
|
||||
/* Check that all buffers in the range exist */
|
||||
for (i = start; i < start + count; i++) {
|
||||
struct vb2_buffer *vb = vb2_get_buffer(q, i);
|
||||
|
||||
if (!vb) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
if (vb->state != VB2_BUF_STATE_DEQUEUED) {
|
||||
ret = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
__vb2_queue_free(q, start, count);
|
||||
dprintk(q, 2, "%u buffers removed\n", count);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&q->mmap_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vb2_core_remove_bufs);
|
||||
|
||||
/*
|
||||
* vb2_start_streaming() - Attempt to start streaming.
|
||||
* @q: videobuf2 queue
|
||||
@ -1694,7 +1775,7 @@ static int vb2_start_streaming(struct vb2_queue *q)
|
||||
* Forcefully reclaim buffers if the driver did not
|
||||
* correctly return them to vb2.
|
||||
*/
|
||||
for (i = 0; i < vb2_get_num_buffers(q); ++i) {
|
||||
for (i = 0; i < q->max_num_buffers; ++i) {
|
||||
vb = vb2_get_buffer(q, i);
|
||||
|
||||
if (!vb)
|
||||
@ -2100,7 +2181,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
|
||||
* to vb2 in stop_streaming().
|
||||
*/
|
||||
if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
|
||||
for (i = 0; i < vb2_get_num_buffers(q); i++) {
|
||||
for (i = 0; i < q->max_num_buffers; i++) {
|
||||
struct vb2_buffer *vb = vb2_get_buffer(q, i);
|
||||
|
||||
if (!vb)
|
||||
@ -2144,7 +2225,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
|
||||
* call to __fill_user_buffer() after buf_finish(). That order can't
|
||||
* be changed, so we can't move the buf_finish() to __vb2_dqbuf().
|
||||
*/
|
||||
for (i = 0; i < vb2_get_num_buffers(q); i++) {
|
||||
for (i = 0; i < q->max_num_buffers; i++) {
|
||||
struct vb2_buffer *vb;
|
||||
struct media_request *req;
|
||||
|
||||
@ -2503,7 +2584,7 @@ int vb2_core_queue_init(struct vb2_queue *q)
|
||||
WARN_ON(!q->ops->buf_queue))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(q->max_num_buffers > MAX_BUFFER_INDEX) ||
|
||||
if (WARN_ON(q->max_num_buffers < VB2_MAX_FRAME) ||
|
||||
WARN_ON(q->min_queued_buffers > q->max_num_buffers))
|
||||
return -EINVAL;
|
||||
|
||||
@ -2521,6 +2602,25 @@ int vb2_core_queue_init(struct vb2_queue *q)
|
||||
if (WARN_ON(q->supports_requests && q->min_queued_buffers))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The minimum requirement is 2: one buffer is used
|
||||
* by the hardware while the other is being processed by userspace.
|
||||
*/
|
||||
if (q->min_reqbufs_allocation < 2)
|
||||
q->min_reqbufs_allocation = 2;
|
||||
|
||||
/*
|
||||
* If the driver needs 'min_queued_buffers' in the queue before
|
||||
* calling start_streaming() then the minimum requirement is
|
||||
* 'min_queued_buffers + 1' to keep at least one buffer available
|
||||
* for userspace.
|
||||
*/
|
||||
if (q->min_reqbufs_allocation < q->min_queued_buffers + 1)
|
||||
q->min_reqbufs_allocation = q->min_queued_buffers + 1;
|
||||
|
||||
if (WARN_ON(q->min_reqbufs_allocation > q->max_num_buffers))
|
||||
return -EINVAL;
|
||||
|
||||
INIT_LIST_HEAD(&q->queued_list);
|
||||
INIT_LIST_HEAD(&q->done_list);
|
||||
spin_lock_init(&q->done_lock);
|
||||
@ -2552,9 +2652,9 @@ void vb2_core_queue_release(struct vb2_queue *q)
|
||||
__vb2_cleanup_fileio(q);
|
||||
__vb2_queue_cancel(q);
|
||||
mutex_lock(&q->mmap_lock);
|
||||
__vb2_queue_free(q, vb2_get_num_buffers(q));
|
||||
kfree(q->bufs);
|
||||
q->bufs = NULL;
|
||||
__vb2_queue_free(q, 0, q->max_num_buffers);
|
||||
vb2_core_free_buffers_storage(q);
|
||||
q->is_busy = 0;
|
||||
mutex_unlock(&q->mmap_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vb2_core_queue_release);
|
||||
@ -2713,7 +2813,6 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
|
||||
struct vb2_fileio_data *fileio;
|
||||
struct vb2_buffer *vb;
|
||||
int i, ret;
|
||||
unsigned int count = 0;
|
||||
|
||||
/*
|
||||
* Sanity check
|
||||
@ -2734,18 +2833,8 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
|
||||
if (q->streaming || vb2_get_num_buffers(q) > 0)
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* Start with q->min_queued_buffers + 1, driver can increase it in
|
||||
* queue_setup()
|
||||
*
|
||||
* 'min_queued_buffers' buffers need to be queued up before you
|
||||
* can start streaming, plus 1 for userspace (or in this case,
|
||||
* kernelspace) processing.
|
||||
*/
|
||||
count = max(2, q->min_queued_buffers + 1);
|
||||
|
||||
dprintk(q, 3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n",
|
||||
(read) ? "read" : "write", count, q->fileio_read_once,
|
||||
(read) ? "read" : "write", q->min_reqbufs_allocation, q->fileio_read_once,
|
||||
q->fileio_write_immediately);
|
||||
|
||||
fileio = kzalloc(sizeof(*fileio), GFP_KERNEL);
|
||||
@ -2759,13 +2848,19 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
|
||||
* Request buffers and use MMAP type to force driver
|
||||
* to allocate buffers by itself.
|
||||
*/
|
||||
fileio->count = count;
|
||||
fileio->count = q->min_reqbufs_allocation;
|
||||
fileio->memory = VB2_MEMORY_MMAP;
|
||||
fileio->type = q->type;
|
||||
q->fileio = fileio;
|
||||
ret = vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count);
|
||||
if (ret)
|
||||
goto err_kfree;
|
||||
/* vb2_fileio_data supports max VB2_MAX_FRAME buffers */
|
||||
if (fileio->count > VB2_MAX_FRAME) {
|
||||
dprintk(q, 1, "fileio: more than VB2_MAX_FRAME buffers requested\n");
|
||||
ret = -ENOSPC;
|
||||
goto err_reqbufs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Userspace can never add or delete buffers later, so there
|
||||
|
@ -685,7 +685,7 @@ static void vb2_set_flags_and_caps(struct vb2_queue *q, u32 memory,
|
||||
*flags &= V4L2_MEMORY_FLAG_NON_COHERENT;
|
||||
}
|
||||
|
||||
*caps = V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS;
|
||||
*caps |= V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS;
|
||||
if (q->io_modes & VB2_MMAP)
|
||||
*caps |= V4L2_BUF_CAP_SUPPORTS_MMAP;
|
||||
if (q->io_modes & VB2_USERPTR)
|
||||
@ -795,11 +795,15 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
|
||||
for (i = 0; i < requested_planes; i++)
|
||||
if (requested_sizes[i] == 0)
|
||||
return -EINVAL;
|
||||
return ret ? ret : vb2_core_create_bufs(q, create->memory,
|
||||
create->flags,
|
||||
&create->count,
|
||||
requested_planes,
|
||||
requested_sizes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return vb2_core_create_bufs(q, create->memory,
|
||||
create->flags,
|
||||
&create->count,
|
||||
requested_planes,
|
||||
requested_sizes,
|
||||
&create->index);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vb2_create_bufs);
|
||||
|
||||
@ -997,6 +1001,24 @@ EXPORT_SYMBOL_GPL(vb2_poll);
|
||||
|
||||
/* vb2 ioctl helpers */
|
||||
|
||||
int vb2_ioctl_remove_bufs(struct file *file, void *priv,
|
||||
struct v4l2_remove_buffers *d)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
|
||||
if (vdev->queue->type != d->type)
|
||||
return -EINVAL;
|
||||
|
||||
if (d->count == 0)
|
||||
return 0;
|
||||
|
||||
if (vb2_queue_is_busy(vdev->queue, file))
|
||||
return -EBUSY;
|
||||
|
||||
return vb2_core_remove_bufs(vdev->queue, d->index, d->count);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vb2_ioctl_remove_bufs);
|
||||
|
||||
int vb2_ioctl_reqbufs(struct file *file, void *priv,
|
||||
struct v4l2_requestbuffers *p)
|
||||
{
|
||||
|
@ -956,7 +956,7 @@ int dvb_usercopy(struct file *file,
|
||||
int (*func)(struct file *file,
|
||||
unsigned int cmd, void *arg))
|
||||
{
|
||||
char sbuf[128];
|
||||
char sbuf[128] = {};
|
||||
void *mbuf = NULL;
|
||||
void *parg = NULL;
|
||||
int err = -EINVAL;
|
||||
|
@ -174,6 +174,6 @@ struct as10x_register_addr {
|
||||
uint32_t addr;
|
||||
/* register mode access */
|
||||
uint8_t mode;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
@ -5,4 +5,4 @@ config DVB_CXD2880
|
||||
depends on DVB_CORE && SPI
|
||||
default m if !MEDIA_SUBDRV_AUTOSELECT
|
||||
help
|
||||
Say Y when you want to support this frontend.
|
||||
Say Y when you want to support this frontend.
|
||||
|
@ -33,7 +33,6 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
/*
|
||||
@ -1910,7 +1909,6 @@ struct drx_demod_instance {
|
||||
/* generic demodulator data */
|
||||
|
||||
struct i2c_adapter *i2c;
|
||||
const struct firmware *firmware;
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
|
@ -56,6 +56,7 @@ INCLUDE FILES
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
@ -1444,8 +1445,7 @@ static int drxdap_fasi_read_block(struct i2c_device_addr *dev_addr,
|
||||
|
||||
/* Read block from I2C **************************************************** */
|
||||
do {
|
||||
u16 todo = (datasize < DRXDAP_MAX_RCHUNKSIZE ?
|
||||
datasize : DRXDAP_MAX_RCHUNKSIZE);
|
||||
u16 todo = min(datasize, DRXDAP_MAX_RCHUNKSIZE);
|
||||
|
||||
bufx = 0;
|
||||
|
||||
@ -1659,7 +1659,7 @@ static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr,
|
||||
Address must be rewritten because HI is reset after data transport and
|
||||
expects an address.
|
||||
*/
|
||||
todo = (block_size < datasize ? block_size : datasize);
|
||||
todo = min(block_size, datasize);
|
||||
if (todo == 0) {
|
||||
u16 overhead_size_i2c_addr = 0;
|
||||
u16 data_block_size = 0;
|
||||
@ -1681,9 +1681,7 @@ static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr,
|
||||
first_err = st;
|
||||
}
|
||||
bufx = 0;
|
||||
todo =
|
||||
(data_block_size <
|
||||
datasize ? data_block_size : datasize);
|
||||
todo = min(data_block_size, datasize);
|
||||
}
|
||||
memcpy(&buf[bufx], data, todo);
|
||||
/* write (address if can do and) data */
|
||||
@ -11750,6 +11748,7 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod,
|
||||
u8 *mc_data = NULL;
|
||||
unsigned size;
|
||||
char *mc_file;
|
||||
const struct firmware *fw;
|
||||
|
||||
/* Check arguments */
|
||||
if (!mc_info || !mc_info->mc_file)
|
||||
@ -11757,28 +11756,22 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod,
|
||||
|
||||
mc_file = mc_info->mc_file;
|
||||
|
||||
if (!demod->firmware) {
|
||||
const struct firmware *fw = NULL;
|
||||
|
||||
rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent);
|
||||
if (rc < 0) {
|
||||
pr_err("Couldn't read firmware %s\n", mc_file);
|
||||
return rc;
|
||||
}
|
||||
demod->firmware = fw;
|
||||
|
||||
if (demod->firmware->size < 2 * sizeof(u16)) {
|
||||
rc = -EINVAL;
|
||||
pr_err("Firmware is too short!\n");
|
||||
goto release;
|
||||
}
|
||||
|
||||
pr_info("Firmware %s, size %zu\n",
|
||||
mc_file, demod->firmware->size);
|
||||
rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent);
|
||||
if (rc < 0) {
|
||||
pr_err("Couldn't read firmware %s\n", mc_file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
mc_data_init = demod->firmware->data;
|
||||
size = demod->firmware->size;
|
||||
if (fw->size < 2 * sizeof(u16)) {
|
||||
rc = -EINVAL;
|
||||
pr_err("Firmware is too short!\n");
|
||||
goto release;
|
||||
}
|
||||
|
||||
pr_info("Firmware %s, size %zu\n", mc_file, fw->size);
|
||||
|
||||
mc_data_init = fw->data;
|
||||
size = fw->size;
|
||||
|
||||
mc_data = (void *)mc_data_init;
|
||||
/* Check data */
|
||||
@ -11874,7 +11867,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod,
|
||||
0x0000)) {
|
||||
pr_err("error reading firmware at pos %zd\n",
|
||||
mc_data - mc_data_init);
|
||||
return -EIO;
|
||||
rc = -EIO;
|
||||
goto release;
|
||||
}
|
||||
|
||||
result = memcmp(curr_ptr, mc_data_buffer,
|
||||
@ -11883,7 +11877,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod,
|
||||
if (result) {
|
||||
pr_err("error verifying firmware at pos %zd\n",
|
||||
mc_data - mc_data_init);
|
||||
return -EIO;
|
||||
rc = -EIO;
|
||||
goto release;
|
||||
}
|
||||
|
||||
curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2));
|
||||
@ -11893,17 +11888,17 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod,
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
rc = -EINVAL;
|
||||
goto release;
|
||||
|
||||
}
|
||||
mc_data += mc_block_nr_bytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
rc = 0;
|
||||
|
||||
release:
|
||||
release_firmware(demod->firmware);
|
||||
demod->firmware = NULL;
|
||||
release_firmware(fw);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -12271,7 +12266,6 @@ static void drx39xxj_release(struct dvb_frontend *fe)
|
||||
kfree(demod->my_ext_attr);
|
||||
kfree(demod->my_common_attr);
|
||||
kfree(demod->my_i2c_dev_addr);
|
||||
release_firmware(demod->firmware);
|
||||
kfree(demod);
|
||||
kfree(state);
|
||||
}
|
||||
|
@ -2176,6 +2176,11 @@ static int lgdt3306a_probe(struct i2c_client *client)
|
||||
struct dvb_frontend *fe;
|
||||
int ret;
|
||||
|
||||
if (!client->dev.platform_data) {
|
||||
dev_err(&client->dev, "platform data is mandatory\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config = kmemdup(client->dev.platform_data,
|
||||
sizeof(struct lgdt3306a_config), GFP_KERNEL);
|
||||
if (config == NULL) {
|
||||
|
@ -1000,6 +1000,13 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
|
||||
/* to light up the LOCK led */
|
||||
ret = m88ds3103_update_bits(dev, 0x11, 0x80, 0x00);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev->delivery_system = c->delivery_system;
|
||||
|
||||
return 0;
|
||||
|
@ -1381,57 +1381,57 @@ static int config_ts(struct mxl *state, enum MXL_HYDRA_DEMOD_ID_E demod_id,
|
||||
u32 nco_count_min = 0;
|
||||
u32 clk_type = 0;
|
||||
|
||||
struct MXL_REG_FIELD_T xpt_sync_polarity[MXL_HYDRA_DEMOD_MAX] = {
|
||||
static const struct MXL_REG_FIELD_T xpt_sync_polarity[MXL_HYDRA_DEMOD_MAX] = {
|
||||
{0x90700010, 8, 1}, {0x90700010, 9, 1},
|
||||
{0x90700010, 10, 1}, {0x90700010, 11, 1},
|
||||
{0x90700010, 12, 1}, {0x90700010, 13, 1},
|
||||
{0x90700010, 14, 1}, {0x90700010, 15, 1} };
|
||||
struct MXL_REG_FIELD_T xpt_clock_polarity[MXL_HYDRA_DEMOD_MAX] = {
|
||||
static const struct MXL_REG_FIELD_T xpt_clock_polarity[MXL_HYDRA_DEMOD_MAX] = {
|
||||
{0x90700010, 16, 1}, {0x90700010, 17, 1},
|
||||
{0x90700010, 18, 1}, {0x90700010, 19, 1},
|
||||
{0x90700010, 20, 1}, {0x90700010, 21, 1},
|
||||
{0x90700010, 22, 1}, {0x90700010, 23, 1} };
|
||||
struct MXL_REG_FIELD_T xpt_valid_polarity[MXL_HYDRA_DEMOD_MAX] = {
|
||||
static const struct MXL_REG_FIELD_T xpt_valid_polarity[MXL_HYDRA_DEMOD_MAX] = {
|
||||
{0x90700014, 0, 1}, {0x90700014, 1, 1},
|
||||
{0x90700014, 2, 1}, {0x90700014, 3, 1},
|
||||
{0x90700014, 4, 1}, {0x90700014, 5, 1},
|
||||
{0x90700014, 6, 1}, {0x90700014, 7, 1} };
|
||||
struct MXL_REG_FIELD_T xpt_ts_clock_phase[MXL_HYDRA_DEMOD_MAX] = {
|
||||
static const struct MXL_REG_FIELD_T xpt_ts_clock_phase[MXL_HYDRA_DEMOD_MAX] = {
|
||||
{0x90700018, 0, 3}, {0x90700018, 4, 3},
|
||||
{0x90700018, 8, 3}, {0x90700018, 12, 3},
|
||||
{0x90700018, 16, 3}, {0x90700018, 20, 3},
|
||||
{0x90700018, 24, 3}, {0x90700018, 28, 3} };
|
||||
struct MXL_REG_FIELD_T xpt_lsb_first[MXL_HYDRA_DEMOD_MAX] = {
|
||||
static const struct MXL_REG_FIELD_T xpt_lsb_first[MXL_HYDRA_DEMOD_MAX] = {
|
||||
{0x9070000C, 16, 1}, {0x9070000C, 17, 1},
|
||||
{0x9070000C, 18, 1}, {0x9070000C, 19, 1},
|
||||
{0x9070000C, 20, 1}, {0x9070000C, 21, 1},
|
||||
{0x9070000C, 22, 1}, {0x9070000C, 23, 1} };
|
||||
struct MXL_REG_FIELD_T xpt_sync_byte[MXL_HYDRA_DEMOD_MAX] = {
|
||||
static const struct MXL_REG_FIELD_T xpt_sync_byte[MXL_HYDRA_DEMOD_MAX] = {
|
||||
{0x90700010, 0, 1}, {0x90700010, 1, 1},
|
||||
{0x90700010, 2, 1}, {0x90700010, 3, 1},
|
||||
{0x90700010, 4, 1}, {0x90700010, 5, 1},
|
||||
{0x90700010, 6, 1}, {0x90700010, 7, 1} };
|
||||
struct MXL_REG_FIELD_T xpt_enable_output[MXL_HYDRA_DEMOD_MAX] = {
|
||||
static const struct MXL_REG_FIELD_T xpt_enable_output[MXL_HYDRA_DEMOD_MAX] = {
|
||||
{0x9070000C, 0, 1}, {0x9070000C, 1, 1},
|
||||
{0x9070000C, 2, 1}, {0x9070000C, 3, 1},
|
||||
{0x9070000C, 4, 1}, {0x9070000C, 5, 1},
|
||||
{0x9070000C, 6, 1}, {0x9070000C, 7, 1} };
|
||||
struct MXL_REG_FIELD_T xpt_err_replace_sync[MXL_HYDRA_DEMOD_MAX] = {
|
||||
static const struct MXL_REG_FIELD_T xpt_err_replace_sync[MXL_HYDRA_DEMOD_MAX] = {
|
||||
{0x9070000C, 24, 1}, {0x9070000C, 25, 1},
|
||||
{0x9070000C, 26, 1}, {0x9070000C, 27, 1},
|
||||
{0x9070000C, 28, 1}, {0x9070000C, 29, 1},
|
||||
{0x9070000C, 30, 1}, {0x9070000C, 31, 1} };
|
||||
struct MXL_REG_FIELD_T xpt_err_replace_valid[MXL_HYDRA_DEMOD_MAX] = {
|
||||
static const struct MXL_REG_FIELD_T xpt_err_replace_valid[MXL_HYDRA_DEMOD_MAX] = {
|
||||
{0x90700014, 8, 1}, {0x90700014, 9, 1},
|
||||
{0x90700014, 10, 1}, {0x90700014, 11, 1},
|
||||
{0x90700014, 12, 1}, {0x90700014, 13, 1},
|
||||
{0x90700014, 14, 1}, {0x90700014, 15, 1} };
|
||||
struct MXL_REG_FIELD_T xpt_continuous_clock[MXL_HYDRA_DEMOD_MAX] = {
|
||||
static const struct MXL_REG_FIELD_T xpt_continuous_clock[MXL_HYDRA_DEMOD_MAX] = {
|
||||
{0x907001D4, 0, 1}, {0x907001D4, 1, 1},
|
||||
{0x907001D4, 2, 1}, {0x907001D4, 3, 1},
|
||||
{0x907001D4, 4, 1}, {0x907001D4, 5, 1},
|
||||
{0x907001D4, 6, 1}, {0x907001D4, 7, 1} };
|
||||
struct MXL_REG_FIELD_T xpt_nco_clock_rate[MXL_HYDRA_DEMOD_MAX] = {
|
||||
static const struct MXL_REG_FIELD_T xpt_nco_clock_rate[MXL_HYDRA_DEMOD_MAX] = {
|
||||
{0x90700044, 16, 80}, {0x90700044, 16, 81},
|
||||
{0x90700044, 16, 82}, {0x90700044, 16, 83},
|
||||
{0x90700044, 16, 84}, {0x90700044, 16, 85},
|
||||
|
@ -513,10 +513,8 @@ static int si2165_upload_firmware(struct si2165_state *state)
|
||||
ret = 0;
|
||||
state->firmware_loaded = true;
|
||||
error:
|
||||
if (fw) {
|
||||
release_firmware(fw);
|
||||
fw = NULL;
|
||||
}
|
||||
release_firmware(fw);
|
||||
fw = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1277,7 +1277,7 @@ static int stb0899_get_dev_id(struct stb0899_state *state)
|
||||
dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver);
|
||||
CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str);
|
||||
fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG);
|
||||
if (! (chip_id > 0)) {
|
||||
if (!chip_id) {
|
||||
dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899");
|
||||
|
||||
return -ENODEV;
|
||||
|
@ -410,6 +410,7 @@ static int tda10048_set_if(struct dvb_frontend *fe, u32 bw)
|
||||
struct tda10048_config *config = &state->config;
|
||||
int i;
|
||||
u32 if_freq_khz;
|
||||
u64 sample_freq;
|
||||
|
||||
dprintk(1, "%s(bw = %d)\n", __func__, bw);
|
||||
|
||||
@ -451,9 +452,11 @@ static int tda10048_set_if(struct dvb_frontend *fe, u32 bw)
|
||||
dprintk(1, "- pll_pfactor = %d\n", state->pll_pfactor);
|
||||
|
||||
/* Calculate the sample frequency */
|
||||
state->sample_freq = state->xtal_hz * (state->pll_mfactor + 45);
|
||||
state->sample_freq /= (state->pll_nfactor + 1);
|
||||
state->sample_freq /= (state->pll_pfactor + 4);
|
||||
sample_freq = state->xtal_hz;
|
||||
sample_freq *= state->pll_mfactor + 45;
|
||||
do_div(sample_freq, state->pll_nfactor + 1);
|
||||
do_div(sample_freq, state->pll_pfactor + 4);
|
||||
state->sample_freq = sample_freq;
|
||||
dprintk(1, "- sample_freq = %d\n", state->sample_freq);
|
||||
|
||||
/* Update the I/F */
|
||||
|
@ -328,7 +328,7 @@ static int CalcMainPLL(struct tda_state *state, u32 freq)
|
||||
|
||||
OscFreq = (u64) freq * (u64) Div;
|
||||
OscFreq *= (u64) 16384;
|
||||
do_div(OscFreq, (u64)16000000);
|
||||
do_div(OscFreq, 16000000);
|
||||
MainDiv = OscFreq;
|
||||
|
||||
state->m_Regs[MPD] = PostDiv & 0x77;
|
||||
@ -352,7 +352,7 @@ static int CalcCalPLL(struct tda_state *state, u32 freq)
|
||||
OscFreq = (u64)freq * (u64)Div;
|
||||
/* CalDiv = u32( OscFreq * 16384 / 16000000 ); */
|
||||
OscFreq *= (u64)16384;
|
||||
do_div(OscFreq, (u64)16000000);
|
||||
do_div(OscFreq, 16000000);
|
||||
CalDiv = OscFreq;
|
||||
|
||||
state->m_Regs[CPD] = PostDiv;
|
||||
|
@ -195,6 +195,7 @@ config VIDEO_IMX334
|
||||
config VIDEO_IMX335
|
||||
tristate "Sony IMX335 sensor support"
|
||||
depends on OF_GPIO
|
||||
select V4L2_CCI_I2C
|
||||
help
|
||||
This is a Video4Linux2 sensor driver for the Sony
|
||||
IMX335 camera.
|
||||
@ -405,6 +406,7 @@ config VIDEO_OV2740
|
||||
config VIDEO_OV4689
|
||||
tristate "OmniVision OV4689 sensor support"
|
||||
depends on GPIOLIB
|
||||
select V4L2_CCI_I2C
|
||||
help
|
||||
This is a Video4Linux2 sensor-level driver for the OmniVision
|
||||
OV4689 camera.
|
||||
|
@ -1486,7 +1486,7 @@ static int adv7180_probe(struct i2c_client *client)
|
||||
if (ret)
|
||||
goto err_media_entity_cleanup;
|
||||
|
||||
if (state->irq) {
|
||||
if (state->irq > 0) {
|
||||
ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
|
||||
KBUILD_MODNAME, state);
|
||||
|
@ -214,7 +214,7 @@ static int adv748x_hdmi_set_video_timings(struct adv748x_state *state,
|
||||
* v4l2_subdev_video_ops
|
||||
*/
|
||||
|
||||
static int adv748x_hdmi_s_dv_timings(struct v4l2_subdev *sd,
|
||||
static int adv748x_hdmi_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
|
||||
@ -254,7 +254,7 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd,
|
||||
static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
|
||||
@ -269,7 +269,7 @@ static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd,
|
||||
static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
|
||||
@ -392,9 +392,6 @@ static int adv748x_hdmi_g_pixelaspect(struct v4l2_subdev *sd,
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_video_ops adv748x_video_ops_hdmi = {
|
||||
.s_dv_timings = adv748x_hdmi_s_dv_timings,
|
||||
.g_dv_timings = adv748x_hdmi_g_dv_timings,
|
||||
.query_dv_timings = adv748x_hdmi_query_dv_timings,
|
||||
.g_input_status = adv748x_hdmi_g_input_status,
|
||||
.s_stream = adv748x_hdmi_s_stream,
|
||||
.g_pixelaspect = adv748x_hdmi_g_pixelaspect,
|
||||
@ -413,7 +410,7 @@ static int adv748x_hdmi_propagate_pixelrate(struct adv748x_hdmi *hdmi)
|
||||
if (!tx)
|
||||
return -ENOLINK;
|
||||
|
||||
adv748x_hdmi_query_dv_timings(&hdmi->sd, &timings);
|
||||
adv748x_hdmi_query_dv_timings(&hdmi->sd, 0, &timings);
|
||||
|
||||
return adv748x_csi2_set_pixelrate(tx, timings.bt.pixelclock);
|
||||
}
|
||||
@ -610,6 +607,9 @@ static const struct v4l2_subdev_pad_ops adv748x_pad_ops_hdmi = {
|
||||
.get_fmt = adv748x_hdmi_get_format,
|
||||
.get_edid = adv748x_hdmi_get_edid,
|
||||
.set_edid = adv748x_hdmi_set_edid,
|
||||
.s_dv_timings = adv748x_hdmi_s_dv_timings,
|
||||
.g_dv_timings = adv748x_hdmi_g_dv_timings,
|
||||
.query_dv_timings = adv748x_hdmi_query_dv_timings,
|
||||
.dv_timings_cap = adv748x_hdmi_dv_timings_cap,
|
||||
.enum_dv_timings = adv748x_hdmi_enum_dv_timings,
|
||||
};
|
||||
@ -734,7 +734,7 @@ int adv748x_hdmi_init(struct adv748x_hdmi *hdmi)
|
||||
struct v4l2_dv_timings cea1280x720 = V4L2_DV_BT_CEA_1280X720P30;
|
||||
int ret;
|
||||
|
||||
adv748x_hdmi_s_dv_timings(&hdmi->sd, &cea1280x720);
|
||||
adv748x_hdmi_s_dv_timings(&hdmi->sd, 0, &cea1280x720);
|
||||
|
||||
/* Initialise a default 16:9 aspect ratio */
|
||||
hdmi->aspect_ratio.numerator = 16;
|
||||
|
@ -995,8 +995,8 @@ static int adv7511_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7511_s_dv_timings(struct v4l2_subdev *sd,
|
||||
struct v4l2_dv_timings *timings)
|
||||
static int adv7511_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
struct v4l2_bt_timings *bt = &timings->bt;
|
||||
@ -1004,6 +1004,9 @@ static int adv7511_s_dv_timings(struct v4l2_subdev *sd,
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* quick sanity check */
|
||||
if (!v4l2_valid_dv_timings(timings, &adv7511_timings_cap, NULL, NULL))
|
||||
return -EINVAL;
|
||||
@ -1042,13 +1045,16 @@ static int adv7511_s_dv_timings(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7511_g_dv_timings(struct v4l2_subdev *sd,
|
||||
static int adv7511_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct adv7511_state *state = get_adv7511_state(sd);
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!timings)
|
||||
return -EINVAL;
|
||||
|
||||
@ -1078,8 +1084,6 @@ static int adv7511_dv_timings_cap(struct v4l2_subdev *sd,
|
||||
|
||||
static const struct v4l2_subdev_video_ops adv7511_video_ops = {
|
||||
.s_stream = adv7511_s_stream,
|
||||
.s_dv_timings = adv7511_s_dv_timings,
|
||||
.g_dv_timings = adv7511_g_dv_timings,
|
||||
};
|
||||
|
||||
/* ------------------------------ AUDIO OPS ------------------------------ */
|
||||
@ -1403,6 +1407,8 @@ static const struct v4l2_subdev_pad_ops adv7511_pad_ops = {
|
||||
.enum_mbus_code = adv7511_enum_mbus_code,
|
||||
.get_fmt = adv7511_get_fmt,
|
||||
.set_fmt = adv7511_set_fmt,
|
||||
.s_dv_timings = adv7511_s_dv_timings,
|
||||
.g_dv_timings = adv7511_g_dv_timings,
|
||||
.enum_dv_timings = adv7511_enum_dv_timings,
|
||||
.dv_timings_cap = adv7511_dv_timings_cap,
|
||||
};
|
||||
|
@ -1557,8 +1557,8 @@ static unsigned int adv76xx_read_hdmi_pixelclock(struct v4l2_subdev *sd)
|
||||
return freq;
|
||||
}
|
||||
|
||||
static int adv76xx_query_dv_timings(struct v4l2_subdev *sd,
|
||||
struct v4l2_dv_timings *timings)
|
||||
static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
const struct adv76xx_chip_info *info = state->info;
|
||||
@ -1687,8 +1687,8 @@ found:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv76xx_s_dv_timings(struct v4l2_subdev *sd,
|
||||
struct v4l2_dv_timings *timings)
|
||||
static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
struct v4l2_bt_timings *bt;
|
||||
@ -1730,8 +1730,8 @@ static int adv76xx_s_dv_timings(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv76xx_g_dv_timings(struct v4l2_subdev *sd,
|
||||
struct v4l2_dv_timings *timings)
|
||||
static int adv76xx_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct adv76xx_state *state = to_state(sd);
|
||||
|
||||
@ -2607,7 +2607,7 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
|
||||
stdi.lcf, stdi.bl, stdi.lcvs,
|
||||
stdi.interlaced ? "interlaced" : "progressive",
|
||||
stdi.hs_pol, stdi.vs_pol);
|
||||
if (adv76xx_query_dv_timings(sd, &timings))
|
||||
if (adv76xx_query_dv_timings(sd, 0, &timings))
|
||||
v4l2_info(sd, "No video detected\n");
|
||||
else
|
||||
v4l2_print_dv_timings(sd->name, "Detected format: ",
|
||||
@ -2726,9 +2726,6 @@ static const struct v4l2_subdev_core_ops adv76xx_core_ops = {
|
||||
static const struct v4l2_subdev_video_ops adv76xx_video_ops = {
|
||||
.s_routing = adv76xx_s_routing,
|
||||
.g_input_status = adv76xx_g_input_status,
|
||||
.s_dv_timings = adv76xx_s_dv_timings,
|
||||
.g_dv_timings = adv76xx_g_dv_timings,
|
||||
.query_dv_timings = adv76xx_query_dv_timings,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
|
||||
@ -2738,6 +2735,9 @@ static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
|
||||
.set_fmt = adv76xx_set_format,
|
||||
.get_edid = adv76xx_get_edid,
|
||||
.set_edid = adv76xx_set_edid,
|
||||
.s_dv_timings = adv76xx_s_dv_timings,
|
||||
.g_dv_timings = adv76xx_g_dv_timings,
|
||||
.query_dv_timings = adv76xx_query_dv_timings,
|
||||
.dv_timings_cap = adv76xx_dv_timings_cap,
|
||||
.enum_dv_timings = adv76xx_enum_dv_timings,
|
||||
};
|
||||
|
@ -1518,7 +1518,7 @@ static void adv7842_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
|
||||
timings->bt.flags |= V4L2_DV_FL_CAN_DETECT_REDUCED_FPS;
|
||||
}
|
||||
|
||||
static int adv7842_query_dv_timings(struct v4l2_subdev *sd,
|
||||
static int adv7842_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
@ -1527,6 +1527,9 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd,
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
memset(timings, 0, sizeof(struct v4l2_dv_timings));
|
||||
|
||||
/* SDP block */
|
||||
@ -1643,7 +1646,7 @@ found:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7842_s_dv_timings(struct v4l2_subdev *sd,
|
||||
static int adv7842_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
@ -1652,6 +1655,9 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd,
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (state->mode == ADV7842_MODE_SDP)
|
||||
return -ENODATA;
|
||||
|
||||
@ -1689,11 +1695,14 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7842_g_dv_timings(struct v4l2_subdev *sd,
|
||||
static int adv7842_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct adv7842_state *state = to_state(sd);
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (state->mode == ADV7842_MODE_SDP)
|
||||
return -ENODATA;
|
||||
*timings = state->timings;
|
||||
@ -2780,7 +2789,7 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
|
||||
"interlaced" : "progressive",
|
||||
hs_pol, vs_pol);
|
||||
}
|
||||
if (adv7842_query_dv_timings(sd, &timings))
|
||||
if (adv7842_query_dv_timings(sd, 0, &timings))
|
||||
v4l2_info(sd, "No video detected\n");
|
||||
else
|
||||
v4l2_print_dv_timings(sd->name, "Detected format: ",
|
||||
@ -3226,7 +3235,7 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd)
|
||||
|
||||
memset(&state->timings, 0, sizeof(struct v4l2_dv_timings));
|
||||
|
||||
adv7842_s_dv_timings(sd, &timings);
|
||||
adv7842_s_dv_timings(sd, 0, &timings);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -3298,9 +3307,6 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = {
|
||||
.s_routing = adv7842_s_routing,
|
||||
.querystd = adv7842_querystd,
|
||||
.g_input_status = adv7842_g_input_status,
|
||||
.s_dv_timings = adv7842_s_dv_timings,
|
||||
.g_dv_timings = adv7842_g_dv_timings,
|
||||
.query_dv_timings = adv7842_query_dv_timings,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_pad_ops adv7842_pad_ops = {
|
||||
@ -3309,6 +3315,9 @@ static const struct v4l2_subdev_pad_ops adv7842_pad_ops = {
|
||||
.set_fmt = adv7842_set_format,
|
||||
.get_edid = adv7842_get_edid,
|
||||
.set_edid = adv7842_set_edid,
|
||||
.s_dv_timings = adv7842_s_dv_timings,
|
||||
.g_dv_timings = adv7842_g_dv_timings,
|
||||
.query_dv_timings = adv7842_query_dv_timings,
|
||||
.enum_dv_timings = adv7842_enum_dv_timings,
|
||||
.dv_timings_cap = adv7842_dv_timings_cap,
|
||||
};
|
||||
|
@ -310,8 +310,8 @@ module_i2c_driver(dw9714_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
|
||||
MODULE_AUTHOR("Jian Xu Zheng");
|
||||
MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
|
||||
MODULE_AUTHOR("Jouni Ukkonen <jouni.ukkonen@intel.com>");
|
||||
MODULE_AUTHOR("Tommi Franttila <tommi.franttila@intel.com>");
|
||||
MODULE_AUTHOR("Yuning Pu");
|
||||
MODULE_AUTHOR("Jouni Ukkonen");
|
||||
MODULE_AUTHOR("Tommi Franttila");
|
||||
MODULE_DESCRIPTION("DW9714 VCM driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -1475,7 +1475,7 @@ err_mutex:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit et8ek8_remove(struct i2c_client *client)
|
||||
static void et8ek8_remove(struct i2c_client *client)
|
||||
{
|
||||
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
|
||||
struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
|
||||
@ -1517,7 +1517,7 @@ static struct i2c_driver et8ek8_i2c_driver = {
|
||||
.of_match_table = et8ek8_of_table,
|
||||
},
|
||||
.probe = et8ek8_probe,
|
||||
.remove = __exit_p(et8ek8_remove),
|
||||
.remove = et8ek8_remove,
|
||||
.id_table = et8ek8_id_table,
|
||||
};
|
||||
|
||||
|
@ -3,10 +3,13 @@
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-fwnode.h>
|
||||
@ -633,6 +636,11 @@ struct hi556 {
|
||||
struct v4l2_ctrl *hblank;
|
||||
struct v4l2_ctrl *exposure;
|
||||
|
||||
/* GPIOs, clocks, etc. */
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct clk *clk;
|
||||
struct regulator *avdd;
|
||||
|
||||
/* Current mode */
|
||||
const struct hi556_mode *cur_mode;
|
||||
|
||||
@ -1206,8 +1214,18 @@ static int hi556_check_hwcfg(struct device *dev)
|
||||
int ret = 0;
|
||||
unsigned int i, j;
|
||||
|
||||
if (!fwnode)
|
||||
return -ENXIO;
|
||||
/*
|
||||
* Sometimes the fwnode graph is initialized by the bridge driver,
|
||||
* wait for this.
|
||||
*/
|
||||
ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
|
||||
if (!ep)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
|
||||
fwnode_handle_put(ep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
|
||||
if (ret) {
|
||||
@ -1220,15 +1238,6 @@ static int hi556_check_hwcfg(struct device *dev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
|
||||
if (!ep)
|
||||
return -ENXIO;
|
||||
|
||||
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
|
||||
fwnode_handle_put(ep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) {
|
||||
dev_err(dev, "number of CSI2 data lanes %d is not supported",
|
||||
bus_cfg.bus.mipi_csi2.num_data_lanes);
|
||||
@ -1275,6 +1284,47 @@ static void hi556_remove(struct i2c_client *client)
|
||||
mutex_destroy(&hi556->mutex);
|
||||
}
|
||||
|
||||
static int hi556_suspend(struct device *dev)
|
||||
{
|
||||
struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
||||
struct hi556 *hi556 = to_hi556(sd);
|
||||
int ret;
|
||||
|
||||
gpiod_set_value_cansleep(hi556->reset_gpio, 1);
|
||||
|
||||
ret = regulator_disable(hi556->avdd);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to disable avdd: %d\n", ret);
|
||||
gpiod_set_value_cansleep(hi556->reset_gpio, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(hi556->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hi556_resume(struct device *dev)
|
||||
{
|
||||
struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
||||
struct hi556 *hi556 = to_hi556(sd);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(hi556->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_enable(hi556->avdd);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable avdd: %d\n", ret);
|
||||
clk_disable_unprepare(hi556->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpiod_set_value_cansleep(hi556->reset_gpio, 0);
|
||||
usleep_range(5000, 5500);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hi556_probe(struct i2c_client *client)
|
||||
{
|
||||
struct hi556 *hi556;
|
||||
@ -1294,12 +1344,35 @@ static int hi556_probe(struct i2c_client *client)
|
||||
|
||||
v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops);
|
||||
|
||||
hi556->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(hi556->reset_gpio))
|
||||
return dev_err_probe(&client->dev, PTR_ERR(hi556->reset_gpio),
|
||||
"failed to get reset GPIO\n");
|
||||
|
||||
hi556->clk = devm_clk_get_optional(&client->dev, "clk");
|
||||
if (IS_ERR(hi556->clk))
|
||||
return dev_err_probe(&client->dev, PTR_ERR(hi556->clk),
|
||||
"failed to get clock\n");
|
||||
|
||||
/* The regulator core will provide a "dummy" regulator if necessary */
|
||||
hi556->avdd = devm_regulator_get(&client->dev, "avdd");
|
||||
if (IS_ERR(hi556->avdd))
|
||||
return dev_err_probe(&client->dev, PTR_ERR(hi556->avdd),
|
||||
"failed to get avdd regulator\n");
|
||||
|
||||
full_power = acpi_dev_state_d0(&client->dev);
|
||||
if (full_power) {
|
||||
/* Ensure non ACPI managed resources are enabled */
|
||||
ret = hi556_resume(&client->dev);
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret,
|
||||
"failed to power on sensor\n");
|
||||
|
||||
ret = hi556_identify_module(hi556);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to find sensor: %d", ret);
|
||||
return ret;
|
||||
goto probe_error_power_off;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1344,9 +1417,16 @@ probe_error_v4l2_ctrl_handler_free:
|
||||
v4l2_ctrl_handler_free(hi556->sd.ctrl_handler);
|
||||
mutex_destroy(&hi556->mutex);
|
||||
|
||||
probe_error_power_off:
|
||||
if (full_power)
|
||||
hi556_suspend(&client->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEFINE_RUNTIME_DEV_PM_OPS(hi556_pm_ops, hi556_suspend, hi556_resume,
|
||||
NULL);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id hi556_acpi_ids[] = {
|
||||
{"INT3537"},
|
||||
@ -1360,6 +1440,7 @@ static struct i2c_driver hi556_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "hi556",
|
||||
.acpi_match_table = ACPI_PTR(hi556_acpi_ids),
|
||||
.pm = pm_sleep_ptr(&hi556_pm_ops),
|
||||
},
|
||||
.probe = hi556_probe,
|
||||
.remove = hi556_remove,
|
||||
|
@ -1114,6 +1114,7 @@ free_ctrl:
|
||||
v4l2_ctrl_handler_free(&imx214->ctrls);
|
||||
error_power_off:
|
||||
pm_runtime_disable(imx214->dev);
|
||||
regulator_bulk_disable(IMX214_NUM_SUPPLIES, imx214->supplies);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -551,8 +551,7 @@ static int imx219_init_controls(struct imx219 *imx219)
|
||||
|
||||
if (ctrl_hdlr->error) {
|
||||
ret = ctrl_hdlr->error;
|
||||
dev_err(&client->dev, "%s control init failed (%d)\n",
|
||||
__func__, ret);
|
||||
dev_err_probe(&client->dev, ret, "Control init failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -1024,17 +1023,15 @@ static int imx219_identify_module(struct imx219 *imx219)
|
||||
u64 val;
|
||||
|
||||
ret = cci_read(imx219->regmap, IMX219_REG_CHIP_ID, &val, NULL);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to read chip id %x\n",
|
||||
IMX219_CHIP_ID);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret,
|
||||
"failed to read chip id %x\n",
|
||||
IMX219_CHIP_ID);
|
||||
|
||||
if (val != IMX219_CHIP_ID) {
|
||||
dev_err(&client->dev, "chip id mismatch: %x!=%llx\n",
|
||||
IMX219_CHIP_ID, val);
|
||||
return -EIO;
|
||||
}
|
||||
if (val != IMX219_CHIP_ID)
|
||||
return dev_err_probe(&client->dev, -EIO,
|
||||
"chip id mismatch: %x!=%llx\n",
|
||||
IMX219_CHIP_ID, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1048,35 +1045,36 @@ static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219)
|
||||
int ret = -EINVAL;
|
||||
|
||||
endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
|
||||
if (!endpoint) {
|
||||
dev_err(dev, "endpoint node not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!endpoint)
|
||||
return dev_err_probe(dev, -EINVAL, "endpoint node not found\n");
|
||||
|
||||
if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
|
||||
dev_err(dev, "could not parse endpoint\n");
|
||||
dev_err_probe(dev, -EINVAL, "could not parse endpoint\n");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
/* Check the number of MIPI CSI2 data lanes */
|
||||
if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2 &&
|
||||
ep_cfg.bus.mipi_csi2.num_data_lanes != 4) {
|
||||
dev_err(dev, "only 2 or 4 data lanes are currently supported\n");
|
||||
dev_err_probe(dev, -EINVAL,
|
||||
"only 2 or 4 data lanes are currently supported\n");
|
||||
goto error_out;
|
||||
}
|
||||
imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes;
|
||||
|
||||
/* Check the link frequency set in device tree */
|
||||
if (!ep_cfg.nr_of_link_frequencies) {
|
||||
dev_err(dev, "link-frequency property not found in DT\n");
|
||||
dev_err_probe(dev, -EINVAL,
|
||||
"link-frequency property not found in DT\n");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (ep_cfg.nr_of_link_frequencies != 1 ||
|
||||
(ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ?
|
||||
IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) {
|
||||
dev_err(dev, "Link frequency not supported: %lld\n",
|
||||
ep_cfg.link_frequencies[0]);
|
||||
dev_err_probe(dev, -EINVAL,
|
||||
"Link frequency not supported: %lld\n",
|
||||
ep_cfg.link_frequencies[0]);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
@ -1107,31 +1105,25 @@ static int imx219_probe(struct i2c_client *client)
|
||||
return -EINVAL;
|
||||
|
||||
imx219->regmap = devm_cci_regmap_init_i2c(client, 16);
|
||||
if (IS_ERR(imx219->regmap)) {
|
||||
ret = PTR_ERR(imx219->regmap);
|
||||
dev_err(dev, "failed to initialize CCI: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(imx219->regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(imx219->regmap),
|
||||
"failed to initialize CCI\n");
|
||||
|
||||
/* Get system clock (xclk) */
|
||||
imx219->xclk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(imx219->xclk)) {
|
||||
dev_err(dev, "failed to get xclk\n");
|
||||
return PTR_ERR(imx219->xclk);
|
||||
}
|
||||
if (IS_ERR(imx219->xclk))
|
||||
return dev_err_probe(dev, PTR_ERR(imx219->xclk),
|
||||
"failed to get xclk\n");
|
||||
|
||||
imx219->xclk_freq = clk_get_rate(imx219->xclk);
|
||||
if (imx219->xclk_freq != IMX219_XCLK_FREQ) {
|
||||
dev_err(dev, "xclk frequency not supported: %d Hz\n",
|
||||
imx219->xclk_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (imx219->xclk_freq != IMX219_XCLK_FREQ)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"xclk frequency not supported: %d Hz\n",
|
||||
imx219->xclk_freq);
|
||||
|
||||
ret = imx219_get_regulators(imx219);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get regulators\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to get regulators\n");
|
||||
|
||||
/* Request optional enable pin */
|
||||
imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset",
|
||||
@ -1183,20 +1175,21 @@ static int imx219_probe(struct i2c_client *client)
|
||||
|
||||
ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to init entity pads: %d\n", ret);
|
||||
dev_err_probe(dev, ret, "failed to init entity pads\n");
|
||||
goto error_handler_free;
|
||||
}
|
||||
|
||||
imx219->sd.state_lock = imx219->ctrl_handler.lock;
|
||||
ret = v4l2_subdev_init_finalize(&imx219->sd);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "subdev init error: %d\n", ret);
|
||||
dev_err_probe(dev, ret, "subdev init error\n");
|
||||
goto error_media_entity;
|
||||
}
|
||||
|
||||
ret = v4l2_async_register_subdev_sensor(&imx219->sd);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
|
||||
dev_err_probe(dev, ret,
|
||||
"failed to register sensor sub-device\n");
|
||||
goto error_subdev_cleanup;
|
||||
}
|
||||
|
||||
|
@ -11,57 +11,108 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <media/v4l2-cci.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-fwnode.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
|
||||
/* Streaming Mode */
|
||||
#define IMX335_REG_MODE_SELECT 0x3000
|
||||
#define IMX335_MODE_STANDBY 0x01
|
||||
#define IMX335_MODE_STREAMING 0x00
|
||||
|
||||
/* Lines per frame */
|
||||
#define IMX335_REG_LPFR 0x3030
|
||||
|
||||
/* Chip ID */
|
||||
#define IMX335_REG_ID 0x3912
|
||||
#define IMX335_ID 0x00
|
||||
|
||||
/* Exposure control */
|
||||
#define IMX335_REG_SHUTTER 0x3058
|
||||
#define IMX335_EXPOSURE_MIN 1
|
||||
#define IMX335_EXPOSURE_OFFSET 9
|
||||
#define IMX335_EXPOSURE_STEP 1
|
||||
#define IMX335_EXPOSURE_DEFAULT 0x0648
|
||||
|
||||
/* Analog gain control */
|
||||
#define IMX335_REG_AGAIN 0x30e8
|
||||
#define IMX335_AGAIN_MIN 0
|
||||
#define IMX335_AGAIN_MAX 240
|
||||
#define IMX335_AGAIN_STEP 1
|
||||
#define IMX335_AGAIN_DEFAULT 0
|
||||
#define IMX335_REG_MODE_SELECT CCI_REG8(0x3000)
|
||||
#define IMX335_MODE_STANDBY 0x01
|
||||
#define IMX335_MODE_STREAMING 0x00
|
||||
|
||||
/* Group hold register */
|
||||
#define IMX335_REG_HOLD 0x3001
|
||||
#define IMX335_REG_HOLD CCI_REG8(0x3001)
|
||||
|
||||
#define IMX335_REG_MASTER_MODE CCI_REG8(0x3002)
|
||||
#define IMX335_REG_BCWAIT_TIME CCI_REG8(0x300c)
|
||||
#define IMX335_REG_CPWAIT_TIME CCI_REG8(0x300d)
|
||||
#define IMX335_REG_WINMODE CCI_REG8(0x3018)
|
||||
#define IMX335_REG_HTRIMMING_START CCI_REG16_LE(0x302c)
|
||||
#define IMX335_REG_HNUM CCI_REG8(0x302e)
|
||||
|
||||
/* Lines per frame */
|
||||
#define IMX335_REG_VMAX CCI_REG24_LE(0x3030)
|
||||
|
||||
#define IMX335_REG_OPB_SIZE_V CCI_REG8(0x304c)
|
||||
#define IMX335_REG_ADBIT CCI_REG8(0x3050)
|
||||
#define IMX335_REG_Y_OUT_SIZE CCI_REG16_LE(0x3056)
|
||||
|
||||
#define IMX335_REG_SHUTTER CCI_REG24_LE(0x3058)
|
||||
#define IMX335_EXPOSURE_MIN 1
|
||||
#define IMX335_EXPOSURE_OFFSET 9
|
||||
#define IMX335_EXPOSURE_STEP 1
|
||||
#define IMX335_EXPOSURE_DEFAULT 0x0648
|
||||
|
||||
#define IMX335_REG_AREA3_ST_ADR_1 CCI_REG16_LE(0x3074)
|
||||
#define IMX335_REG_AREA3_WIDTH_1 CCI_REG16_LE(0x3076)
|
||||
|
||||
/* Analog and Digital gain control */
|
||||
#define IMX335_REG_GAIN CCI_REG8(0x30e8)
|
||||
#define IMX335_AGAIN_MIN 0
|
||||
#define IMX335_AGAIN_MAX 100
|
||||
#define IMX335_AGAIN_STEP 1
|
||||
#define IMX335_AGAIN_DEFAULT 0
|
||||
|
||||
#define IMX335_REG_TPG_TESTCLKEN CCI_REG8(0x3148)
|
||||
|
||||
#define IMX335_REG_INCLKSEL1 CCI_REG16_LE(0x314c)
|
||||
#define IMX335_REG_INCLKSEL2 CCI_REG8(0x315a)
|
||||
#define IMX335_REG_INCLKSEL3 CCI_REG8(0x3168)
|
||||
#define IMX335_REG_INCLKSEL4 CCI_REG8(0x316a)
|
||||
|
||||
#define IMX335_REG_MDBIT CCI_REG8(0x319d)
|
||||
#define IMX335_REG_SYSMODE CCI_REG8(0x319e)
|
||||
|
||||
#define IMX335_REG_XVS_XHS_DRV CCI_REG8(0x31a1)
|
||||
|
||||
/* Test pattern generator */
|
||||
#define IMX335_REG_TPG 0x329e
|
||||
#define IMX335_TPG_ALL_000 0
|
||||
#define IMX335_TPG_ALL_FFF 1
|
||||
#define IMX335_TPG_ALL_555 2
|
||||
#define IMX335_TPG_ALL_AAA 3
|
||||
#define IMX335_TPG_TOG_555_AAA 4
|
||||
#define IMX335_TPG_TOG_AAA_555 5
|
||||
#define IMX335_TPG_TOG_000_555 6
|
||||
#define IMX335_TPG_TOG_555_000 7
|
||||
#define IMX335_TPG_TOG_000_FFF 8
|
||||
#define IMX335_TPG_TOG_FFF_000 9
|
||||
#define IMX335_TPG_H_COLOR_BARS 10
|
||||
#define IMX335_TPG_V_COLOR_BARS 11
|
||||
#define IMX335_REG_TPG_DIG_CLP_MODE CCI_REG8(0x3280)
|
||||
#define IMX335_REG_TPG_EN_DUOUT CCI_REG8(0x329c)
|
||||
#define IMX335_REG_TPG CCI_REG8(0x329e)
|
||||
#define IMX335_TPG_ALL_000 0
|
||||
#define IMX335_TPG_ALL_FFF 1
|
||||
#define IMX335_TPG_ALL_555 2
|
||||
#define IMX335_TPG_ALL_AAA 3
|
||||
#define IMX335_TPG_TOG_555_AAA 4
|
||||
#define IMX335_TPG_TOG_AAA_555 5
|
||||
#define IMX335_TPG_TOG_000_555 6
|
||||
#define IMX335_TPG_TOG_555_000 7
|
||||
#define IMX335_TPG_TOG_000_FFF 8
|
||||
#define IMX335_TPG_TOG_FFF_000 9
|
||||
#define IMX335_TPG_H_COLOR_BARS 10
|
||||
#define IMX335_TPG_V_COLOR_BARS 11
|
||||
#define IMX335_REG_TPG_COLORWIDTH CCI_REG8(0x32a0)
|
||||
|
||||
#define IMX335_REG_BLKLEVEL CCI_REG16_LE(0x3302)
|
||||
|
||||
#define IMX335_REG_WRJ_OPEN CCI_REG8(0x336c)
|
||||
|
||||
#define IMX335_REG_ADBIT1 CCI_REG16_LE(0x341c)
|
||||
|
||||
/* Chip ID */
|
||||
#define IMX335_REG_ID CCI_REG8(0x3912)
|
||||
#define IMX335_ID 0x00
|
||||
|
||||
/* Data Lanes */
|
||||
#define IMX335_REG_LANEMODE CCI_REG8(0x3a01)
|
||||
#define IMX335_2LANE 1
|
||||
#define IMX335_4LANE 3
|
||||
|
||||
#define IMX335_REG_TCLKPOST CCI_REG16_LE(0x3a18)
|
||||
#define IMX335_REG_TCLKPREPARE CCI_REG16_LE(0x3a1a)
|
||||
#define IMX335_REG_TCLK_TRAIL CCI_REG16_LE(0x3a1c)
|
||||
#define IMX335_REG_TCLK_ZERO CCI_REG16_LE(0x3a1e)
|
||||
#define IMX335_REG_THS_PREPARE CCI_REG16_LE(0x3a20)
|
||||
#define IMX335_REG_THS_ZERO CCI_REG16_LE(0x3a22)
|
||||
#define IMX335_REG_THS_TRAIL CCI_REG16_LE(0x3a24)
|
||||
#define IMX335_REG_THS_EXIT CCI_REG16_LE(0x3a26)
|
||||
#define IMX335_REG_TPLX CCI_REG16_LE(0x3a28)
|
||||
|
||||
/* Input clock rate */
|
||||
#define IMX335_INCLK_RATE 24000000
|
||||
#define IMX335_INCLK_RATE 24000000
|
||||
|
||||
/* CSI2 HW configuration */
|
||||
#define IMX335_LINK_FREQ_594MHz 594000000LL
|
||||
@ -69,9 +120,6 @@
|
||||
|
||||
#define IMX335_NUM_DATA_LANES 4
|
||||
|
||||
#define IMX335_REG_MIN 0x00
|
||||
#define IMX335_REG_MAX 0xfffff
|
||||
|
||||
/* IMX335 native and active pixel array size. */
|
||||
#define IMX335_NATIVE_WIDTH 2616U
|
||||
#define IMX335_NATIVE_HEIGHT 1964U
|
||||
@ -80,16 +128,6 @@
|
||||
#define IMX335_PIXEL_ARRAY_WIDTH 2592U
|
||||
#define IMX335_PIXEL_ARRAY_HEIGHT 1944U
|
||||
|
||||
/**
|
||||
* struct imx335_reg - imx335 sensor register
|
||||
* @address: Register address
|
||||
* @val: Register value
|
||||
*/
|
||||
struct imx335_reg {
|
||||
u16 address;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct imx335_reg_list - imx335 sensor register list
|
||||
* @num_of_regs: Number of registers in the list
|
||||
@ -97,7 +135,7 @@ struct imx335_reg {
|
||||
*/
|
||||
struct imx335_reg_list {
|
||||
u32 num_of_regs;
|
||||
const struct imx335_reg *regs;
|
||||
const struct cci_reg_sequence *regs;
|
||||
};
|
||||
|
||||
static const char * const imx335_supply_name[] = {
|
||||
@ -138,6 +176,7 @@ struct imx335_mode {
|
||||
* @pad: Media pad. Only one pad supported
|
||||
* @reset_gpio: Sensor reset gpio
|
||||
* @supplies: Regulator supplies to handle power control
|
||||
* @cci: CCI register map
|
||||
* @inclk: Sensor input clock
|
||||
* @ctrl_handler: V4L2 control handler
|
||||
* @link_freq_ctrl: Pointer to link frequency control
|
||||
@ -147,6 +186,7 @@ struct imx335_mode {
|
||||
* @exp_ctrl: Pointer to exposure control
|
||||
* @again_ctrl: Pointer to analog gain control
|
||||
* @vblank: Vertical blanking in lines
|
||||
* @lane_mode: Mode for number of connected data lanes
|
||||
* @cur_mode: Pointer to current selected sensor mode
|
||||
* @mutex: Mutex for serializing sensor controls
|
||||
* @link_freq_bitmap: Menu bitmap for link_freq_ctrl
|
||||
@ -159,6 +199,7 @@ struct imx335 {
|
||||
struct media_pad pad;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct regulator_bulk_data supplies[ARRAY_SIZE(imx335_supply_name)];
|
||||
struct regmap *cci;
|
||||
|
||||
struct clk *inclk;
|
||||
struct v4l2_ctrl_handler ctrl_handler;
|
||||
@ -171,6 +212,7 @@ struct imx335 {
|
||||
struct v4l2_ctrl *again_ctrl;
|
||||
};
|
||||
u32 vblank;
|
||||
u32 lane_mode;
|
||||
const struct imx335_mode *cur_mode;
|
||||
struct mutex mutex;
|
||||
unsigned long link_freq_bitmap;
|
||||
@ -210,140 +252,135 @@ static const int imx335_tpg_val[] = {
|
||||
};
|
||||
|
||||
/* Sensor mode registers */
|
||||
static const struct imx335_reg mode_2592x1940_regs[] = {
|
||||
{0x3000, 0x01},
|
||||
{0x3002, 0x00},
|
||||
{0x3018, 0x04},
|
||||
{0x302c, 0x3c},
|
||||
{0x302e, 0x20},
|
||||
{0x3056, 0x94},
|
||||
{0x3074, 0xc8},
|
||||
{0x3076, 0x28},
|
||||
{0x304c, 0x00},
|
||||
{0x31a1, 0x00},
|
||||
{0x3288, 0x21},
|
||||
{0x328a, 0x02},
|
||||
{0x3414, 0x05},
|
||||
{0x3416, 0x18},
|
||||
{0x3648, 0x01},
|
||||
{0x364a, 0x04},
|
||||
{0x364c, 0x04},
|
||||
{0x3678, 0x01},
|
||||
{0x367c, 0x31},
|
||||
{0x367e, 0x31},
|
||||
{0x3706, 0x10},
|
||||
{0x3708, 0x03},
|
||||
{0x3714, 0x02},
|
||||
{0x3715, 0x02},
|
||||
{0x3716, 0x01},
|
||||
{0x3717, 0x03},
|
||||
{0x371c, 0x3d},
|
||||
{0x371d, 0x3f},
|
||||
{0x372c, 0x00},
|
||||
{0x372d, 0x00},
|
||||
{0x372e, 0x46},
|
||||
{0x372f, 0x00},
|
||||
{0x3730, 0x89},
|
||||
{0x3731, 0x00},
|
||||
{0x3732, 0x08},
|
||||
{0x3733, 0x01},
|
||||
{0x3734, 0xfe},
|
||||
{0x3735, 0x05},
|
||||
{0x3740, 0x02},
|
||||
{0x375d, 0x00},
|
||||
{0x375e, 0x00},
|
||||
{0x375f, 0x11},
|
||||
{0x3760, 0x01},
|
||||
{0x3768, 0x1b},
|
||||
{0x3769, 0x1b},
|
||||
{0x376a, 0x1b},
|
||||
{0x376b, 0x1b},
|
||||
{0x376c, 0x1a},
|
||||
{0x376d, 0x17},
|
||||
{0x376e, 0x0f},
|
||||
{0x3776, 0x00},
|
||||
{0x3777, 0x00},
|
||||
{0x3778, 0x46},
|
||||
{0x3779, 0x00},
|
||||
{0x377a, 0x89},
|
||||
{0x377b, 0x00},
|
||||
{0x377c, 0x08},
|
||||
{0x377d, 0x01},
|
||||
{0x377e, 0x23},
|
||||
{0x377f, 0x02},
|
||||
{0x3780, 0xd9},
|
||||
{0x3781, 0x03},
|
||||
{0x3782, 0xf5},
|
||||
{0x3783, 0x06},
|
||||
{0x3784, 0xa5},
|
||||
{0x3788, 0x0f},
|
||||
{0x378a, 0xd9},
|
||||
{0x378b, 0x03},
|
||||
{0x378c, 0xeb},
|
||||
{0x378d, 0x05},
|
||||
{0x378e, 0x87},
|
||||
{0x378f, 0x06},
|
||||
{0x3790, 0xf5},
|
||||
{0x3792, 0x43},
|
||||
{0x3794, 0x7a},
|
||||
{0x3796, 0xa1},
|
||||
{0x37b0, 0x36},
|
||||
{0x3a00, 0x00},
|
||||
static const struct cci_reg_sequence mode_2592x1940_regs[] = {
|
||||
{ IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY },
|
||||
{ IMX335_REG_MASTER_MODE, 0x00 },
|
||||
{ IMX335_REG_WINMODE, 0x04 },
|
||||
{ IMX335_REG_HTRIMMING_START, 48 },
|
||||
{ IMX335_REG_HNUM, 2592 },
|
||||
{ IMX335_REG_Y_OUT_SIZE, 1944 },
|
||||
{ IMX335_REG_AREA3_ST_ADR_1, 176 },
|
||||
{ IMX335_REG_AREA3_WIDTH_1, 3928 },
|
||||
{ IMX335_REG_OPB_SIZE_V, 0 },
|
||||
{ IMX335_REG_XVS_XHS_DRV, 0x00 },
|
||||
{ CCI_REG8(0x3288), 0x21 },
|
||||
{ CCI_REG8(0x328a), 0x02 },
|
||||
{ CCI_REG8(0x3414), 0x05 },
|
||||
{ CCI_REG8(0x3416), 0x18 },
|
||||
{ CCI_REG8(0x3648), 0x01 },
|
||||
{ CCI_REG8(0x364a), 0x04 },
|
||||
{ CCI_REG8(0x364c), 0x04 },
|
||||
{ CCI_REG8(0x3678), 0x01 },
|
||||
{ CCI_REG8(0x367c), 0x31 },
|
||||
{ CCI_REG8(0x367e), 0x31 },
|
||||
{ CCI_REG8(0x3706), 0x10 },
|
||||
{ CCI_REG8(0x3708), 0x03 },
|
||||
{ CCI_REG8(0x3714), 0x02 },
|
||||
{ CCI_REG8(0x3715), 0x02 },
|
||||
{ CCI_REG8(0x3716), 0x01 },
|
||||
{ CCI_REG8(0x3717), 0x03 },
|
||||
{ CCI_REG8(0x371c), 0x3d },
|
||||
{ CCI_REG8(0x371d), 0x3f },
|
||||
{ CCI_REG8(0x372c), 0x00 },
|
||||
{ CCI_REG8(0x372d), 0x00 },
|
||||
{ CCI_REG8(0x372e), 0x46 },
|
||||
{ CCI_REG8(0x372f), 0x00 },
|
||||
{ CCI_REG8(0x3730), 0x89 },
|
||||
{ CCI_REG8(0x3731), 0x00 },
|
||||
{ CCI_REG8(0x3732), 0x08 },
|
||||
{ CCI_REG8(0x3733), 0x01 },
|
||||
{ CCI_REG8(0x3734), 0xfe },
|
||||
{ CCI_REG8(0x3735), 0x05 },
|
||||
{ CCI_REG8(0x3740), 0x02 },
|
||||
{ CCI_REG8(0x375d), 0x00 },
|
||||
{ CCI_REG8(0x375e), 0x00 },
|
||||
{ CCI_REG8(0x375f), 0x11 },
|
||||
{ CCI_REG8(0x3760), 0x01 },
|
||||
{ CCI_REG8(0x3768), 0x1b },
|
||||
{ CCI_REG8(0x3769), 0x1b },
|
||||
{ CCI_REG8(0x376a), 0x1b },
|
||||
{ CCI_REG8(0x376b), 0x1b },
|
||||
{ CCI_REG8(0x376c), 0x1a },
|
||||
{ CCI_REG8(0x376d), 0x17 },
|
||||
{ CCI_REG8(0x376e), 0x0f },
|
||||
{ CCI_REG8(0x3776), 0x00 },
|
||||
{ CCI_REG8(0x3777), 0x00 },
|
||||
{ CCI_REG8(0x3778), 0x46 },
|
||||
{ CCI_REG8(0x3779), 0x00 },
|
||||
{ CCI_REG8(0x377a), 0x89 },
|
||||
{ CCI_REG8(0x377b), 0x00 },
|
||||
{ CCI_REG8(0x377c), 0x08 },
|
||||
{ CCI_REG8(0x377d), 0x01 },
|
||||
{ CCI_REG8(0x377e), 0x23 },
|
||||
{ CCI_REG8(0x377f), 0x02 },
|
||||
{ CCI_REG8(0x3780), 0xd9 },
|
||||
{ CCI_REG8(0x3781), 0x03 },
|
||||
{ CCI_REG8(0x3782), 0xf5 },
|
||||
{ CCI_REG8(0x3783), 0x06 },
|
||||
{ CCI_REG8(0x3784), 0xa5 },
|
||||
{ CCI_REG8(0x3788), 0x0f },
|
||||
{ CCI_REG8(0x378a), 0xd9 },
|
||||
{ CCI_REG8(0x378b), 0x03 },
|
||||
{ CCI_REG8(0x378c), 0xeb },
|
||||
{ CCI_REG8(0x378d), 0x05 },
|
||||
{ CCI_REG8(0x378e), 0x87 },
|
||||
{ CCI_REG8(0x378f), 0x06 },
|
||||
{ CCI_REG8(0x3790), 0xf5 },
|
||||
{ CCI_REG8(0x3792), 0x43 },
|
||||
{ CCI_REG8(0x3794), 0x7a },
|
||||
{ CCI_REG8(0x3796), 0xa1 },
|
||||
{ CCI_REG8(0x37b0), 0x36 },
|
||||
{ CCI_REG8(0x3a00), 0x00 },
|
||||
};
|
||||
|
||||
static const struct imx335_reg raw10_framefmt_regs[] = {
|
||||
{0x3050, 0x00},
|
||||
{0x319d, 0x00},
|
||||
{0x341c, 0xff},
|
||||
{0x341d, 0x01},
|
||||
static const struct cci_reg_sequence raw10_framefmt_regs[] = {
|
||||
{ IMX335_REG_ADBIT, 0x00 },
|
||||
{ IMX335_REG_MDBIT, 0x00 },
|
||||
{ IMX335_REG_ADBIT1, 0x1ff },
|
||||
};
|
||||
|
||||
static const struct imx335_reg raw12_framefmt_regs[] = {
|
||||
{0x3050, 0x01},
|
||||
{0x319d, 0x01},
|
||||
{0x341c, 0x47},
|
||||
{0x341d, 0x00},
|
||||
static const struct cci_reg_sequence raw12_framefmt_regs[] = {
|
||||
{ IMX335_REG_ADBIT, 0x01 },
|
||||
{ IMX335_REG_MDBIT, 0x01 },
|
||||
{ IMX335_REG_ADBIT1, 0x47 },
|
||||
};
|
||||
|
||||
static const struct imx335_reg mipi_data_rate_1188Mbps[] = {
|
||||
{0x300c, 0x3b},
|
||||
{0x300d, 0x2a},
|
||||
{0x314c, 0xc6},
|
||||
{0x314d, 0x00},
|
||||
{0x315a, 0x02},
|
||||
{0x3168, 0xa0},
|
||||
{0x316a, 0x7e},
|
||||
{0x319e, 0x01},
|
||||
{0x3a18, 0x8f},
|
||||
{0x3a1a, 0x4f},
|
||||
{0x3a1c, 0x47},
|
||||
{0x3a1e, 0x37},
|
||||
{0x3a1f, 0x01},
|
||||
{0x3a20, 0x4f},
|
||||
{0x3a22, 0x87},
|
||||
{0x3a24, 0x4f},
|
||||
{0x3a26, 0x7f},
|
||||
{0x3a28, 0x3f},
|
||||
static const struct cci_reg_sequence mipi_data_rate_1188Mbps[] = {
|
||||
{ IMX335_REG_BCWAIT_TIME, 0x3b },
|
||||
{ IMX335_REG_CPWAIT_TIME, 0x2a },
|
||||
{ IMX335_REG_INCLKSEL1, 0x00c6 },
|
||||
{ IMX335_REG_INCLKSEL2, 0x02 },
|
||||
{ IMX335_REG_INCLKSEL3, 0xa0 },
|
||||
{ IMX335_REG_INCLKSEL4, 0x7e },
|
||||
{ IMX335_REG_SYSMODE, 0x01 },
|
||||
{ IMX335_REG_TCLKPOST, 0x8f },
|
||||
{ IMX335_REG_TCLKPREPARE, 0x4f },
|
||||
{ IMX335_REG_TCLK_TRAIL, 0x47 },
|
||||
{ IMX335_REG_TCLK_ZERO, 0x0137 },
|
||||
{ IMX335_REG_THS_PREPARE, 0x4f },
|
||||
{ IMX335_REG_THS_ZERO, 0x87 },
|
||||
{ IMX335_REG_THS_TRAIL, 0x4f },
|
||||
{ IMX335_REG_THS_EXIT, 0x7f },
|
||||
{ IMX335_REG_TPLX, 0x3f },
|
||||
};
|
||||
|
||||
static const struct imx335_reg mipi_data_rate_891Mbps[] = {
|
||||
{0x300c, 0x3b},
|
||||
{0x300d, 0x2a},
|
||||
{0x314c, 0x29},
|
||||
{0x314d, 0x01},
|
||||
{0x315a, 0x06},
|
||||
{0x3168, 0xa0},
|
||||
{0x316a, 0x7e},
|
||||
{0x319e, 0x02},
|
||||
{0x3a18, 0x7f},
|
||||
{0x3a1a, 0x37},
|
||||
{0x3a1c, 0x37},
|
||||
{0x3a1e, 0xf7},
|
||||
{0x3a20, 0x3f},
|
||||
{0x3a22, 0x6f},
|
||||
{0x3a24, 0x3f},
|
||||
{0x3a26, 0x5f},
|
||||
{0x3a28, 0x2f},
|
||||
static const struct cci_reg_sequence mipi_data_rate_891Mbps[] = {
|
||||
{ IMX335_REG_BCWAIT_TIME, 0x3b },
|
||||
{ IMX335_REG_CPWAIT_TIME, 0x2a },
|
||||
{ IMX335_REG_INCLKSEL1, 0x0129 },
|
||||
{ IMX335_REG_INCLKSEL2, 0x06 },
|
||||
{ IMX335_REG_INCLKSEL3, 0xa0 },
|
||||
{ IMX335_REG_INCLKSEL4, 0x7e },
|
||||
{ IMX335_REG_SYSMODE, 0x02 },
|
||||
{ IMX335_REG_TCLKPOST, 0x7f },
|
||||
{ IMX335_REG_TCLKPREPARE, 0x37 },
|
||||
{ IMX335_REG_TCLK_TRAIL, 0x37 },
|
||||
{ IMX335_REG_TCLK_ZERO, 0xf7 },
|
||||
{ IMX335_REG_THS_PREPARE, 0x3f },
|
||||
{ IMX335_REG_THS_ZERO, 0x6f },
|
||||
{ IMX335_REG_THS_TRAIL, 0x3f },
|
||||
{ IMX335_REG_THS_EXIT, 0x5f },
|
||||
{ IMX335_REG_TPLX, 0x2f },
|
||||
};
|
||||
|
||||
static const s64 link_freq[] = {
|
||||
@ -372,10 +409,10 @@ static const u32 imx335_mbus_codes[] = {
|
||||
/* Supported sensor mode configurations */
|
||||
static const struct imx335_mode supported_mode = {
|
||||
.width = 2592,
|
||||
.height = 1940,
|
||||
.height = 1944,
|
||||
.hblank = 342,
|
||||
.vblank = 2560,
|
||||
.vblank_min = 2560,
|
||||
.vblank = 2556,
|
||||
.vblank_min = 2556,
|
||||
.vblank_max = 133060,
|
||||
.pclk = 396000000,
|
||||
.reg_list = {
|
||||
@ -395,101 +432,6 @@ static inline struct imx335 *to_imx335(struct v4l2_subdev *subdev)
|
||||
return container_of(subdev, struct imx335, sd);
|
||||
}
|
||||
|
||||
/**
|
||||
* imx335_read_reg() - Read registers.
|
||||
* @imx335: pointer to imx335 device
|
||||
* @reg: register address
|
||||
* @len: length of bytes to read. Max supported bytes is 4
|
||||
* @val: pointer to register value to be filled.
|
||||
*
|
||||
* Big endian register addresses with little endian values.
|
||||
*
|
||||
* Return: 0 if successful, error code otherwise.
|
||||
*/
|
||||
static int imx335_read_reg(struct imx335 *imx335, u16 reg, u32 len, u32 *val)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&imx335->sd);
|
||||
struct i2c_msg msgs[2] = {0};
|
||||
u8 addr_buf[2] = {0};
|
||||
u8 data_buf[4] = {0};
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(len > 4))
|
||||
return -EINVAL;
|
||||
|
||||
put_unaligned_be16(reg, addr_buf);
|
||||
|
||||
/* Write register address */
|
||||
msgs[0].addr = client->addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = ARRAY_SIZE(addr_buf);
|
||||
msgs[0].buf = addr_buf;
|
||||
|
||||
/* Read data from register */
|
||||
msgs[1].addr = client->addr;
|
||||
msgs[1].flags = I2C_M_RD;
|
||||
msgs[1].len = len;
|
||||
msgs[1].buf = data_buf;
|
||||
|
||||
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (ret != ARRAY_SIZE(msgs))
|
||||
return -EIO;
|
||||
|
||||
*val = get_unaligned_le32(data_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* imx335_write_reg() - Write register
|
||||
* @imx335: pointer to imx335 device
|
||||
* @reg: register address
|
||||
* @len: length of bytes. Max supported bytes is 4
|
||||
* @val: register value
|
||||
*
|
||||
* Big endian register addresses with little endian values.
|
||||
*
|
||||
* Return: 0 if successful, error code otherwise.
|
||||
*/
|
||||
static int imx335_write_reg(struct imx335 *imx335, u16 reg, u32 len, u32 val)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&imx335->sd);
|
||||
u8 buf[6] = {0};
|
||||
|
||||
if (WARN_ON(len > 4))
|
||||
return -EINVAL;
|
||||
|
||||
put_unaligned_be16(reg, buf);
|
||||
put_unaligned_le32(val, buf + 2);
|
||||
if (i2c_master_send(client, buf, len + 2) != len + 2)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* imx335_write_regs() - Write a list of registers
|
||||
* @imx335: pointer to imx335 device
|
||||
* @regs: list of registers to be written
|
||||
* @len: length of registers array
|
||||
*
|
||||
* Return: 0 if successful. error code otherwise.
|
||||
*/
|
||||
static int imx335_write_regs(struct imx335 *imx335,
|
||||
const struct imx335_reg *regs, u32 len)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = imx335_write_reg(imx335, regs[i].address, 1, regs[i].val);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* imx335_update_controls() - Update control ranges based on streaming mode
|
||||
* @imx335: pointer to imx335 device
|
||||
@ -526,7 +468,8 @@ static int imx335_update_controls(struct imx335 *imx335,
|
||||
static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain)
|
||||
{
|
||||
u32 lpfr, shutter;
|
||||
int ret;
|
||||
int ret_hold;
|
||||
int ret = 0;
|
||||
|
||||
lpfr = imx335->vblank + imx335->cur_mode->height;
|
||||
shutter = lpfr - exposure;
|
||||
@ -534,64 +477,55 @@ static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain)
|
||||
dev_dbg(imx335->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u\n",
|
||||
exposure, gain, shutter, lpfr);
|
||||
|
||||
ret = imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = imx335_write_reg(imx335, IMX335_REG_LPFR, 3, lpfr);
|
||||
if (ret)
|
||||
goto error_release_group_hold;
|
||||
|
||||
ret = imx335_write_reg(imx335, IMX335_REG_SHUTTER, 3, shutter);
|
||||
if (ret)
|
||||
goto error_release_group_hold;
|
||||
|
||||
ret = imx335_write_reg(imx335, IMX335_REG_AGAIN, 2, gain);
|
||||
|
||||
error_release_group_hold:
|
||||
imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 0);
|
||||
cci_write(imx335->cci, IMX335_REG_HOLD, 1, &ret);
|
||||
cci_write(imx335->cci, IMX335_REG_VMAX, lpfr, &ret);
|
||||
cci_write(imx335->cci, IMX335_REG_SHUTTER, shutter, &ret);
|
||||
cci_write(imx335->cci, IMX335_REG_GAIN, gain, &ret);
|
||||
/*
|
||||
* Unconditionally attempt to release the hold, but track the
|
||||
* error if the unhold itself fails.
|
||||
*/
|
||||
ret_hold = cci_write(imx335->cci, IMX335_REG_HOLD, 0, NULL);
|
||||
if (ret_hold)
|
||||
ret = ret_hold;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (pattern_index >= ARRAY_SIZE(imx335_tpg_val))
|
||||
return -EINVAL;
|
||||
|
||||
if (pattern_index) {
|
||||
const struct imx335_reg tpg_enable_regs[] = {
|
||||
{ 0x3148, 0x10 },
|
||||
{ 0x3280, 0x00 },
|
||||
{ 0x329c, 0x01 },
|
||||
{ 0x32a0, 0x11 },
|
||||
{ 0x3302, 0x00 },
|
||||
{ 0x3303, 0x00 },
|
||||
{ 0x336c, 0x00 },
|
||||
const struct cci_reg_sequence tpg_enable_regs[] = {
|
||||
{ IMX335_REG_TPG_TESTCLKEN, 0x10 },
|
||||
{ IMX335_REG_TPG_DIG_CLP_MODE, 0x00 },
|
||||
{ IMX335_REG_TPG_EN_DUOUT, 0x01 },
|
||||
{ IMX335_REG_TPG_COLORWIDTH, 0x11 },
|
||||
{ IMX335_REG_BLKLEVEL, 0x00 },
|
||||
{ IMX335_REG_WRJ_OPEN, 0x00 },
|
||||
};
|
||||
|
||||
ret = imx335_write_reg(imx335, IMX335_REG_TPG, 1,
|
||||
imx335_tpg_val[pattern_index]);
|
||||
if (ret)
|
||||
return ret;
|
||||
cci_write(imx335->cci, IMX335_REG_TPG,
|
||||
imx335_tpg_val[pattern_index], &ret);
|
||||
|
||||
ret = imx335_write_regs(imx335, tpg_enable_regs,
|
||||
ARRAY_SIZE(tpg_enable_regs));
|
||||
cci_multi_reg_write(imx335->cci, tpg_enable_regs,
|
||||
ARRAY_SIZE(tpg_enable_regs), &ret);
|
||||
} else {
|
||||
const struct imx335_reg tpg_disable_regs[] = {
|
||||
{ 0x3148, 0x00 },
|
||||
{ 0x3280, 0x01 },
|
||||
{ 0x329c, 0x00 },
|
||||
{ 0x32a0, 0x10 },
|
||||
{ 0x3302, 0x32 },
|
||||
{ 0x3303, 0x00 },
|
||||
{ 0x336c, 0x01 },
|
||||
const struct cci_reg_sequence tpg_disable_regs[] = {
|
||||
{ IMX335_REG_TPG_TESTCLKEN, 0x00 },
|
||||
{ IMX335_REG_TPG_DIG_CLP_MODE, 0x01 },
|
||||
{ IMX335_REG_TPG_EN_DUOUT, 0x00 },
|
||||
{ IMX335_REG_TPG_COLORWIDTH, 0x10 },
|
||||
{ IMX335_REG_BLKLEVEL, 0x32 },
|
||||
{ IMX335_REG_WRJ_OPEN, 0x01 },
|
||||
};
|
||||
|
||||
ret = imx335_write_regs(imx335, tpg_disable_regs,
|
||||
ARRAY_SIZE(tpg_disable_regs));
|
||||
cci_multi_reg_write(imx335->cci, tpg_disable_regs,
|
||||
ARRAY_SIZE(tpg_disable_regs), &ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -890,12 +824,14 @@ static int imx335_set_framefmt(struct imx335 *imx335)
|
||||
{
|
||||
switch (imx335->cur_mbus_code) {
|
||||
case MEDIA_BUS_FMT_SRGGB10_1X10:
|
||||
return imx335_write_regs(imx335, raw10_framefmt_regs,
|
||||
ARRAY_SIZE(raw10_framefmt_regs));
|
||||
return cci_multi_reg_write(imx335->cci, raw10_framefmt_regs,
|
||||
ARRAY_SIZE(raw10_framefmt_regs),
|
||||
NULL);
|
||||
|
||||
case MEDIA_BUS_FMT_SRGGB12_1X12:
|
||||
return imx335_write_regs(imx335, raw12_framefmt_regs,
|
||||
ARRAY_SIZE(raw12_framefmt_regs));
|
||||
return cci_multi_reg_write(imx335->cci, raw12_framefmt_regs,
|
||||
ARRAY_SIZE(raw12_framefmt_regs),
|
||||
NULL);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
@ -914,7 +850,8 @@ static int imx335_start_streaming(struct imx335 *imx335)
|
||||
|
||||
/* Setup PLL */
|
||||
reg_list = &link_freq_reglist[__ffs(imx335->link_freq_bitmap)];
|
||||
ret = imx335_write_regs(imx335, reg_list->regs, reg_list->num_of_regs);
|
||||
ret = cci_multi_reg_write(imx335->cci, reg_list->regs,
|
||||
reg_list->num_of_regs, NULL);
|
||||
if (ret) {
|
||||
dev_err(imx335->dev, "%s failed to set plls\n", __func__);
|
||||
return ret;
|
||||
@ -922,8 +859,8 @@ static int imx335_start_streaming(struct imx335 *imx335)
|
||||
|
||||
/* Write sensor mode registers */
|
||||
reg_list = &imx335->cur_mode->reg_list;
|
||||
ret = imx335_write_regs(imx335, reg_list->regs,
|
||||
reg_list->num_of_regs);
|
||||
ret = cci_multi_reg_write(imx335->cci, reg_list->regs,
|
||||
reg_list->num_of_regs, NULL);
|
||||
if (ret) {
|
||||
dev_err(imx335->dev, "fail to write initial registers\n");
|
||||
return ret;
|
||||
@ -936,6 +873,12 @@ static int imx335_start_streaming(struct imx335 *imx335)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure lanes */
|
||||
ret = cci_write(imx335->cci, IMX335_REG_LANEMODE,
|
||||
imx335->lane_mode, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Setup handler will write actual exposure and gain */
|
||||
ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler);
|
||||
if (ret) {
|
||||
@ -944,8 +887,8 @@ static int imx335_start_streaming(struct imx335 *imx335)
|
||||
}
|
||||
|
||||
/* Start streaming */
|
||||
ret = imx335_write_reg(imx335, IMX335_REG_MODE_SELECT,
|
||||
1, IMX335_MODE_STREAMING);
|
||||
ret = cci_write(imx335->cci, IMX335_REG_MODE_SELECT,
|
||||
IMX335_MODE_STREAMING, NULL);
|
||||
if (ret) {
|
||||
dev_err(imx335->dev, "fail to start streaming\n");
|
||||
return ret;
|
||||
@ -965,8 +908,8 @@ static int imx335_start_streaming(struct imx335 *imx335)
|
||||
*/
|
||||
static int imx335_stop_streaming(struct imx335 *imx335)
|
||||
{
|
||||
return imx335_write_reg(imx335, IMX335_REG_MODE_SELECT,
|
||||
1, IMX335_MODE_STANDBY);
|
||||
return cci_write(imx335->cci, IMX335_REG_MODE_SELECT,
|
||||
IMX335_MODE_STANDBY, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1017,14 +960,14 @@ error_unlock:
|
||||
static int imx335_detect(struct imx335 *imx335)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
u64 val;
|
||||
|
||||
ret = imx335_read_reg(imx335, IMX335_REG_ID, 2, &val);
|
||||
ret = cci_read(imx335->cci, IMX335_REG_ID, &val, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val != IMX335_ID) {
|
||||
dev_err(imx335->dev, "chip id mismatch: %x!=%x\n",
|
||||
dev_err(imx335->dev, "chip id mismatch: %x!=%llx\n",
|
||||
IMX335_ID, val);
|
||||
return -ENXIO;
|
||||
}
|
||||
@ -1096,7 +1039,14 @@ static int imx335_parse_hw_config(struct imx335 *imx335)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX335_NUM_DATA_LANES) {
|
||||
switch (bus_cfg.bus.mipi_csi2.num_data_lanes) {
|
||||
case 2:
|
||||
imx335->lane_mode = IMX335_2LANE;
|
||||
break;
|
||||
case 4:
|
||||
imx335->lane_mode = IMX335_4LANE;
|
||||
break;
|
||||
default:
|
||||
dev_err(imx335->dev,
|
||||
"number of CSI2 data lanes %d is not supported\n",
|
||||
bus_cfg.bus.mipi_csi2.num_data_lanes);
|
||||
@ -1208,10 +1158,16 @@ static int imx335_init_controls(struct imx335 *imx335)
|
||||
{
|
||||
struct v4l2_ctrl_handler *ctrl_hdlr = &imx335->ctrl_handler;
|
||||
const struct imx335_mode *mode = imx335->cur_mode;
|
||||
struct v4l2_fwnode_device_properties props;
|
||||
u32 lpfr;
|
||||
int ret;
|
||||
|
||||
ret = v4l2_ctrl_handler_init(ctrl_hdlr, 7);
|
||||
ret = v4l2_fwnode_device_parse(imx335->dev, &props);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* v4l2_fwnode_device_properties can add two more controls */
|
||||
ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1228,6 +1184,14 @@ static int imx335_init_controls(struct imx335 *imx335)
|
||||
IMX335_EXPOSURE_STEP,
|
||||
IMX335_EXPOSURE_DEFAULT);
|
||||
|
||||
/*
|
||||
* The sensor has an analog gain and a digital gain, both controlled
|
||||
* through a single gain value, expressed in 0.3dB increments. Values
|
||||
* from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values
|
||||
* up to 72.0dB (240) add further digital gain. Limit the range to
|
||||
* analog gain only, support for digital gain can be added separately
|
||||
* if needed.
|
||||
*/
|
||||
imx335->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
|
||||
&imx335_ctrl_ops,
|
||||
V4L2_CID_ANALOGUE_GAIN,
|
||||
@ -1276,6 +1240,8 @@ static int imx335_init_controls(struct imx335 *imx335)
|
||||
if (imx335->hblank_ctrl)
|
||||
imx335->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
||||
|
||||
v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx335_ctrl_ops, &props);
|
||||
|
||||
if (ctrl_hdlr->error) {
|
||||
dev_err(imx335->dev, "control init failed: %d\n",
|
||||
ctrl_hdlr->error);
|
||||
@ -1304,6 +1270,11 @@ static int imx335_probe(struct i2c_client *client)
|
||||
return -ENOMEM;
|
||||
|
||||
imx335->dev = &client->dev;
|
||||
imx335->cci = devm_cci_regmap_init_i2c(client, 16);
|
||||
if (IS_ERR(imx335->cci)) {
|
||||
dev_err(imx335->dev, "Unable to initialize I2C\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Initialize subdev */
|
||||
v4l2_i2c_subdev_init(&imx335->sd, client, &imx335_subdev_ops);
|
||||
|
@ -8,6 +8,9 @@
|
||||
* Copyright (C) 2015 Cogent Embedded, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __MEDIA_I2C_MAX9271_H__
|
||||
#define __MEDIA_I2C_MAX9271_H__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#define MAX9271_DEFAULT_ADDR 0x40
|
||||
@ -231,3 +234,5 @@ int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr);
|
||||
* Return 0 on success or a negative error code on failure
|
||||
*/
|
||||
int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest);
|
||||
|
||||
#endif /* __MEDIA_I2C_MAX9271_H__ */
|
||||
|
@ -75,6 +75,8 @@
|
||||
#define OV2680_ACTIVE_START_TOP 8
|
||||
#define OV2680_MIN_CROP_WIDTH 2
|
||||
#define OV2680_MIN_CROP_HEIGHT 2
|
||||
#define OV2680_MIN_VBLANK 4
|
||||
#define OV2680_MAX_VBLANK 0xffff
|
||||
|
||||
/* Fixed pre-div of 1/2 */
|
||||
#define OV2680_PLL_PREDIV0 2
|
||||
@ -84,10 +86,7 @@
|
||||
|
||||
/* 66MHz pixel clock: 66MHz / 1704 * 1294 = 30fps */
|
||||
#define OV2680_PIXELS_PER_LINE 1704
|
||||
#define OV2680_LINES_PER_FRAME 1294
|
||||
|
||||
/* If possible send 16 extra rows / lines to the ISP as padding */
|
||||
#define OV2680_END_MARGIN 16
|
||||
#define OV2680_LINES_PER_FRAME_30FPS 1294
|
||||
|
||||
/* Max exposure time is VTS - 8 */
|
||||
#define OV2680_INTEGRATION_TIME_MARGIN 8
|
||||
@ -130,6 +129,8 @@ struct ov2680_ctrls {
|
||||
struct v4l2_ctrl *test_pattern;
|
||||
struct v4l2_ctrl *link_freq;
|
||||
struct v4l2_ctrl *pixel_rate;
|
||||
struct v4l2_ctrl *vblank;
|
||||
struct v4l2_ctrl *hblank;
|
||||
};
|
||||
|
||||
struct ov2680_mode {
|
||||
@ -143,8 +144,6 @@ struct ov2680_mode {
|
||||
u16 v_end;
|
||||
u16 h_output_size;
|
||||
u16 v_output_size;
|
||||
u16 hts;
|
||||
u16 vts;
|
||||
};
|
||||
|
||||
struct ov2680_dev {
|
||||
@ -359,15 +358,11 @@ static void ov2680_calc_mode(struct ov2680_dev *sensor)
|
||||
sensor->mode.v_start = (sensor->mode.crop.top +
|
||||
(sensor->mode.crop.height - height) / 2) & ~1;
|
||||
sensor->mode.h_end =
|
||||
min(sensor->mode.h_start + width + OV2680_END_MARGIN - 1,
|
||||
OV2680_NATIVE_WIDTH - 1);
|
||||
min(sensor->mode.h_start + width - 1, OV2680_NATIVE_WIDTH - 1);
|
||||
sensor->mode.v_end =
|
||||
min(sensor->mode.v_start + height + OV2680_END_MARGIN - 1,
|
||||
OV2680_NATIVE_HEIGHT - 1);
|
||||
min(sensor->mode.v_start + height - 1, OV2680_NATIVE_HEIGHT - 1);
|
||||
sensor->mode.h_output_size = orig_width;
|
||||
sensor->mode.v_output_size = orig_height;
|
||||
sensor->mode.hts = OV2680_PIXELS_PER_LINE;
|
||||
sensor->mode.vts = OV2680_LINES_PER_FRAME;
|
||||
}
|
||||
|
||||
static int ov2680_set_mode(struct ov2680_dev *sensor)
|
||||
@ -402,9 +397,8 @@ static int ov2680_set_mode(struct ov2680_dev *sensor)
|
||||
cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE,
|
||||
sensor->mode.v_output_size, &ret);
|
||||
cci_write(sensor->regmap, OV2680_REG_TIMING_HTS,
|
||||
sensor->mode.hts, &ret);
|
||||
cci_write(sensor->regmap, OV2680_REG_TIMING_VTS,
|
||||
sensor->mode.vts, &ret);
|
||||
OV2680_PIXELS_PER_LINE, &ret);
|
||||
/* VTS gets set by the vblank ctrl */
|
||||
cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret);
|
||||
cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret);
|
||||
cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret);
|
||||
@ -478,6 +472,15 @@ static int ov2680_exposure_set(struct ov2680_dev *sensor, u32 exp)
|
||||
NULL);
|
||||
}
|
||||
|
||||
static int ov2680_exposure_update_range(struct ov2680_dev *sensor)
|
||||
{
|
||||
int exp_max = sensor->mode.fmt.height + sensor->ctrls.vblank->val -
|
||||
OV2680_INTEGRATION_TIME_MARGIN;
|
||||
|
||||
return __v4l2_ctrl_modify_range(sensor->ctrls.exposure, 0, exp_max,
|
||||
1, exp_max);
|
||||
}
|
||||
|
||||
static int ov2680_stream_enable(struct ov2680_dev *sensor)
|
||||
{
|
||||
int ret;
|
||||
@ -644,7 +647,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_framefmt *try_fmt;
|
||||
const struct v4l2_rect *crop;
|
||||
unsigned int width, height;
|
||||
int ret = 0;
|
||||
int def, max, ret = 0;
|
||||
|
||||
crop = __ov2680_get_pad_crop(sensor, sd_state, format->pad,
|
||||
format->which);
|
||||
@ -673,6 +676,27 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
|
||||
sensor->mode.fmt = format->format;
|
||||
ov2680_calc_mode(sensor);
|
||||
|
||||
/* vblank range is height dependent adjust and reset to default */
|
||||
max = OV2680_MAX_VBLANK - height;
|
||||
def = OV2680_LINES_PER_FRAME_30FPS - height;
|
||||
ret = __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV2680_MIN_VBLANK,
|
||||
max, 1, def);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, def);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
/* exposure range depends on vts which may have changed */
|
||||
ret = ov2680_exposure_update_range(sensor);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
/* adjust hblank value for new width */
|
||||
def = OV2680_PIXELS_PER_LINE - width;
|
||||
ret = __v4l2_ctrl_modify_range(sensor->ctrls.hblank, def, def, 1, def);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&sensor->lock);
|
||||
|
||||
@ -842,6 +866,13 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
struct ov2680_dev *sensor = to_ov2680_dev(sd);
|
||||
int ret;
|
||||
|
||||
/* Update exposure range on vblank changes */
|
||||
if (ctrl->id == V4L2_CID_VBLANK) {
|
||||
ret = ov2680_exposure_update_range(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Only apply changes to the controls if the device is powered up */
|
||||
if (!pm_runtime_get_if_in_use(sensor->sd.dev)) {
|
||||
ov2680_set_bayer_order(sensor, &sensor->mode.fmt);
|
||||
@ -864,6 +895,10 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
case V4L2_CID_TEST_PATTERN:
|
||||
ret = ov2680_test_pattern_set(sensor, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_VBLANK:
|
||||
ret = cci_write(sensor->regmap, OV2680_REG_TIMING_VTS,
|
||||
sensor->mode.fmt.height + ctrl->val, NULL);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
@ -922,8 +957,8 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
|
||||
const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops;
|
||||
struct ov2680_ctrls *ctrls = &sensor->ctrls;
|
||||
struct v4l2_ctrl_handler *hdl = &ctrls->handler;
|
||||
int exp_max = OV2680_LINES_PER_FRAME - OV2680_INTEGRATION_TIME_MARGIN;
|
||||
int ret = 0;
|
||||
struct v4l2_fwnode_device_properties props;
|
||||
int def, max, ret = 0;
|
||||
|
||||
v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops);
|
||||
sensor->sd.internal_ops = &ov2680_internal_ops;
|
||||
@ -948,8 +983,9 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
|
||||
ARRAY_SIZE(test_pattern_menu) - 1,
|
||||
0, 0, test_pattern_menu);
|
||||
|
||||
max = OV2680_LINES_PER_FRAME_30FPS - OV2680_INTEGRATION_TIME_MARGIN;
|
||||
ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
|
||||
0, exp_max, 1, exp_max);
|
||||
0, max, 1, max);
|
||||
|
||||
ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
|
||||
0, 1023, 1, 250);
|
||||
@ -960,6 +996,21 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
|
||||
0, sensor->pixel_rate,
|
||||
1, sensor->pixel_rate);
|
||||
|
||||
max = OV2680_MAX_VBLANK - OV2680_DEFAULT_HEIGHT;
|
||||
def = OV2680_LINES_PER_FRAME_30FPS - OV2680_DEFAULT_HEIGHT;
|
||||
ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
|
||||
OV2680_MIN_VBLANK, max, 1, def);
|
||||
|
||||
def = OV2680_PIXELS_PER_LINE - OV2680_DEFAULT_WIDTH;
|
||||
ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK,
|
||||
def, def, 1, def);
|
||||
|
||||
ret = v4l2_fwnode_device_parse(sensor->dev, &props);
|
||||
if (ret)
|
||||
goto cleanup_entity;
|
||||
|
||||
v4l2_ctrl_new_fwnode_properties(hdl, ops, &props);
|
||||
|
||||
if (hdl->error) {
|
||||
ret = hdl->error;
|
||||
goto cleanup_entity;
|
||||
@ -968,6 +1019,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
|
||||
ctrls->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
|
||||
ctrls->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
|
||||
ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
||||
ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
||||
|
||||
sensor->sd.ctrl_handler = hdl;
|
||||
|
||||
@ -1116,25 +1168,24 @@ static int ov2680_parse_dt(struct ov2680_dev *sensor)
|
||||
sensor->pixel_rate = sensor->link_freq[0] * 2;
|
||||
do_div(sensor->pixel_rate, 10);
|
||||
|
||||
/* Verify bus cfg */
|
||||
if (bus_cfg.bus.mipi_csi2.num_data_lanes != 1) {
|
||||
ret = dev_err_probe(dev, -EINVAL,
|
||||
"only a 1-lane CSI2 config is supported");
|
||||
goto out_free_bus_cfg;
|
||||
if (!bus_cfg.nr_of_link_frequencies) {
|
||||
dev_warn(dev, "Consider passing 'link-frequencies' in DT\n");
|
||||
goto skip_link_freq_validation;
|
||||
}
|
||||
|
||||
for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
|
||||
if (bus_cfg.link_frequencies[i] == sensor->link_freq[0])
|
||||
break;
|
||||
|
||||
if (bus_cfg.nr_of_link_frequencies == 0 ||
|
||||
bus_cfg.nr_of_link_frequencies == i) {
|
||||
if (bus_cfg.nr_of_link_frequencies == i) {
|
||||
ret = dev_err_probe(dev, -EINVAL,
|
||||
"supported link freq %lld not found\n",
|
||||
sensor->link_freq[0]);
|
||||
goto out_free_bus_cfg;
|
||||
}
|
||||
|
||||
skip_link_freq_validation:
|
||||
ret = 0;
|
||||
out_free_bus_cfg:
|
||||
v4l2_fwnode_endpoint_free(&bus_cfg);
|
||||
return ret;
|
||||
|
@ -768,14 +768,15 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
|
||||
cur_mode = ov2740->cur_mode;
|
||||
size = ARRAY_SIZE(link_freq_menu_items);
|
||||
|
||||
ov2740->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops,
|
||||
V4L2_CID_LINK_FREQ,
|
||||
size - 1, 0,
|
||||
link_freq_menu_items);
|
||||
ov2740->link_freq =
|
||||
v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops,
|
||||
V4L2_CID_LINK_FREQ, size - 1,
|
||||
ov2740->supported_modes->link_freq_index,
|
||||
link_freq_menu_items);
|
||||
if (ov2740->link_freq)
|
||||
ov2740->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
||||
|
||||
pixel_rate = to_pixel_rate(OV2740_LINK_FREQ_360MHZ_INDEX);
|
||||
pixel_rate = to_pixel_rate(ov2740->supported_modes->link_freq_index);
|
||||
ov2740->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
|
||||
V4L2_CID_PIXEL_RATE, 0,
|
||||
pixel_rate, 1, pixel_rate);
|
||||
@ -1332,9 +1333,16 @@ static int ov2740_probe(struct i2c_client *client)
|
||||
return dev_err_probe(dev, ret, "failed to check HW configuration\n");
|
||||
|
||||
ov2740->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ov2740->reset_gpio))
|
||||
if (IS_ERR(ov2740->reset_gpio)) {
|
||||
return dev_err_probe(dev, PTR_ERR(ov2740->reset_gpio),
|
||||
"failed to get reset GPIO\n");
|
||||
} else if (ov2740->reset_gpio) {
|
||||
/*
|
||||
* Ensure reset is asserted for at least 20 ms before
|
||||
* ov2740_resume() deasserts it.
|
||||
*/
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
ov2740->clk = devm_clk_get_optional(dev, "clk");
|
||||
if (IS_ERR(ov2740->clk))
|
||||
|
@ -3,7 +3,7 @@
|
||||
* ov4689 driver
|
||||
*
|
||||
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
||||
* Copyright (C) 2022 Mikhail Rudenko
|
||||
* Copyright (C) 2022, 2024 Mikhail Rudenko
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
@ -15,45 +15,86 @@
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <media/media-entity.h>
|
||||
#include <media/v4l2-async.h>
|
||||
#include <media/v4l2-cci.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-fwnode.h>
|
||||
|
||||
#define CHIP_ID 0x004688
|
||||
#define OV4689_REG_CHIP_ID 0x300a
|
||||
|
||||
#define OV4689_XVCLK_FREQ 24000000
|
||||
|
||||
#define OV4689_REG_CTRL_MODE 0x0100
|
||||
#define OV4689_REG_CTRL_MODE CCI_REG8(0x0100)
|
||||
#define OV4689_MODE_SW_STANDBY 0x0
|
||||
#define OV4689_MODE_STREAMING BIT(0)
|
||||
|
||||
#define OV4689_REG_EXPOSURE 0x3500
|
||||
#define OV4689_REG_CHIP_ID CCI_REG16(0x300a)
|
||||
#define CHIP_ID 0x004688
|
||||
|
||||
#define OV4689_REG_EXPOSURE CCI_REG24(0x3500)
|
||||
#define OV4689_EXPOSURE_MIN 4
|
||||
#define OV4689_EXPOSURE_STEP 1
|
||||
#define OV4689_VTS_MAX 0x7fff
|
||||
|
||||
#define OV4689_REG_GAIN_H 0x3508
|
||||
#define OV4689_REG_GAIN_L 0x3509
|
||||
#define OV4689_GAIN_H_MASK 0x07
|
||||
#define OV4689_GAIN_H_SHIFT 8
|
||||
#define OV4689_GAIN_L_MASK 0xff
|
||||
#define OV4689_REG_GAIN CCI_REG16(0x3508)
|
||||
#define OV4689_GAIN_STEP 1
|
||||
#define OV4689_GAIN_DEFAULT 0x80
|
||||
|
||||
#define OV4689_REG_TEST_PATTERN 0x5040
|
||||
#define OV4689_REG_DIG_GAIN CCI_REG16(0x352a)
|
||||
#define OV4689_DIG_GAIN_MIN 1
|
||||
#define OV4689_DIG_GAIN_MAX 0x7fff
|
||||
#define OV4689_DIG_GAIN_STEP 1
|
||||
#define OV4689_DIG_GAIN_DEFAULT 0x800
|
||||
|
||||
#define OV4689_REG_H_CROP_START CCI_REG16(0x3800)
|
||||
#define OV4689_REG_V_CROP_START CCI_REG16(0x3802)
|
||||
#define OV4689_REG_H_CROP_END CCI_REG16(0x3804)
|
||||
#define OV4689_REG_V_CROP_END CCI_REG16(0x3806)
|
||||
#define OV4689_REG_H_OUTPUT_SIZE CCI_REG16(0x3808)
|
||||
#define OV4689_REG_V_OUTPUT_SIZE CCI_REG16(0x380a)
|
||||
|
||||
#define OV4689_REG_HTS CCI_REG16(0x380c)
|
||||
#define OV4689_HTS_DIVIDER 4
|
||||
#define OV4689_HTS_MAX 0x7fff
|
||||
|
||||
#define OV4689_REG_VTS CCI_REG16(0x380e)
|
||||
#define OV4689_VTS_MAX 0x7fff
|
||||
|
||||
#define OV4689_REG_H_WIN_OFF CCI_REG16(0x3810)
|
||||
#define OV4689_REG_V_WIN_OFF CCI_REG16(0x3812)
|
||||
|
||||
#define OV4689_REG_TIMING_FORMAT1 CCI_REG8(0x3820) /* Vertical */
|
||||
#define OV4689_REG_TIMING_FORMAT2 CCI_REG8(0x3821) /* Horizontal */
|
||||
#define OV4689_TIMING_FLIP_MASK GENMASK(2, 1)
|
||||
#define OV4689_TIMING_FLIP_ARRAY BIT(1)
|
||||
#define OV4689_TIMING_FLIP_DIGITAL BIT(2)
|
||||
#define OV4689_TIMING_FLIP_BOTH (OV4689_TIMING_FLIP_ARRAY |\
|
||||
OV4689_TIMING_FLIP_DIGITAL)
|
||||
|
||||
#define OV4689_REG_ANCHOR_LEFT_START CCI_REG16(0x4020)
|
||||
#define OV4689_ANCHOR_LEFT_START_DEF 576
|
||||
#define OV4689_REG_ANCHOR_LEFT_END CCI_REG16(0x4022)
|
||||
#define OV4689_ANCHOR_LEFT_END_DEF 831
|
||||
#define OV4689_REG_ANCHOR_RIGHT_START CCI_REG16(0x4024)
|
||||
#define OV4689_ANCHOR_RIGHT_START_DEF 1984
|
||||
#define OV4689_REG_ANCHOR_RIGHT_END CCI_REG16(0x4026)
|
||||
#define OV4689_ANCHOR_RIGHT_END_DEF 2239
|
||||
|
||||
#define OV4689_REG_VFIFO_CTRL_01 CCI_REG8(0x4601)
|
||||
|
||||
#define OV4689_REG_WB_GAIN_RED CCI_REG16(0x500c)
|
||||
#define OV4689_REG_WB_GAIN_BLUE CCI_REG16(0x5010)
|
||||
#define OV4689_WB_GAIN_MIN 1
|
||||
#define OV4689_WB_GAIN_MAX 0xfff
|
||||
#define OV4689_WB_GAIN_STEP 1
|
||||
#define OV4689_WB_GAIN_DEFAULT 0x400
|
||||
|
||||
#define OV4689_REG_TEST_PATTERN CCI_REG8(0x5040)
|
||||
#define OV4689_TEST_PATTERN_ENABLE 0x80
|
||||
#define OV4689_TEST_PATTERN_DISABLE 0x0
|
||||
|
||||
#define OV4689_REG_VTS 0x380e
|
||||
|
||||
#define REG_NULL 0xFFFF
|
||||
|
||||
#define OV4689_REG_VALUE_08BIT 1
|
||||
#define OV4689_REG_VALUE_16BIT 2
|
||||
#define OV4689_REG_VALUE_24BIT 3
|
||||
|
||||
#define OV4689_LANES 4
|
||||
#define OV4689_XVCLK_FREQ 24000000
|
||||
|
||||
#define OV4689_PIXEL_ARRAY_WIDTH 2720
|
||||
#define OV4689_PIXEL_ARRAY_HEIGHT 1536
|
||||
#define OV4689_DUMMY_ROWS 8 /* 8 dummy rows on each side */
|
||||
#define OV4689_DUMMY_COLUMNS 16 /* 16 dummy columns on each side */
|
||||
|
||||
static const char *const ov4689_supply_names[] = {
|
||||
"avdd", /* Analog power */
|
||||
@ -61,11 +102,6 @@ static const char *const ov4689_supply_names[] = {
|
||||
"dvdd", /* Digital core power */
|
||||
};
|
||||
|
||||
struct regval {
|
||||
u16 addr;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
enum ov4689_mode_id {
|
||||
OV4689_MODE_2688_1520 = 0,
|
||||
OV4689_NUM_MODES,
|
||||
@ -75,20 +111,18 @@ struct ov4689_mode {
|
||||
enum ov4689_mode_id id;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 max_fps;
|
||||
u32 hts_def;
|
||||
u32 hts_min;
|
||||
u32 vts_def;
|
||||
u32 exp_def;
|
||||
u32 pixel_rate;
|
||||
u32 sensor_width;
|
||||
u32 sensor_height;
|
||||
u32 crop_top;
|
||||
u32 crop_left;
|
||||
const struct regval *reg_list;
|
||||
const struct cci_reg_sequence *reg_list;
|
||||
unsigned int num_regs;
|
||||
};
|
||||
|
||||
struct ov4689 {
|
||||
struct i2c_client *client;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct clk *xvclk;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct gpio_desc *pwdn_gpio;
|
||||
@ -99,7 +133,6 @@ struct ov4689 {
|
||||
|
||||
u32 clock_rate;
|
||||
|
||||
struct mutex mutex; /* lock to protect ctrls and cur_mode */
|
||||
struct v4l2_ctrl_handler ctrl_handler;
|
||||
struct v4l2_ctrl *exposure;
|
||||
|
||||
@ -119,95 +152,108 @@ struct ov4689_gain_range {
|
||||
|
||||
/*
|
||||
* Xclk 24Mhz
|
||||
* max_framerate 30fps
|
||||
* max_framerate 90fps
|
||||
* mipi_datarate per lane 1008Mbps
|
||||
*/
|
||||
static const struct regval ov4689_2688x1520_regs[] = {
|
||||
{0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00},
|
||||
{0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03},
|
||||
{0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04},
|
||||
{0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00},
|
||||
{0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72},
|
||||
{0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01},
|
||||
{0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1},
|
||||
{0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00},
|
||||
{0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04},
|
||||
{0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00},
|
||||
{0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80},
|
||||
{0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00},
|
||||
{0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80},
|
||||
{0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00},
|
||||
{0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80},
|
||||
{0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00},
|
||||
{0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80},
|
||||
{0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00},
|
||||
{0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80},
|
||||
{0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08},
|
||||
{0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00},
|
||||
{0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00},
|
||||
{0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12},
|
||||
{0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5},
|
||||
{0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7},
|
||||
{0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60},
|
||||
{0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02},
|
||||
{0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10},
|
||||
{0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86},
|
||||
{0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22},
|
||||
{0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00},
|
||||
{0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01},
|
||||
{0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c},
|
||||
{0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b},
|
||||
{0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc},
|
||||
{0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33},
|
||||
{0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0},
|
||||
{0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23},
|
||||
{0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd},
|
||||
{0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00},
|
||||
{0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00},
|
||||
{0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04},
|
||||
{0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08},
|
||||
{0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00},
|
||||
{0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51},
|
||||
{0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18},
|
||||
{0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00},
|
||||
{0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04},
|
||||
{0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05},
|
||||
{0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80},
|
||||
{0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a},
|
||||
{0x380d, 0x0e}, {0x380e, 0x06}, {0x380f, 0x12},
|
||||
{0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00},
|
||||
{0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01},
|
||||
{0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06},
|
||||
{0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01},
|
||||
{0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01},
|
||||
{0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08},
|
||||
{0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71},
|
||||
{0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1},
|
||||
{0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14},
|
||||
{0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00},
|
||||
{0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00},
|
||||
{0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10},
|
||||
{0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09},
|
||||
{0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f},
|
||||
{0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06},
|
||||
{0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02},
|
||||
{0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff},
|
||||
{0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00},
|
||||
{0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c},
|
||||
{0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01},
|
||||
{0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08},
|
||||
{0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10},
|
||||
{0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04},
|
||||
{0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93},
|
||||
{0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3},
|
||||
{0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00},
|
||||
{0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00},
|
||||
{0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10},
|
||||
{0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00},
|
||||
{0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00},
|
||||
{0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00},
|
||||
{0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00},
|
||||
{REG_NULL, 0x00},
|
||||
static const struct cci_reg_sequence ov4689_2688x1520_regs[] = {
|
||||
/* System control*/
|
||||
{ CCI_REG8(0x0103), 0x01 }, /* SC_CTRL0103 software_reset = 1 */
|
||||
{ CCI_REG8(0x3000), 0x20 }, /* SC_CMMN_PAD_OEN0 FSIN_output_enable = 1 */
|
||||
{ CCI_REG8(0x3021), 0x03 }, /*
|
||||
* SC_CMMN_MISC_CTRL fst_stby_ctr = 0,
|
||||
* sleep_no_latch_enable = 0
|
||||
*/
|
||||
|
||||
/* AEC PK */
|
||||
{ CCI_REG8(0x3503), 0x04 }, /* AEC_MANUAL gain_input_as_sensor_gain_format = 1 */
|
||||
|
||||
/* ADC and analog control*/
|
||||
{ CCI_REG8(0x3603), 0x40 },
|
||||
{ CCI_REG8(0x3604), 0x02 },
|
||||
{ CCI_REG8(0x3609), 0x12 },
|
||||
{ CCI_REG8(0x360c), 0x08 },
|
||||
{ CCI_REG8(0x360f), 0xe5 },
|
||||
{ CCI_REG8(0x3608), 0x8f },
|
||||
{ CCI_REG8(0x3611), 0x00 },
|
||||
{ CCI_REG8(0x3613), 0xf7 },
|
||||
{ CCI_REG8(0x3616), 0x58 },
|
||||
{ CCI_REG8(0x3619), 0x99 },
|
||||
{ CCI_REG8(0x361b), 0x60 },
|
||||
{ CCI_REG8(0x361e), 0x79 },
|
||||
{ CCI_REG8(0x3634), 0x10 },
|
||||
{ CCI_REG8(0x3635), 0x10 },
|
||||
{ CCI_REG8(0x3636), 0x15 },
|
||||
{ CCI_REG8(0x3646), 0x86 },
|
||||
{ CCI_REG8(0x364a), 0x0b },
|
||||
|
||||
/* Sensor control */
|
||||
{ CCI_REG8(0x3700), 0x17 },
|
||||
{ CCI_REG8(0x3701), 0x22 },
|
||||
{ CCI_REG8(0x3703), 0x10 },
|
||||
{ CCI_REG8(0x370a), 0x37 },
|
||||
{ CCI_REG8(0x3706), 0x63 },
|
||||
{ CCI_REG8(0x3709), 0x3c },
|
||||
{ CCI_REG8(0x370c), 0x30 },
|
||||
{ CCI_REG8(0x3710), 0x24 },
|
||||
{ CCI_REG8(0x3720), 0x28 },
|
||||
{ CCI_REG8(0x3729), 0x7b },
|
||||
{ CCI_REG8(0x372b), 0xbd },
|
||||
{ CCI_REG8(0x372c), 0xbc },
|
||||
{ CCI_REG8(0x372e), 0x52 },
|
||||
{ CCI_REG8(0x373c), 0x0e },
|
||||
{ CCI_REG8(0x373e), 0x33 },
|
||||
{ CCI_REG8(0x3743), 0x10 },
|
||||
{ CCI_REG8(0x3744), 0x88 },
|
||||
{ CCI_REG8(0x3745), 0xc0 },
|
||||
{ CCI_REG8(0x374c), 0x00 },
|
||||
{ CCI_REG8(0x374e), 0x23 },
|
||||
{ CCI_REG8(0x3751), 0x7b },
|
||||
{ CCI_REG8(0x3753), 0xbd },
|
||||
{ CCI_REG8(0x3754), 0xbc },
|
||||
{ CCI_REG8(0x3756), 0x52 },
|
||||
{ CCI_REG8(0x376b), 0x20 },
|
||||
{ CCI_REG8(0x3774), 0x51 },
|
||||
{ CCI_REG8(0x3776), 0xbd },
|
||||
{ CCI_REG8(0x3777), 0xbd },
|
||||
{ CCI_REG8(0x3781), 0x18 },
|
||||
{ CCI_REG8(0x3783), 0x25 },
|
||||
{ CCI_REG8(0x3798), 0x1b },
|
||||
|
||||
/* Timing control */
|
||||
{ CCI_REG8(0x3819), 0x01 }, /* VSYNC_END_L vsync_end_point[7:0] = 0x01 */
|
||||
|
||||
/* OTP control */
|
||||
{ CCI_REG8(0x3d85), 0x36 }, /* OTP_REG85 OTP_power_up_load_setting_enable = 1,
|
||||
* OTP_power_up_load_data_enable = 1,
|
||||
* OTP_bist_select = 1 (compare with zero)
|
||||
*/
|
||||
{ CCI_REG8(0x3d8c), 0x71 }, /* OTP_SETTING_STT_ADDRESS_H */
|
||||
{ CCI_REG8(0x3d8d), 0xcb }, /* OTP_SETTING_STT_ADDRESS_L */
|
||||
|
||||
/* BLC registers*/
|
||||
{ CCI_REG8(0x4001), 0x40 }, /* DEBUG_MODE */
|
||||
{ CCI_REG8(0x401b), 0x00 }, /* DEBUG_MODE */
|
||||
{ CCI_REG8(0x401d), 0x00 }, /* DEBUG_MODE */
|
||||
{ CCI_REG8(0x401f), 0x00 }, /* DEBUG_MODE */
|
||||
|
||||
/* ADC sync control */
|
||||
{ CCI_REG8(0x4500), 0x6c }, /* ADC_SYNC_CTRL */
|
||||
{ CCI_REG8(0x4503), 0x01 }, /* ADC_SYNC_CTRL */
|
||||
|
||||
/* Temperature monitor */
|
||||
{ CCI_REG8(0x4d00), 0x04 }, /* TPM_CTRL_00 tmp_slope[15:8] = 0x04 */
|
||||
{ CCI_REG8(0x4d01), 0x42 }, /* TPM_CTRL_01 tmp_slope[7:0] = 0x42 */
|
||||
{ CCI_REG8(0x4d02), 0xd1 }, /* TPM_CTRL_02 tpm_offset[31:24] = 0xd1 */
|
||||
{ CCI_REG8(0x4d03), 0x93 }, /* TPM_CTRL_03 tpm_offset[23:16] = 0x93 */
|
||||
{ CCI_REG8(0x4d04), 0xf5 }, /* TPM_CTRL_04 tpm_offset[15:8] = 0xf5 */
|
||||
{ CCI_REG8(0x4d05), 0xc1 }, /* TPM_CTRL_05 tpm_offset[7:0] = 0xc1 */
|
||||
|
||||
/* pre-ISP control */
|
||||
{ CCI_REG8(0x5050), 0x0c }, /* DEBUG_MODE */
|
||||
|
||||
/* OTP-DPC control */
|
||||
{ CCI_REG8(0x5501), 0x10 }, /* OTP_DPC_START_L otp_start_address[7:0] = 0x10 */
|
||||
{ CCI_REG8(0x5503), 0x0f }, /* OTP_DPC_END_L otp_end_address[7:0] = 0x0f */
|
||||
};
|
||||
|
||||
static const struct ov4689_mode supported_modes[] = {
|
||||
@ -215,16 +261,13 @@ static const struct ov4689_mode supported_modes[] = {
|
||||
.id = OV4689_MODE_2688_1520,
|
||||
.width = 2688,
|
||||
.height = 1520,
|
||||
.sensor_width = 2720,
|
||||
.sensor_height = 1536,
|
||||
.crop_top = 8,
|
||||
.crop_left = 16,
|
||||
.max_fps = 30,
|
||||
.exp_def = 1536,
|
||||
.hts_def = 4 * 2574,
|
||||
.hts_def = 10296,
|
||||
.hts_min = 3432,
|
||||
.vts_def = 1554,
|
||||
.pixel_rate = 480000000,
|
||||
.reg_list = ov4689_2688x1520_regs,
|
||||
.num_regs = ARRAY_SIZE(ov4689_2688x1520_regs),
|
||||
},
|
||||
};
|
||||
|
||||
@ -277,83 +320,6 @@ static const struct ov4689_gain_range ov4689_gain_ranges[] = {
|
||||
},
|
||||
};
|
||||
|
||||
/* Write registers up to 4 at a time */
|
||||
static int ov4689_write_reg(struct i2c_client *client, u16 reg, u32 len,
|
||||
u32 val)
|
||||
{
|
||||
u32 buf_i, val_i;
|
||||
__be32 val_be;
|
||||
u8 *val_p;
|
||||
u8 buf[6];
|
||||
|
||||
if (len > 4)
|
||||
return -EINVAL;
|
||||
|
||||
buf[0] = reg >> 8;
|
||||
buf[1] = reg & 0xff;
|
||||
|
||||
val_be = cpu_to_be32(val);
|
||||
val_p = (u8 *)&val_be;
|
||||
buf_i = 2;
|
||||
val_i = 4 - len;
|
||||
|
||||
while (val_i < 4)
|
||||
buf[buf_i++] = val_p[val_i++];
|
||||
|
||||
if (i2c_master_send(client, buf, len + 2) != len + 2)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov4689_write_array(struct i2c_client *client,
|
||||
const struct regval *regs)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
|
||||
ret = ov4689_write_reg(client, regs[i].addr,
|
||||
OV4689_REG_VALUE_08BIT, regs[i].val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read registers up to 4 at a time */
|
||||
static int ov4689_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
|
||||
u32 *val)
|
||||
{
|
||||
__be16 reg_addr_be = cpu_to_be16(reg);
|
||||
struct i2c_msg msgs[2];
|
||||
__be32 data_be = 0;
|
||||
u8 *data_be_p;
|
||||
int ret;
|
||||
|
||||
if (len > 4 || !len)
|
||||
return -EINVAL;
|
||||
|
||||
data_be_p = (u8 *)&data_be;
|
||||
/* Write register address */
|
||||
msgs[0].addr = client->addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = 2;
|
||||
msgs[0].buf = (u8 *)®_addr_be;
|
||||
|
||||
/* Read data from register */
|
||||
msgs[1].addr = client->addr;
|
||||
msgs[1].flags = I2C_M_RD;
|
||||
msgs[1].len = len;
|
||||
msgs[1].buf = &data_be_p[4 - len];
|
||||
|
||||
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (ret != ARRAY_SIZE(msgs))
|
||||
return -EIO;
|
||||
|
||||
*val = be32_to_cpu(data_be);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ov4689_fill_fmt(const struct ov4689_mode *mode,
|
||||
struct v4l2_mbus_framefmt *fmt)
|
||||
{
|
||||
@ -376,19 +342,6 @@ static int ov4689_set_fmt(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov4689_get_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_format *fmt)
|
||||
{
|
||||
struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
|
||||
struct ov4689 *ov4689 = to_ov4689(sd);
|
||||
|
||||
/* only one mode supported for now */
|
||||
ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_mbus_code_enum *code)
|
||||
@ -427,16 +380,14 @@ static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern)
|
||||
else
|
||||
val = OV4689_TEST_PATTERN_DISABLE;
|
||||
|
||||
return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN,
|
||||
OV4689_REG_VALUE_08BIT, val);
|
||||
return cci_write(ov4689->regmap, OV4689_REG_TEST_PATTERN,
|
||||
val, NULL);
|
||||
}
|
||||
|
||||
static int ov4689_get_selection(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct v4l2_subdev_selection *sel)
|
||||
{
|
||||
const struct ov4689_mode *mode = to_ov4689(sd)->cur_mode;
|
||||
|
||||
if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
|
||||
return -EINVAL;
|
||||
|
||||
@ -444,63 +395,114 @@ static int ov4689_get_selection(struct v4l2_subdev *sd,
|
||||
case V4L2_SEL_TGT_CROP_BOUNDS:
|
||||
sel->r.top = 0;
|
||||
sel->r.left = 0;
|
||||
sel->r.width = mode->sensor_width;
|
||||
sel->r.height = mode->sensor_height;
|
||||
sel->r.width = OV4689_PIXEL_ARRAY_WIDTH;
|
||||
sel->r.height = OV4689_PIXEL_ARRAY_HEIGHT;
|
||||
return 0;
|
||||
case V4L2_SEL_TGT_CROP:
|
||||
case V4L2_SEL_TGT_CROP_DEFAULT:
|
||||
sel->r.top = mode->crop_top;
|
||||
sel->r.left = mode->crop_left;
|
||||
sel->r.width = mode->width;
|
||||
sel->r.height = mode->height;
|
||||
sel->r.top = OV4689_DUMMY_ROWS;
|
||||
sel->r.left = OV4689_DUMMY_COLUMNS;
|
||||
sel->r.width =
|
||||
OV4689_PIXEL_ARRAY_WIDTH - 2 * OV4689_DUMMY_COLUMNS;
|
||||
sel->r.height =
|
||||
OV4689_PIXEL_ARRAY_HEIGHT - 2 * OV4689_DUMMY_ROWS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ov4689_setup_timings(struct ov4689 *ov4689)
|
||||
{
|
||||
const struct ov4689_mode *mode = ov4689->cur_mode;
|
||||
struct regmap *rm = ov4689->regmap;
|
||||
int ret = 0;
|
||||
|
||||
cci_write(rm, OV4689_REG_H_CROP_START, 8, &ret);
|
||||
cci_write(rm, OV4689_REG_V_CROP_START, 8, &ret);
|
||||
cci_write(rm, OV4689_REG_H_CROP_END, 2711, &ret);
|
||||
cci_write(rm, OV4689_REG_V_CROP_END, 1531, &ret);
|
||||
|
||||
cci_write(rm, OV4689_REG_H_OUTPUT_SIZE, mode->width, &ret);
|
||||
cci_write(rm, OV4689_REG_V_OUTPUT_SIZE, mode->height, &ret);
|
||||
|
||||
cci_write(rm, OV4689_REG_H_WIN_OFF, 8, &ret);
|
||||
cci_write(rm, OV4689_REG_V_WIN_OFF, 4, &ret);
|
||||
|
||||
cci_write(rm, OV4689_REG_VFIFO_CTRL_01, 167, &ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ov4689_setup_blc_anchors(struct ov4689 *ov4689)
|
||||
{
|
||||
struct regmap *rm = ov4689->regmap;
|
||||
int ret = 0;
|
||||
|
||||
cci_write(rm, OV4689_REG_ANCHOR_LEFT_START, 16, &ret);
|
||||
cci_write(rm, OV4689_REG_ANCHOR_LEFT_END, 1999, &ret);
|
||||
cci_write(rm, OV4689_REG_ANCHOR_RIGHT_START, 2400, &ret);
|
||||
cci_write(rm, OV4689_REG_ANCHOR_RIGHT_END, 2415, &ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ov4689_s_stream(struct v4l2_subdev *sd, int on)
|
||||
{
|
||||
struct ov4689 *ov4689 = to_ov4689(sd);
|
||||
struct i2c_client *client = ov4689->client;
|
||||
struct v4l2_subdev_state *sd_state;
|
||||
struct device *dev = ov4689->dev;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&ov4689->mutex);
|
||||
sd_state = v4l2_subdev_lock_and_get_active_state(&ov4689->subdev);
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_resume_and_get(&client->dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
goto unlock_and_return;
|
||||
|
||||
ret = ov4689_write_array(ov4689->client,
|
||||
ov4689->cur_mode->reg_list);
|
||||
ret = cci_multi_reg_write(ov4689->regmap,
|
||||
ov4689->cur_mode->reg_list,
|
||||
ov4689->cur_mode->num_regs,
|
||||
NULL);
|
||||
if (ret) {
|
||||
pm_runtime_put(&client->dev);
|
||||
pm_runtime_put(dev);
|
||||
goto unlock_and_return;
|
||||
}
|
||||
|
||||
ret = ov4689_setup_timings(ov4689);
|
||||
if (ret) {
|
||||
pm_runtime_put(dev);
|
||||
goto unlock_and_return;
|
||||
}
|
||||
|
||||
ret = ov4689_setup_blc_anchors(ov4689);
|
||||
if (ret) {
|
||||
pm_runtime_put(dev);
|
||||
goto unlock_and_return;
|
||||
}
|
||||
|
||||
ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler);
|
||||
if (ret) {
|
||||
pm_runtime_put(&client->dev);
|
||||
pm_runtime_put(dev);
|
||||
goto unlock_and_return;
|
||||
}
|
||||
|
||||
ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
|
||||
OV4689_REG_VALUE_08BIT,
|
||||
OV4689_MODE_STREAMING);
|
||||
ret = cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE,
|
||||
OV4689_MODE_STREAMING, NULL);
|
||||
if (ret) {
|
||||
pm_runtime_put(&client->dev);
|
||||
pm_runtime_put(dev);
|
||||
goto unlock_and_return;
|
||||
}
|
||||
} else {
|
||||
ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
|
||||
OV4689_REG_VALUE_08BIT,
|
||||
OV4689_MODE_SW_STANDBY);
|
||||
pm_runtime_put(&client->dev);
|
||||
cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE,
|
||||
OV4689_MODE_SW_STANDBY, NULL);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
unlock_and_return:
|
||||
mutex_unlock(&ov4689->mutex);
|
||||
v4l2_subdev_unlock_state(sd_state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -563,18 +565,13 @@ static int __maybe_unused ov4689_power_off(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
||||
static int ov4689_init_state(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state)
|
||||
{
|
||||
struct ov4689 *ov4689 = to_ov4689(sd);
|
||||
struct v4l2_mbus_framefmt *try_fmt;
|
||||
struct v4l2_mbus_framefmt *fmt =
|
||||
v4l2_subdev_state_get_format(sd_state, 0);
|
||||
|
||||
mutex_lock(&ov4689->mutex);
|
||||
|
||||
try_fmt = v4l2_subdev_state_get_format(fh->state, 0);
|
||||
/* Initialize try_fmt */
|
||||
ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], try_fmt);
|
||||
|
||||
mutex_unlock(&ov4689->mutex);
|
||||
ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], fmt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -583,10 +580,6 @@ static const struct dev_pm_ops ov4689_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(ov4689_power_off, ov4689_power_on, NULL)
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
|
||||
.open = ov4689_open,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_video_ops ov4689_video_ops = {
|
||||
.s_stream = ov4689_s_stream,
|
||||
};
|
||||
@ -594,11 +587,15 @@ static const struct v4l2_subdev_video_ops ov4689_video_ops = {
|
||||
static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
|
||||
.enum_mbus_code = ov4689_enum_mbus_code,
|
||||
.enum_frame_size = ov4689_enum_frame_sizes,
|
||||
.get_fmt = ov4689_get_fmt,
|
||||
.get_fmt = v4l2_subdev_get_fmt,
|
||||
.set_fmt = ov4689_set_fmt,
|
||||
.get_selection = ov4689_get_selection,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
|
||||
.init_state = ov4689_init_state,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops ov4689_subdev_ops = {
|
||||
.video = &ov4689_video_ops,
|
||||
.pad = &ov4689_pad_ops,
|
||||
@ -610,7 +607,6 @@ static const struct v4l2_subdev_ops ov4689_subdev_ops = {
|
||||
*/
|
||||
static int ov4689_map_gain(struct ov4689 *ov4689, int logical_gain, int *result)
|
||||
{
|
||||
const struct device *dev = &ov4689->client->dev;
|
||||
const struct ov4689_gain_range *range;
|
||||
unsigned int n;
|
||||
|
||||
@ -621,7 +617,8 @@ static int ov4689_map_gain(struct ov4689 *ov4689, int logical_gain, int *result)
|
||||
}
|
||||
|
||||
if (n == ARRAY_SIZE(ov4689_gain_ranges)) {
|
||||
dev_warn_ratelimited(dev, "no mapping found for gain %d\n",
|
||||
dev_warn_ratelimited(ov4689->dev,
|
||||
"no mapping found for gain %d\n",
|
||||
logical_gain);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -637,10 +634,11 @@ static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct ov4689 *ov4689 =
|
||||
container_of(ctrl->handler, struct ov4689, ctrl_handler);
|
||||
struct i2c_client *client = ov4689->client;
|
||||
int sensor_gain;
|
||||
struct regmap *regmap = ov4689->regmap;
|
||||
struct device *dev = ov4689->dev;
|
||||
int sensor_gain = 0;
|
||||
s64 max_expo;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
/* Propagate change of current control to all related controls */
|
||||
switch (ctrl->id) {
|
||||
@ -654,44 +652,58 @@ static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pm_runtime_get_if_in_use(&client->dev))
|
||||
if (!pm_runtime_get_if_in_use(dev))
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_EXPOSURE:
|
||||
/* 4 least significant bits of expsoure are fractional part */
|
||||
ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE,
|
||||
OV4689_REG_VALUE_24BIT, ctrl->val << 4);
|
||||
/* 4 least significant bits of exposure are fractional part */
|
||||
cci_write(regmap, OV4689_REG_EXPOSURE, ctrl->val << 4, &ret);
|
||||
break;
|
||||
case V4L2_CID_ANALOGUE_GAIN:
|
||||
ret = ov4689_map_gain(ov4689, ctrl->val, &sensor_gain);
|
||||
|
||||
ret = ret ?:
|
||||
ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_H,
|
||||
OV4689_REG_VALUE_08BIT,
|
||||
(sensor_gain >> OV4689_GAIN_H_SHIFT) &
|
||||
OV4689_GAIN_H_MASK);
|
||||
ret = ret ?:
|
||||
ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_L,
|
||||
OV4689_REG_VALUE_08BIT,
|
||||
sensor_gain & OV4689_GAIN_L_MASK);
|
||||
cci_write(regmap, OV4689_REG_GAIN, sensor_gain, &ret);
|
||||
break;
|
||||
case V4L2_CID_VBLANK:
|
||||
ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS,
|
||||
OV4689_REG_VALUE_16BIT,
|
||||
ctrl->val + ov4689->cur_mode->height);
|
||||
cci_write(regmap, OV4689_REG_VTS,
|
||||
ctrl->val + ov4689->cur_mode->height, &ret);
|
||||
break;
|
||||
case V4L2_CID_TEST_PATTERN:
|
||||
ret = ov4689_enable_test_pattern(ov4689, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_HBLANK:
|
||||
cci_write(regmap, OV4689_REG_HTS,
|
||||
(ctrl->val + ov4689->cur_mode->width) /
|
||||
OV4689_HTS_DIVIDER, &ret);
|
||||
break;
|
||||
case V4L2_CID_VFLIP:
|
||||
cci_update_bits(regmap, OV4689_REG_TIMING_FORMAT1,
|
||||
OV4689_TIMING_FLIP_MASK,
|
||||
ctrl->val ? OV4689_TIMING_FLIP_BOTH : 0, &ret);
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
cci_update_bits(regmap, OV4689_REG_TIMING_FORMAT2,
|
||||
OV4689_TIMING_FLIP_MASK,
|
||||
ctrl->val ? 0 : OV4689_TIMING_FLIP_BOTH, &ret);
|
||||
break;
|
||||
case V4L2_CID_DIGITAL_GAIN:
|
||||
cci_write(regmap, OV4689_REG_DIG_GAIN, ctrl->val, &ret);
|
||||
break;
|
||||
case V4L2_CID_RED_BALANCE:
|
||||
cci_write(regmap, OV4689_REG_WB_GAIN_RED, ctrl->val, &ret);
|
||||
break;
|
||||
case V4L2_CID_BLUE_BALANCE:
|
||||
cci_write(regmap, OV4689_REG_WB_GAIN_BLUE, ctrl->val, &ret);
|
||||
break;
|
||||
default:
|
||||
dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
|
||||
dev_warn(dev, "%s Unhandled id:0x%x, val:0x%x\n",
|
||||
__func__, ctrl->id, ctrl->val);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
pm_runtime_put(&client->dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -707,16 +719,15 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689)
|
||||
struct v4l2_ctrl_handler *handler;
|
||||
const struct ov4689_mode *mode;
|
||||
s64 exposure_max, vblank_def;
|
||||
s64 hblank_def, hblank_min;
|
||||
struct v4l2_ctrl *ctrl;
|
||||
s64 h_blank_def;
|
||||
int ret;
|
||||
|
||||
handler = &ov4689->ctrl_handler;
|
||||
mode = ov4689->cur_mode;
|
||||
ret = v4l2_ctrl_handler_init(handler, 10);
|
||||
ret = v4l2_ctrl_handler_init(handler, 15);
|
||||
if (ret)
|
||||
return ret;
|
||||
handler->lock = &ov4689->mutex;
|
||||
|
||||
ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0,
|
||||
link_freq_menu_items);
|
||||
@ -726,11 +737,11 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689)
|
||||
v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0,
|
||||
mode->pixel_rate, 1, mode->pixel_rate);
|
||||
|
||||
h_blank_def = mode->hts_def - mode->width;
|
||||
ctrl = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, h_blank_def,
|
||||
h_blank_def, 1, h_blank_def);
|
||||
if (ctrl)
|
||||
ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
||||
hblank_def = mode->hts_def - mode->width;
|
||||
hblank_min = mode->hts_min - mode->width;
|
||||
v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_HBLANK,
|
||||
hblank_min, OV4689_HTS_MAX - mode->width,
|
||||
OV4689_HTS_DIVIDER, hblank_def);
|
||||
|
||||
vblank_def = mode->vts_def - mode->height;
|
||||
v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK,
|
||||
@ -754,10 +765,24 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689)
|
||||
ARRAY_SIZE(ov4689_test_pattern_menu) - 1,
|
||||
0, 0, ov4689_test_pattern_menu);
|
||||
|
||||
v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
|
||||
v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
|
||||
OV4689_DIG_GAIN_MIN, OV4689_DIG_GAIN_MAX,
|
||||
OV4689_DIG_GAIN_STEP, OV4689_DIG_GAIN_DEFAULT);
|
||||
|
||||
v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_RED_BALANCE,
|
||||
OV4689_WB_GAIN_MIN, OV4689_WB_GAIN_MAX,
|
||||
OV4689_WB_GAIN_STEP, OV4689_WB_GAIN_DEFAULT);
|
||||
|
||||
v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_BLUE_BALANCE,
|
||||
OV4689_WB_GAIN_MIN, OV4689_WB_GAIN_MAX,
|
||||
OV4689_WB_GAIN_STEP, OV4689_WB_GAIN_DEFAULT);
|
||||
|
||||
if (handler->error) {
|
||||
ret = handler->error;
|
||||
dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n",
|
||||
ret);
|
||||
dev_err(ov4689->dev, "Failed to init controls(%d)\n", ret);
|
||||
goto err_free_handler;
|
||||
}
|
||||
|
||||
@ -783,19 +808,18 @@ err_free_handler:
|
||||
static int ov4689_check_sensor_id(struct ov4689 *ov4689,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &ov4689->client->dev;
|
||||
u32 id = 0;
|
||||
struct device *dev = ov4689->dev;
|
||||
u64 id = 0;
|
||||
int ret;
|
||||
|
||||
ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID,
|
||||
OV4689_REG_VALUE_16BIT, &id);
|
||||
ret = cci_read(ov4689->regmap, OV4689_REG_CHIP_ID, &id, NULL);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot read sensor ID\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (id != CHIP_ID) {
|
||||
dev_err(dev, "Unexpected sensor ID %06x, expected %06x\n",
|
||||
dev_err(dev, "Unexpected sensor ID %06llx, expected %06x\n",
|
||||
id, CHIP_ID);
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -812,7 +836,7 @@ static int ov4689_configure_regulators(struct ov4689 *ov4689)
|
||||
for (i = 0; i < ARRAY_SIZE(ov4689_supply_names); i++)
|
||||
ov4689->supplies[i].supply = ov4689_supply_names[i];
|
||||
|
||||
return devm_regulator_bulk_get(&ov4689->client->dev,
|
||||
return devm_regulator_bulk_get(ov4689->dev,
|
||||
ARRAY_SIZE(ov4689_supply_names),
|
||||
ov4689->supplies);
|
||||
}
|
||||
@ -881,7 +905,8 @@ static int ov4689_probe(struct i2c_client *client)
|
||||
if (!ov4689)
|
||||
return -ENOMEM;
|
||||
|
||||
ov4689->client = client;
|
||||
ov4689->dev = dev;
|
||||
|
||||
ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520];
|
||||
|
||||
ov4689->xvclk = devm_clk_get_optional(dev, NULL);
|
||||
@ -905,6 +930,13 @@ static int ov4689_probe(struct i2c_client *client)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ov4689->regmap = devm_cci_regmap_init_i2c(client, 16);
|
||||
if (IS_ERR(ov4689->regmap)) {
|
||||
ret = PTR_ERR(ov4689->regmap);
|
||||
dev_err(dev, "failed to initialize CCI: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ov4689->reset_gpio = devm_gpiod_get_optional(dev, "reset",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ov4689->reset_gpio)) {
|
||||
@ -923,13 +955,15 @@ static int ov4689_probe(struct i2c_client *client)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to get power regulators\n");
|
||||
|
||||
mutex_init(&ov4689->mutex);
|
||||
|
||||
sd = &ov4689->subdev;
|
||||
v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops);
|
||||
sd->internal_ops = &ov4689_internal_ops;
|
||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||
ret = ov4689_initialize_controls(ov4689);
|
||||
if (ret)
|
||||
goto err_destroy_mutex;
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize controls\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ov4689_power_on(dev);
|
||||
if (ret)
|
||||
@ -939,35 +973,47 @@ static int ov4689_probe(struct i2c_client *client)
|
||||
if (ret)
|
||||
goto err_power_off;
|
||||
|
||||
sd->internal_ops = &ov4689_internal_ops;
|
||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||
|
||||
ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||
ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||
ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad);
|
||||
if (ret < 0)
|
||||
goto err_power_off;
|
||||
|
||||
ret = v4l2_async_register_subdev_sensor(sd);
|
||||
sd->state_lock = ov4689->ctrl_handler.lock;
|
||||
ret = v4l2_subdev_init_finalize(sd);
|
||||
if (ret) {
|
||||
dev_err(dev, "v4l2 async register subdev failed\n");
|
||||
dev_err(dev, "Could not register v4l2 device\n");
|
||||
goto err_clean_entity;
|
||||
}
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_idle(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, 1000);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
|
||||
ret = v4l2_async_register_subdev_sensor(sd);
|
||||
if (ret) {
|
||||
dev_err(dev, "v4l2 async register subdev failed\n");
|
||||
goto err_clean_subdev_pm;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clean_subdev_pm:
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
v4l2_subdev_cleanup(sd);
|
||||
err_clean_entity:
|
||||
media_entity_cleanup(&sd->entity);
|
||||
err_power_off:
|
||||
ov4689_power_off(dev);
|
||||
err_free_handler:
|
||||
v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
|
||||
err_destroy_mutex:
|
||||
mutex_destroy(&ov4689->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -979,9 +1025,8 @@ static void ov4689_remove(struct i2c_client *client)
|
||||
|
||||
v4l2_async_unregister_subdev(sd);
|
||||
media_entity_cleanup(&sd->entity);
|
||||
|
||||
v4l2_subdev_cleanup(sd);
|
||||
v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
|
||||
mutex_destroy(&ov4689->mutex);
|
||||
|
||||
pm_runtime_disable(&client->dev);
|
||||
if (!pm_runtime_status_suspended(&client->dev))
|
||||
|
@ -463,8 +463,8 @@ static int rdacm20_initialize(struct rdacm20_device *dev)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Ensure that we have a good link configuration before attempting to
|
||||
* identify the device.
|
||||
* Ensure that we have a good link configuration before attempting to
|
||||
* identify the device.
|
||||
*/
|
||||
ret = max9271_configure_i2c(&dev->serializer,
|
||||
MAX9271_I2CSLVSH_469NS_234NS |
|
||||
|
@ -326,7 +326,7 @@ static int mipid02_configure_from_rx_speed(struct mipid02_dev *bridge,
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev, "detect link_freq = %lld Hz", link_freq);
|
||||
do_div(ui_4, link_freq);
|
||||
ui_4 = div64_u64(ui_4, link_freq);
|
||||
bridge->r.clk_lane_reg1 |= ui_4 << 2;
|
||||
|
||||
return 0;
|
||||
|
@ -1521,11 +1521,14 @@ static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc358743_s_dv_timings(struct v4l2_subdev *sd,
|
||||
static int tc358743_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct tc358743_state *state = to_state(sd);
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!timings)
|
||||
return -EINVAL;
|
||||
|
||||
@ -1553,11 +1556,14 @@ static int tc358743_s_dv_timings(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc358743_g_dv_timings(struct v4l2_subdev *sd,
|
||||
static int tc358743_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct tc358743_state *state = to_state(sd);
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
*timings = state->timings;
|
||||
|
||||
return 0;
|
||||
@ -1573,11 +1579,14 @@ static int tc358743_enum_dv_timings(struct v4l2_subdev *sd,
|
||||
&tc358743_timings_cap, NULL, NULL);
|
||||
}
|
||||
|
||||
static int tc358743_query_dv_timings(struct v4l2_subdev *sd,
|
||||
struct v4l2_dv_timings *timings)
|
||||
static int tc358743_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = tc358743_get_detected_timings(sd, timings);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -1822,9 +1831,6 @@ static const struct v4l2_subdev_core_ops tc358743_core_ops = {
|
||||
|
||||
static const struct v4l2_subdev_video_ops tc358743_video_ops = {
|
||||
.g_input_status = tc358743_g_input_status,
|
||||
.s_dv_timings = tc358743_s_dv_timings,
|
||||
.g_dv_timings = tc358743_g_dv_timings,
|
||||
.query_dv_timings = tc358743_query_dv_timings,
|
||||
.s_stream = tc358743_s_stream,
|
||||
};
|
||||
|
||||
@ -1834,6 +1840,9 @@ static const struct v4l2_subdev_pad_ops tc358743_pad_ops = {
|
||||
.get_fmt = tc358743_get_fmt,
|
||||
.get_edid = tc358743_g_edid,
|
||||
.set_edid = tc358743_s_edid,
|
||||
.s_dv_timings = tc358743_s_dv_timings,
|
||||
.g_dv_timings = tc358743_g_dv_timings,
|
||||
.query_dv_timings = tc358743_query_dv_timings,
|
||||
.enum_dv_timings = tc358743_enum_dv_timings,
|
||||
.dv_timings_cap = tc358743_dv_timings_cap,
|
||||
.get_mbus_config = tc358743_get_mbus_config,
|
||||
@ -2110,7 +2119,7 @@ static int tc358743_probe(struct i2c_client *client)
|
||||
|
||||
tc358743_initial_setup(sd);
|
||||
|
||||
tc358743_s_dv_timings(sd, &default_timing);
|
||||
tc358743_s_dv_timings(sd, 0, &default_timing);
|
||||
|
||||
tc358743_set_csi_color_space(sd);
|
||||
|
||||
|
@ -844,8 +844,7 @@ static unsigned long tc358746_find_pll_settings(struct tc358746 *tc358746,
|
||||
continue;
|
||||
|
||||
tmp = fout * postdiv;
|
||||
do_div(tmp, fin);
|
||||
mul = tmp;
|
||||
mul = div64_ul(tmp, fin);
|
||||
if (mul > 511)
|
||||
continue;
|
||||
|
||||
|
@ -1669,8 +1669,8 @@ tda1997x_g_input_status(struct v4l2_subdev *sd, u32 *status)
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int tda1997x_s_dv_timings(struct v4l2_subdev *sd,
|
||||
struct v4l2_dv_timings *timings)
|
||||
static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct tda1997x_state *state = to_state(sd);
|
||||
|
||||
@ -1694,7 +1694,7 @@ static int tda1997x_s_dv_timings(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tda1997x_g_dv_timings(struct v4l2_subdev *sd,
|
||||
static int tda1997x_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct tda1997x_state *state = to_state(sd);
|
||||
@ -1707,7 +1707,7 @@ static int tda1997x_g_dv_timings(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tda1997x_query_dv_timings(struct v4l2_subdev *sd,
|
||||
static int tda1997x_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct tda1997x_state *state = to_state(sd);
|
||||
@ -1724,9 +1724,6 @@ static int tda1997x_query_dv_timings(struct v4l2_subdev *sd,
|
||||
|
||||
static const struct v4l2_subdev_video_ops tda1997x_video_ops = {
|
||||
.g_input_status = tda1997x_g_input_status,
|
||||
.s_dv_timings = tda1997x_s_dv_timings,
|
||||
.g_dv_timings = tda1997x_g_dv_timings,
|
||||
.query_dv_timings = tda1997x_query_dv_timings,
|
||||
};
|
||||
|
||||
|
||||
@ -1930,6 +1927,9 @@ static const struct v4l2_subdev_pad_ops tda1997x_pad_ops = {
|
||||
.set_fmt = tda1997x_set_format,
|
||||
.get_edid = tda1997x_get_edid,
|
||||
.set_edid = tda1997x_set_edid,
|
||||
.s_dv_timings = tda1997x_s_dv_timings,
|
||||
.g_dv_timings = tda1997x_g_dv_timings,
|
||||
.query_dv_timings = tda1997x_query_dv_timings,
|
||||
.dv_timings_cap = tda1997x_get_dv_timings_cap,
|
||||
.enum_dv_timings = tda1997x_enum_dv_timings,
|
||||
};
|
||||
|
@ -193,8 +193,8 @@ static int ths7303_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
}
|
||||
|
||||
/* for setting filter for HD output */
|
||||
static int ths7303_s_dv_timings(struct v4l2_subdev *sd,
|
||||
struct v4l2_dv_timings *dv_timings)
|
||||
static int ths7303_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *dv_timings)
|
||||
{
|
||||
struct ths7303_state *state = to_state(sd);
|
||||
|
||||
@ -210,7 +210,6 @@ static int ths7303_s_dv_timings(struct v4l2_subdev *sd,
|
||||
static const struct v4l2_subdev_video_ops ths7303_video_ops = {
|
||||
.s_stream = ths7303_s_stream,
|
||||
.s_std_output = ths7303_s_std_output,
|
||||
.s_dv_timings = ths7303_s_dv_timings,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
@ -317,9 +316,14 @@ static const struct v4l2_subdev_core_ops ths7303_core_ops = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_pad_ops ths7303_pad_ops = {
|
||||
.s_dv_timings = ths7303_s_dv_timings,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops ths7303_ops = {
|
||||
.core = &ths7303_core_ops,
|
||||
.video = &ths7303_video_ops,
|
||||
.pad = &ths7303_pad_ops,
|
||||
};
|
||||
|
||||
static int ths7303_probe(struct i2c_client *client)
|
||||
|
@ -358,13 +358,16 @@ static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)
|
||||
bt->hsync, bt->vsync);
|
||||
}
|
||||
|
||||
static int ths8200_s_dv_timings(struct v4l2_subdev *sd,
|
||||
static int ths8200_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct ths8200_state *state = to_state(sd);
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap,
|
||||
NULL, NULL))
|
||||
return -EINVAL;
|
||||
@ -385,13 +388,16 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ths8200_g_dv_timings(struct v4l2_subdev *sd,
|
||||
static int ths8200_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct ths8200_state *state = to_state(sd);
|
||||
|
||||
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
*timings = state->dv_timings;
|
||||
|
||||
return 0;
|
||||
@ -420,11 +426,11 @@ static int ths8200_dv_timings_cap(struct v4l2_subdev *sd,
|
||||
/* Specific video subsystem operation handlers */
|
||||
static const struct v4l2_subdev_video_ops ths8200_video_ops = {
|
||||
.s_stream = ths8200_s_stream,
|
||||
.s_dv_timings = ths8200_s_dv_timings,
|
||||
.g_dv_timings = ths8200_g_dv_timings,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_pad_ops ths8200_pad_ops = {
|
||||
.s_dv_timings = ths8200_s_dv_timings,
|
||||
.g_dv_timings = ths8200_g_dv_timings,
|
||||
.enum_dv_timings = ths8200_enum_dv_timings,
|
||||
.dv_timings_cap = ths8200_dv_timings_cap,
|
||||
};
|
||||
|
@ -546,13 +546,16 @@ static int tvp7002_write_inittab(struct v4l2_subdev *sd,
|
||||
return error;
|
||||
}
|
||||
|
||||
static int tvp7002_s_dv_timings(struct v4l2_subdev *sd,
|
||||
struct v4l2_dv_timings *dv_timings)
|
||||
static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *dv_timings)
|
||||
{
|
||||
struct tvp7002 *device = to_tvp7002(sd);
|
||||
const struct v4l2_bt_timings *bt = &dv_timings->bt;
|
||||
int i;
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (dv_timings->type != V4L2_DV_BT_656_1120)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < NUM_TIMINGS; i++) {
|
||||
@ -566,11 +569,14 @@ static int tvp7002_s_dv_timings(struct v4l2_subdev *sd,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int tvp7002_g_dv_timings(struct v4l2_subdev *sd,
|
||||
struct v4l2_dv_timings *dv_timings)
|
||||
static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *dv_timings)
|
||||
{
|
||||
struct tvp7002 *device = to_tvp7002(sd);
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
*dv_timings = device->current_timings->timings;
|
||||
return 0;
|
||||
}
|
||||
@ -659,12 +665,16 @@ static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tvp7002_query_dv_timings(struct v4l2_subdev *sd,
|
||||
struct v4l2_dv_timings *timings)
|
||||
static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
int index;
|
||||
int err = tvp7002_query_dv(sd, &index);
|
||||
int err;
|
||||
|
||||
if (pad != 0)
|
||||
return -EINVAL;
|
||||
|
||||
err = tvp7002_query_dv(sd, &index);
|
||||
if (err)
|
||||
return err;
|
||||
*timings = tvp7002_timings[index].timings;
|
||||
@ -861,9 +871,6 @@ static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
|
||||
|
||||
/* Specific video subsystem operation handlers */
|
||||
static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
|
||||
.g_dv_timings = tvp7002_g_dv_timings,
|
||||
.s_dv_timings = tvp7002_s_dv_timings,
|
||||
.query_dv_timings = tvp7002_query_dv_timings,
|
||||
.s_stream = tvp7002_s_stream,
|
||||
};
|
||||
|
||||
@ -872,6 +879,9 @@ static const struct v4l2_subdev_pad_ops tvp7002_pad_ops = {
|
||||
.enum_mbus_code = tvp7002_enum_mbus_code,
|
||||
.get_fmt = tvp7002_get_pad_format,
|
||||
.set_fmt = tvp7002_set_pad_format,
|
||||
.g_dv_timings = tvp7002_g_dv_timings,
|
||||
.s_dv_timings = tvp7002_s_dv_timings,
|
||||
.query_dv_timings = tvp7002_query_dv_timings,
|
||||
.enum_dv_timings = tvp7002_enum_dv_timings,
|
||||
};
|
||||
|
||||
@ -1001,7 +1011,7 @@ static int tvp7002_probe(struct i2c_client *c)
|
||||
|
||||
/* Set registers according to default video mode */
|
||||
timings = device->current_timings->timings;
|
||||
error = tvp7002_s_dv_timings(sd, &timings);
|
||||
error = tvp7002_s_dv_timings(sd, 0, &timings);
|
||||
|
||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||
device->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||
|
@ -245,15 +245,14 @@ int __must_check media_devnode_register(struct media_device *mdev,
|
||||
kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor);
|
||||
|
||||
/* Part 3: Add the media and char device */
|
||||
set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
|
||||
ret = cdev_device_add(&devnode->cdev, &devnode->dev);
|
||||
if (ret < 0) {
|
||||
clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
|
||||
pr_err("%s: cdev_device_add failed\n", __func__);
|
||||
goto cdev_add_error;
|
||||
}
|
||||
|
||||
/* Part 4: Activate this minor. The char device can now be used. */
|
||||
set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
|
||||
|
||||
return 0;
|
||||
|
||||
cdev_add_error:
|
||||
|
@ -619,6 +619,12 @@ static int media_pipeline_explore_next_link(struct media_pipeline *pipe,
|
||||
link = list_entry(entry->links, typeof(*link), list);
|
||||
last_link = media_pipeline_walk_pop(walk);
|
||||
|
||||
if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) != MEDIA_LNK_FL_DATA_LINK) {
|
||||
dev_dbg(walk->mdev->dev,
|
||||
"media pipeline: skipping link (not data-link)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(walk->mdev->dev,
|
||||
"media pipeline: exploring link '%s':%u -> '%s':%u\n",
|
||||
link->source->entity->name, link->source->index,
|
||||
|
@ -344,30 +344,7 @@ static struct sdio_driver smssdio_driver = {
|
||||
.probe = smssdio_probe,
|
||||
.remove = smssdio_remove,
|
||||
};
|
||||
|
||||
/*******************************************************************/
|
||||
/* Module functions */
|
||||
/*******************************************************************/
|
||||
|
||||
static int __init smssdio_module_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n");
|
||||
printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n");
|
||||
|
||||
ret = sdio_register_driver(&smssdio_driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit smssdio_module_exit(void)
|
||||
{
|
||||
sdio_unregister_driver(&smssdio_driver);
|
||||
}
|
||||
|
||||
module_init(smssdio_module_init);
|
||||
module_exit(smssdio_module_exit);
|
||||
module_sdio_driver(smssdio_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");
|
||||
MODULE_AUTHOR("Pierre Ossman");
|
||||
|
@ -633,7 +633,7 @@ static int cobalt_s_dv_timings(struct file *file, void *priv_fh,
|
||||
return -EBUSY;
|
||||
|
||||
err = v4l2_subdev_call(s->sd,
|
||||
video, s_dv_timings, timings);
|
||||
pad, s_dv_timings, 0, timings);
|
||||
if (!err) {
|
||||
s->timings = *timings;
|
||||
s->width = timings->bt.width;
|
||||
@ -653,7 +653,7 @@ static int cobalt_g_dv_timings(struct file *file, void *priv_fh,
|
||||
return 0;
|
||||
}
|
||||
return v4l2_subdev_call(s->sd,
|
||||
video, g_dv_timings, timings);
|
||||
pad, g_dv_timings, 0, timings);
|
||||
}
|
||||
|
||||
static int cobalt_query_dv_timings(struct file *file, void *priv_fh,
|
||||
@ -666,7 +666,7 @@ static int cobalt_query_dv_timings(struct file *file, void *priv_fh,
|
||||
return 0;
|
||||
}
|
||||
return v4l2_subdev_call(s->sd,
|
||||
video, query_dv_timings, timings);
|
||||
pad, query_dv_timings, 0, timings);
|
||||
}
|
||||
|
||||
static int cobalt_dv_timings_cap(struct file *file, void *priv_fh,
|
||||
@ -1080,7 +1080,7 @@ static int cobalt_g_pixelaspect(struct file *file, void *fh,
|
||||
if (s->input == 1)
|
||||
timings = cea1080p60;
|
||||
else
|
||||
err = v4l2_subdev_call(s->sd, video, g_dv_timings, &timings);
|
||||
err = v4l2_subdev_call(s->sd, pad, g_dv_timings, 0, &timings);
|
||||
if (!err)
|
||||
*f = v4l2_dv_timings_aspect_ratio(&timings);
|
||||
return err;
|
||||
@ -1099,7 +1099,7 @@ static int cobalt_g_selection(struct file *file, void *fh,
|
||||
if (s->input == 1)
|
||||
timings = cea1080p60;
|
||||
else
|
||||
err = v4l2_subdev_call(s->sd, video, g_dv_timings, &timings);
|
||||
err = v4l2_subdev_call(s->sd, pad, g_dv_timings, 0, &timings);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
@ -1243,7 +1243,7 @@ static int cobalt_node_register(struct cobalt *cobalt, int node)
|
||||
if (s->sd)
|
||||
vdev->ctrl_handler = s->sd->ctrl_handler;
|
||||
s->timings = dv1080p60;
|
||||
v4l2_subdev_call(s->sd, video, s_dv_timings, &s->timings);
|
||||
v4l2_subdev_call(s->sd, pad, s_dv_timings, 0, &s->timings);
|
||||
if (!s->is_output && s->sd)
|
||||
cobalt_enable_input(s);
|
||||
vdev->ioctl_ops = s->is_dummy ? &cobalt_ioctl_empty_ops :
|
||||
|
@ -1,11 +1,13 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
source "drivers/media/pci/intel/ipu3/Kconfig"
|
||||
source "drivers/media/pci/intel/ipu6/Kconfig"
|
||||
source "drivers/media/pci/intel/ivsc/Kconfig"
|
||||
|
||||
config IPU_BRIDGE
|
||||
tristate "Intel IPU Bridge"
|
||||
depends on I2C && ACPI
|
||||
depends on ACPI || COMPILE_TEST
|
||||
depends on I2C
|
||||
help
|
||||
The IPU bridge is a helper library for Intel IPU drivers to
|
||||
function on systems shipped with Windows.
|
||||
|
@ -5,3 +5,4 @@
|
||||
obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o
|
||||
obj-y += ipu3/
|
||||
obj-y += ivsc/
|
||||
obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include <media/ipu-bridge.h>
|
||||
#include <media/v4l2-fwnode.h>
|
||||
|
||||
#define ADEV_DEV(adev) ACPI_PTR(&((adev)->dev))
|
||||
|
||||
/*
|
||||
* 92335fcf-3203-4472-af93-7b4453ac29da
|
||||
*
|
||||
@ -87,6 +89,7 @@ static const char * const ipu_vcm_types[] = {
|
||||
"lc898212axb",
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
/*
|
||||
* Used to figure out IVSC acpi device by ipu_bridge_get_ivsc_acpi_dev()
|
||||
* instead of device and driver match to probe IVSC device.
|
||||
@ -100,13 +103,13 @@ static const struct acpi_device_id ivsc_acpi_ids[] = {
|
||||
|
||||
static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev)
|
||||
{
|
||||
acpi_handle handle = acpi_device_handle(adev);
|
||||
struct acpi_device *consumer, *ivsc_adev;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) {
|
||||
const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i];
|
||||
struct acpi_device *consumer, *ivsc_adev;
|
||||
|
||||
acpi_handle handle = acpi_device_handle(adev);
|
||||
for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1)
|
||||
/* camera sensor depends on IVSC in DSDT if exist */
|
||||
for_each_acpi_consumer_dev(ivsc_adev, consumer)
|
||||
@ -118,6 +121,12 @@ static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ipu_bridge_match_ivsc_dev(struct device *dev, const void *adev)
|
||||
{
|
||||
@ -163,7 +172,7 @@ static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor,
|
||||
csi_dev = ipu_bridge_get_ivsc_csi_dev(adev);
|
||||
if (!csi_dev) {
|
||||
acpi_dev_put(adev);
|
||||
dev_err(&adev->dev, "Failed to find MEI CSI dev\n");
|
||||
dev_err(ADEV_DEV(adev), "Failed to find MEI CSI dev\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -182,24 +191,25 @@ static int ipu_bridge_read_acpi_buffer(struct acpi_device *adev, char *id,
|
||||
acpi_status status;
|
||||
int ret = 0;
|
||||
|
||||
status = acpi_evaluate_object(adev->handle, id, NULL, &buffer);
|
||||
status = acpi_evaluate_object(ACPI_PTR(adev->handle),
|
||||
id, NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
obj = buffer.pointer;
|
||||
if (!obj) {
|
||||
dev_err(&adev->dev, "Couldn't locate ACPI buffer\n");
|
||||
dev_err(ADEV_DEV(adev), "Couldn't locate ACPI buffer\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
dev_err(&adev->dev, "Not an ACPI buffer\n");
|
||||
dev_err(ADEV_DEV(adev), "Not an ACPI buffer\n");
|
||||
ret = -ENODEV;
|
||||
goto out_free_buff;
|
||||
}
|
||||
|
||||
if (obj->buffer.length > size) {
|
||||
dev_err(&adev->dev, "Given buffer is too small\n");
|
||||
dev_err(ADEV_DEV(adev), "Given buffer is too small\n");
|
||||
ret = -EINVAL;
|
||||
goto out_free_buff;
|
||||
}
|
||||
@ -220,7 +230,7 @@ static u32 ipu_bridge_parse_rotation(struct acpi_device *adev,
|
||||
case IPU_SENSOR_ROTATION_INVERTED:
|
||||
return 180;
|
||||
default:
|
||||
dev_warn(&adev->dev,
|
||||
dev_warn(ADEV_DEV(adev),
|
||||
"Unknown rotation %d. Assume 0 degree rotation\n",
|
||||
ssdb->degree);
|
||||
return 0;
|
||||
@ -230,12 +240,14 @@ static u32 ipu_bridge_parse_rotation(struct acpi_device *adev,
|
||||
static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_device *adev)
|
||||
{
|
||||
enum v4l2_fwnode_orientation orientation;
|
||||
struct acpi_pld_info *pld;
|
||||
acpi_status status;
|
||||
struct acpi_pld_info *pld = NULL;
|
||||
acpi_status status = AE_ERROR;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
status = acpi_get_physical_device_location(adev->handle, &pld);
|
||||
#endif
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_warn(&adev->dev, "_PLD call failed, using default orientation\n");
|
||||
dev_warn(ADEV_DEV(adev), "_PLD call failed, using default orientation\n");
|
||||
return V4L2_FWNODE_ORIENTATION_EXTERNAL;
|
||||
}
|
||||
|
||||
@ -253,7 +265,8 @@ static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_dev
|
||||
orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL;
|
||||
break;
|
||||
default:
|
||||
dev_warn(&adev->dev, "Unknown _PLD panel val %d\n", pld->panel);
|
||||
dev_warn(ADEV_DEV(adev), "Unknown _PLD panel val %d\n",
|
||||
pld->panel);
|
||||
orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL;
|
||||
break;
|
||||
}
|
||||
@ -272,12 +285,12 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor)
|
||||
return ret;
|
||||
|
||||
if (ssdb.vcmtype > ARRAY_SIZE(ipu_vcm_types)) {
|
||||
dev_warn(&adev->dev, "Unknown VCM type %d\n", ssdb.vcmtype);
|
||||
dev_warn(ADEV_DEV(adev), "Unknown VCM type %d\n", ssdb.vcmtype);
|
||||
ssdb.vcmtype = 0;
|
||||
}
|
||||
|
||||
if (ssdb.lanes > IPU_MAX_LANES) {
|
||||
dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n");
|
||||
dev_err(ADEV_DEV(adev), "Number of lanes in SSDB is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -465,8 +478,14 @@ static void ipu_bridge_create_connection_swnodes(struct ipu_bridge *bridge,
|
||||
sensor->ipu_properties);
|
||||
|
||||
if (sensor->csi_dev) {
|
||||
const char *device_hid = "";
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
device_hid = acpi_device_hid(sensor->ivsc_adev);
|
||||
#endif
|
||||
|
||||
snprintf(sensor->ivsc_name, sizeof(sensor->ivsc_name), "%s-%u",
|
||||
acpi_device_hid(sensor->ivsc_adev), sensor->link);
|
||||
device_hid, sensor->link);
|
||||
|
||||
nodes[SWNODE_IVSC_HID] = NODE_SENSOR(sensor->ivsc_name,
|
||||
sensor->ivsc_properties);
|
||||
@ -631,11 +650,15 @@ static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg,
|
||||
{
|
||||
struct fwnode_handle *fwnode, *primary;
|
||||
struct ipu_sensor *sensor;
|
||||
struct acpi_device *adev;
|
||||
struct acpi_device *adev = NULL;
|
||||
int ret;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) {
|
||||
if (!adev->status.enabled)
|
||||
#else
|
||||
while (true) {
|
||||
#endif
|
||||
if (!ACPI_PTR(adev->status.enabled))
|
||||
continue;
|
||||
|
||||
if (bridge->n_sensors >= IPU_MAX_PORTS) {
|
||||
@ -671,7 +694,7 @@ static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg,
|
||||
goto err_free_swnodes;
|
||||
}
|
||||
|
||||
sensor->adev = acpi_dev_get(adev);
|
||||
sensor->adev = ACPI_PTR(acpi_dev_get(adev));
|
||||
|
||||
primary = acpi_fwnode_handle(adev);
|
||||
primary->secondary = fwnode;
|
||||
@ -727,11 +750,16 @@ static int ipu_bridge_ivsc_is_ready(void)
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) {
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
const struct ipu_sensor_config *cfg =
|
||||
&ipu_supported_sensors[i];
|
||||
|
||||
for_each_acpi_dev_match(sensor_adev, cfg->hid, NULL, -1) {
|
||||
if (!sensor_adev->status.enabled)
|
||||
#else
|
||||
while (true) {
|
||||
sensor_adev = NULL;
|
||||
#endif
|
||||
if (!ACPI_PTR(sensor_adev->status.enabled))
|
||||
continue;
|
||||
|
||||
adev = ipu_bridge_get_ivsc_acpi_dev(sensor_adev);
|
||||
|
@ -4,9 +4,9 @@
|
||||
*
|
||||
* Based partially on Intel IPU4 driver written by
|
||||
* Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
* Samu Onkalo <samu.onkalo@intel.com>
|
||||
* Samu Onkalo
|
||||
* Jouni Högander <jouni.hogander@intel.com>
|
||||
* Jouni Ukkonen <jouni.ukkonen@intel.com>
|
||||
* Jouni Ukkonen
|
||||
* Antti Laakso <antti.laakso@intel.com>
|
||||
* et al.
|
||||
*/
|
||||
@ -1752,11 +1752,6 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
|
||||
|
||||
v4l2_async_nf_init(&cio2->notifier, &cio2->v4l2_dev);
|
||||
|
||||
/* Register notifier for subdevices we care */
|
||||
r = cio2_parse_firmware(cio2);
|
||||
if (r)
|
||||
goto fail_clean_notifier;
|
||||
|
||||
r = devm_request_irq(dev, pci_dev->irq, cio2_irq, IRQF_SHARED,
|
||||
CIO2_NAME, cio2);
|
||||
if (r) {
|
||||
@ -1764,6 +1759,11 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
|
||||
goto fail_clean_notifier;
|
||||
}
|
||||
|
||||
/* Register notifier for subdevices we care */
|
||||
r = cio2_parse_firmware(cio2);
|
||||
if (r)
|
||||
goto fail_clean_notifier;
|
||||
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_allow(dev);
|
||||
|
||||
@ -1807,16 +1807,10 @@ static int __maybe_unused cio2_runtime_suspend(struct device *dev)
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
|
||||
void __iomem *const base = cio2->base;
|
||||
u16 pm;
|
||||
|
||||
writel(CIO2_D0I3C_I3, base + CIO2_REG_D0I3C);
|
||||
dev_dbg(dev, "cio2 runtime suspend.\n");
|
||||
|
||||
pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm);
|
||||
pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT;
|
||||
pm |= CIO2_PMCSR_D3;
|
||||
pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1825,15 +1819,10 @@ static int __maybe_unused cio2_runtime_resume(struct device *dev)
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
|
||||
void __iomem *const base = cio2->base;
|
||||
u16 pm;
|
||||
|
||||
writel(CIO2_D0I3C_RR, base + CIO2_REG_D0I3C);
|
||||
dev_dbg(dev, "cio2 runtime resume.\n");
|
||||
|
||||
pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm);
|
||||
pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT;
|
||||
pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2006,10 +1995,10 @@ static struct pci_driver cio2_pci_driver = {
|
||||
|
||||
module_pci_driver(cio2_pci_driver);
|
||||
|
||||
MODULE_AUTHOR("Tuukka Toivonen <tuukka.toivonen@intel.com>");
|
||||
MODULE_AUTHOR("Tuukka Toivonen");
|
||||
MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
|
||||
MODULE_AUTHOR("Jian Xu Zheng");
|
||||
MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
|
||||
MODULE_AUTHOR("Yuning Pu");
|
||||
MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("IPU3 CIO2 driver");
|
||||
|
@ -320,10 +320,6 @@ struct pci_dev;
|
||||
#define CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT 0x4
|
||||
#define CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT 0x570
|
||||
|
||||
#define CIO2_PMCSR_OFFSET 4U
|
||||
#define CIO2_PMCSR_D0D3_SHIFT 2U
|
||||
#define CIO2_PMCSR_D3 0x3
|
||||
|
||||
struct cio2_csi2_timing {
|
||||
s32 clk_termen;
|
||||
s32 clk_settle;
|
||||
|
18
drivers/media/pci/intel/ipu6/Kconfig
Normal file
18
drivers/media/pci/intel/ipu6/Kconfig
Normal file
@ -0,0 +1,18 @@
|
||||
config VIDEO_INTEL_IPU6
|
||||
tristate "Intel IPU6 driver"
|
||||
depends on ACPI || COMPILE_TEST
|
||||
depends on VIDEO_DEV
|
||||
depends on X86 && X86_64 && HAS_DMA
|
||||
select DMA_OPS
|
||||
select IOMMU_IOVA
|
||||
select VIDEO_V4L2_SUBDEV_API
|
||||
select MEDIA_CONTROLLER
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
select V4L2_FWNODE
|
||||
select IPU_BRIDGE
|
||||
help
|
||||
This is the 6th Gen Intel Image Processing Unit, found in Intel SoCs
|
||||
and used for capturing images and video from camera sensors.
|
||||
|
||||
To compile this driver, say Y here! It contains 2 modules -
|
||||
intel_ipu6 and intel_ipu6_isys.
|
23
drivers/media/pci/intel/ipu6/Makefile
Normal file
23
drivers/media/pci/intel/ipu6/Makefile
Normal file
@ -0,0 +1,23 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
intel-ipu6-y := ipu6.o \
|
||||
ipu6-bus.o \
|
||||
ipu6-dma.o \
|
||||
ipu6-mmu.o \
|
||||
ipu6-buttress.o \
|
||||
ipu6-cpd.o \
|
||||
ipu6-fw-com.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6.o
|
||||
|
||||
intel-ipu6-isys-y := ipu6-isys.o \
|
||||
ipu6-isys-csi2.o \
|
||||
ipu6-fw-isys.o \
|
||||
ipu6-isys-video.o \
|
||||
ipu6-isys-queue.o \
|
||||
ipu6-isys-subdev.o \
|
||||
ipu6-isys-mcd-phy.o \
|
||||
ipu6-isys-jsl-phy.o \
|
||||
ipu6-isys-dwc-phy.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6-isys.o
|
165
drivers/media/pci/intel/ipu6/ipu6-bus.c
Normal file
165
drivers/media/pci/intel/ipu6/ipu6-bus.c
Normal file
@ -0,0 +1,165 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2013 - 2024 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "ipu6.h"
|
||||
#include "ipu6-bus.h"
|
||||
#include "ipu6-buttress.h"
|
||||
#include "ipu6-dma.h"
|
||||
|
||||
static int bus_pm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
|
||||
int ret;
|
||||
|
||||
ret = pm_generic_runtime_suspend(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ipu6_buttress_power(dev, adev->ctrl, false);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
dev_err(dev, "power down failed!\n");
|
||||
|
||||
/* Powering down failed, attempt to resume device now */
|
||||
ret = pm_generic_runtime_resume(dev);
|
||||
if (!ret)
|
||||
return -EBUSY;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int bus_pm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
|
||||
int ret;
|
||||
|
||||
ret = ipu6_buttress_power(dev, adev->ctrl, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pm_generic_runtime_resume(dev);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
ipu6_buttress_power(dev, adev->ctrl, false);
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static struct dev_pm_domain ipu6_bus_pm_domain = {
|
||||
.ops = {
|
||||
.runtime_suspend = bus_pm_runtime_suspend,
|
||||
.runtime_resume = bus_pm_runtime_resume,
|
||||
},
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(ipu6_bus_mutex);
|
||||
|
||||
static void ipu6_bus_release(struct device *dev)
|
||||
{
|
||||
struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
|
||||
|
||||
kfree(adev->pdata);
|
||||
kfree(adev);
|
||||
}
|
||||
|
||||
struct ipu6_bus_device *
|
||||
ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
|
||||
void *pdata, struct ipu6_buttress_ctrl *ctrl,
|
||||
char *name)
|
||||
{
|
||||
struct auxiliary_device *auxdev;
|
||||
struct ipu6_bus_device *adev;
|
||||
struct ipu6_device *isp = pci_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
adev = kzalloc(sizeof(*adev), GFP_KERNEL);
|
||||
if (!adev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
adev->dma_mask = DMA_BIT_MASK(isp->secure_mode ? IPU6_MMU_ADDR_BITS :
|
||||
IPU6_MMU_ADDR_BITS_NON_SECURE);
|
||||
adev->isp = isp;
|
||||
adev->ctrl = ctrl;
|
||||
adev->pdata = pdata;
|
||||
auxdev = &adev->auxdev;
|
||||
auxdev->name = name;
|
||||
auxdev->id = (pci_domain_nr(pdev->bus) << 16) |
|
||||
PCI_DEVID(pdev->bus->number, pdev->devfn);
|
||||
|
||||
auxdev->dev.parent = parent;
|
||||
auxdev->dev.release = ipu6_bus_release;
|
||||
auxdev->dev.dma_ops = &ipu6_dma_ops;
|
||||
auxdev->dev.dma_mask = &adev->dma_mask;
|
||||
auxdev->dev.dma_parms = pdev->dev.dma_parms;
|
||||
auxdev->dev.coherent_dma_mask = adev->dma_mask;
|
||||
|
||||
ret = auxiliary_device_init(auxdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n",
|
||||
ret);
|
||||
kfree(adev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
dev_pm_domain_set(&auxdev->dev, &ipu6_bus_pm_domain);
|
||||
|
||||
pm_runtime_forbid(&adev->auxdev.dev);
|
||||
pm_runtime_enable(&adev->auxdev.dev);
|
||||
|
||||
return adev;
|
||||
}
|
||||
|
||||
int ipu6_bus_add_device(struct ipu6_bus_device *adev)
|
||||
{
|
||||
struct auxiliary_device *auxdev = &adev->auxdev;
|
||||
int ret;
|
||||
|
||||
ret = auxiliary_device_add(auxdev);
|
||||
if (ret) {
|
||||
auxiliary_device_uninit(auxdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&ipu6_bus_mutex);
|
||||
list_add(&adev->list, &adev->isp->devices);
|
||||
mutex_unlock(&ipu6_bus_mutex);
|
||||
|
||||
pm_runtime_allow(&auxdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ipu6_bus_del_devices(struct pci_dev *pdev)
|
||||
{
|
||||
struct ipu6_device *isp = pci_get_drvdata(pdev);
|
||||
struct ipu6_bus_device *adev, *save;
|
||||
|
||||
mutex_lock(&ipu6_bus_mutex);
|
||||
|
||||
list_for_each_entry_safe(adev, save, &isp->devices, list) {
|
||||
pm_runtime_disable(&adev->auxdev.dev);
|
||||
list_del(&adev->list);
|
||||
auxiliary_device_delete(&adev->auxdev);
|
||||
auxiliary_device_uninit(&adev->auxdev);
|
||||
}
|
||||
|
||||
mutex_unlock(&ipu6_bus_mutex);
|
||||
}
|
58
drivers/media/pci/intel/ipu6/ipu6-bus.h
Normal file
58
drivers/media/pci/intel/ipu6/ipu6-bus.h
Normal file
@ -0,0 +1,58 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (C) 2013 - 2024 Intel Corporation */
|
||||
|
||||
#ifndef IPU6_BUS_H
|
||||
#define IPU6_BUS_H
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct firmware;
|
||||
struct pci_dev;
|
||||
|
||||
#define IPU6_BUS_NAME IPU6_NAME "-bus"
|
||||
|
||||
struct ipu6_buttress_ctrl;
|
||||
|
||||
struct ipu6_bus_device {
|
||||
struct auxiliary_device auxdev;
|
||||
struct auxiliary_driver *auxdrv;
|
||||
const struct ipu6_auxdrv_data *auxdrv_data;
|
||||
struct list_head list;
|
||||
void *pdata;
|
||||
struct ipu6_mmu *mmu;
|
||||
struct ipu6_device *isp;
|
||||
struct ipu6_buttress_ctrl *ctrl;
|
||||
u64 dma_mask;
|
||||
const struct firmware *fw;
|
||||
struct sg_table fw_sgt;
|
||||
u64 *pkg_dir;
|
||||
dma_addr_t pkg_dir_dma_addr;
|
||||
unsigned int pkg_dir_size;
|
||||
};
|
||||
|
||||
struct ipu6_auxdrv_data {
|
||||
irqreturn_t (*isr)(struct ipu6_bus_device *adev);
|
||||
irqreturn_t (*isr_threaded)(struct ipu6_bus_device *adev);
|
||||
bool wake_isr_thread;
|
||||
};
|
||||
|
||||
#define to_ipu6_bus_device(_dev) \
|
||||
container_of(to_auxiliary_dev(_dev), struct ipu6_bus_device, auxdev)
|
||||
#define auxdev_to_adev(_auxdev) \
|
||||
container_of(_auxdev, struct ipu6_bus_device, auxdev)
|
||||
#define ipu6_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev)
|
||||
|
||||
struct ipu6_bus_device *
|
||||
ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
|
||||
void *pdata, struct ipu6_buttress_ctrl *ctrl,
|
||||
char *name);
|
||||
int ipu6_bus_add_device(struct ipu6_bus_device *adev);
|
||||
void ipu6_bus_del_devices(struct pci_dev *pdev);
|
||||
|
||||
#endif
|
917
drivers/media/pci/intel/ipu6/ipu6-buttress.c
Normal file
917
drivers/media/pci/intel/ipu6/ipu6-buttress.c
Normal file
@ -0,0 +1,917 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2013--2024 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pfn.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time64.h>
|
||||
|
||||
#include "ipu6.h"
|
||||
#include "ipu6-bus.h"
|
||||
#include "ipu6-buttress.h"
|
||||
#include "ipu6-platform-buttress-regs.h"
|
||||
|
||||
#define BOOTLOADER_STATUS_OFFSET 0x15c
|
||||
|
||||
#define BOOTLOADER_MAGIC_KEY 0xb00710ad
|
||||
|
||||
#define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1
|
||||
#define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2
|
||||
#define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE
|
||||
|
||||
#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10
|
||||
|
||||
#define BUTTRESS_POWER_TIMEOUT_US (200 * USEC_PER_MSEC)
|
||||
|
||||
#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US (5 * USEC_PER_SEC)
|
||||
#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US (10 * USEC_PER_SEC)
|
||||
#define BUTTRESS_CSE_FWRESET_TIMEOUT_US (100 * USEC_PER_MSEC)
|
||||
|
||||
#define BUTTRESS_IPC_TX_TIMEOUT_MS MSEC_PER_SEC
|
||||
#define BUTTRESS_IPC_RX_TIMEOUT_MS MSEC_PER_SEC
|
||||
#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US (1 * USEC_PER_SEC)
|
||||
#define BUTTRESS_TSC_SYNC_TIMEOUT_US (5 * USEC_PER_MSEC)
|
||||
|
||||
#define BUTTRESS_IPC_RESET_RETRY 2000
|
||||
#define BUTTRESS_CSE_IPC_RESET_RETRY 4
|
||||
#define BUTTRESS_IPC_CMD_SEND_RETRY 1
|
||||
|
||||
#define BUTTRESS_MAX_CONSECUTIVE_IRQS 100
|
||||
|
||||
static const u32 ipu6_adev_irq_mask[2] = {
|
||||
BUTTRESS_ISR_IS_IRQ,
|
||||
BUTTRESS_ISR_PS_IRQ
|
||||
};
|
||||
|
||||
int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
|
||||
struct ipu6_buttress_ipc *ipc)
|
||||
{
|
||||
unsigned int retries = BUTTRESS_IPC_RESET_RETRY;
|
||||
struct ipu6_buttress *b = &isp->buttress;
|
||||
u32 val = 0, csr_in_clr;
|
||||
|
||||
if (!isp->secure_mode) {
|
||||
dev_dbg(&isp->pdev->dev, "Skip IPC reset for non-secure mode");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_lock(&b->ipc_mutex);
|
||||
|
||||
/* Clear-by-1 CSR (all bits), corresponding internal states. */
|
||||
val = readl(isp->base + ipc->csr_in);
|
||||
writel(val, isp->base + ipc->csr_in);
|
||||
|
||||
/* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */
|
||||
writel(ENTRY, isp->base + ipc->csr_out);
|
||||
/*
|
||||
* Clear-by-1 all CSR bits EXCEPT following
|
||||
* bits:
|
||||
* A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
|
||||
* B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
|
||||
* C. Possibly custom bits, depending on
|
||||
* their role.
|
||||
*/
|
||||
csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ |
|
||||
BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID |
|
||||
BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY;
|
||||
|
||||
do {
|
||||
usleep_range(400, 500);
|
||||
val = readl(isp->base + ipc->csr_in);
|
||||
switch (val) {
|
||||
case ENTRY | EXIT:
|
||||
case ENTRY | EXIT | QUERY:
|
||||
/*
|
||||
* 1) Clear-by-1 CSR bits
|
||||
* (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
|
||||
* IPC_PEER_COMP_ACTIONS_RST_PHASE2).
|
||||
* 2) Set peer CSR bit
|
||||
* IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
|
||||
*/
|
||||
writel(ENTRY | EXIT, isp->base + ipc->csr_in);
|
||||
writel(QUERY, isp->base + ipc->csr_out);
|
||||
break;
|
||||
case ENTRY:
|
||||
case ENTRY | QUERY:
|
||||
/*
|
||||
* 1) Clear-by-1 CSR bits
|
||||
* (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
|
||||
* IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE).
|
||||
* 2) Set peer CSR bit
|
||||
* IPC_PEER_COMP_ACTIONS_RST_PHASE1.
|
||||
*/
|
||||
writel(ENTRY | QUERY, isp->base + ipc->csr_in);
|
||||
writel(ENTRY, isp->base + ipc->csr_out);
|
||||
break;
|
||||
case EXIT:
|
||||
case EXIT | QUERY:
|
||||
/*
|
||||
* Clear-by-1 CSR bit
|
||||
* IPC_PEER_COMP_ACTIONS_RST_PHASE2.
|
||||
* 1) Clear incoming doorbell.
|
||||
* 2) Clear-by-1 all CSR bits EXCEPT following
|
||||
* bits:
|
||||
* A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
|
||||
* B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
|
||||
* C. Possibly custom bits, depending on
|
||||
* their role.
|
||||
* 3) Set peer CSR bit
|
||||
* IPC_PEER_COMP_ACTIONS_RST_PHASE2.
|
||||
*/
|
||||
writel(EXIT, isp->base + ipc->csr_in);
|
||||
writel(0, isp->base + ipc->db0_in);
|
||||
writel(csr_in_clr, isp->base + ipc->csr_in);
|
||||
writel(EXIT, isp->base + ipc->csr_out);
|
||||
|
||||
/*
|
||||
* Read csr_in again to make sure if RST_PHASE2 is done.
|
||||
* If csr_in is QUERY, it should be handled again.
|
||||
*/
|
||||
usleep_range(200, 300);
|
||||
val = readl(isp->base + ipc->csr_in);
|
||||
if (val & QUERY) {
|
||||
dev_dbg(&isp->pdev->dev,
|
||||
"RST_PHASE2 retry csr_in = %x\n", val);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&b->ipc_mutex);
|
||||
return 0;
|
||||
case QUERY:
|
||||
/*
|
||||
* 1) Clear-by-1 CSR bit
|
||||
* IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
|
||||
* 2) Set peer CSR bit
|
||||
* IPC_PEER_COMP_ACTIONS_RST_PHASE1
|
||||
*/
|
||||
writel(QUERY, isp->base + ipc->csr_in);
|
||||
writel(ENTRY, isp->base + ipc->csr_out);
|
||||
break;
|
||||
default:
|
||||
dev_warn_ratelimited(&isp->pdev->dev,
|
||||
"Unexpected CSR 0x%x\n", val);
|
||||
break;
|
||||
}
|
||||
} while (retries--);
|
||||
|
||||
mutex_unlock(&b->ipc_mutex);
|
||||
dev_err(&isp->pdev->dev, "Timed out while waiting for CSE\n");
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void ipu6_buttress_ipc_validity_close(struct ipu6_device *isp,
|
||||
struct ipu6_buttress_ipc *ipc)
|
||||
{
|
||||
writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ,
|
||||
isp->base + ipc->csr_out);
|
||||
}
|
||||
|
||||
static int
|
||||
ipu6_buttress_ipc_validity_open(struct ipu6_device *isp,
|
||||
struct ipu6_buttress_ipc *ipc)
|
||||
{
|
||||
unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID;
|
||||
void __iomem *addr;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ,
|
||||
isp->base + ipc->csr_out);
|
||||
|
||||
addr = isp->base + ipc->csr_in;
|
||||
ret = readl_poll_timeout(addr, val, val & mask, 200,
|
||||
BUTTRESS_IPC_VALIDITY_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val);
|
||||
ipu6_buttress_ipc_validity_close(isp, ipc);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ipu6_buttress_ipc_recv(struct ipu6_device *isp,
|
||||
struct ipu6_buttress_ipc *ipc, u32 *ipc_msg)
|
||||
{
|
||||
if (ipc_msg)
|
||||
*ipc_msg = readl(isp->base + ipc->data0_in);
|
||||
writel(0, isp->base + ipc->db0_in);
|
||||
}
|
||||
|
||||
static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp,
|
||||
enum ipu6_buttress_ipc_domain ipc_domain,
|
||||
struct ipu6_ipc_buttress_bulk_msg *msgs,
|
||||
u32 size)
|
||||
{
|
||||
unsigned long tx_timeout_jiffies, rx_timeout_jiffies;
|
||||
unsigned int i, retry = BUTTRESS_IPC_CMD_SEND_RETRY;
|
||||
struct ipu6_buttress *b = &isp->buttress;
|
||||
struct ipu6_buttress_ipc *ipc;
|
||||
u32 val;
|
||||
int ret;
|
||||
int tout;
|
||||
|
||||
ipc = ipc_domain == IPU6_BUTTRESS_IPC_CSE ? &b->cse : &b->ish;
|
||||
|
||||
mutex_lock(&b->ipc_mutex);
|
||||
|
||||
ret = ipu6_buttress_ipc_validity_open(isp, ipc);
|
||||
if (ret) {
|
||||
dev_err(&isp->pdev->dev, "IPC validity open failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS);
|
||||
rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
reinit_completion(&ipc->send_complete);
|
||||
if (msgs[i].require_resp)
|
||||
reinit_completion(&ipc->recv_complete);
|
||||
|
||||
dev_dbg(&isp->pdev->dev, "bulk IPC command: 0x%x\n",
|
||||
msgs[i].cmd);
|
||||
writel(msgs[i].cmd, isp->base + ipc->data0_out);
|
||||
val = BUTTRESS_IU2CSEDB0_BUSY | msgs[i].cmd_size;
|
||||
writel(val, isp->base + ipc->db0_out);
|
||||
|
||||
tout = wait_for_completion_timeout(&ipc->send_complete,
|
||||
tx_timeout_jiffies);
|
||||
if (!tout) {
|
||||
dev_err(&isp->pdev->dev, "send IPC response timeout\n");
|
||||
if (!retry--) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Try again if CSE is not responding on first try */
|
||||
writel(0, isp->base + ipc->db0_out);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
retry = BUTTRESS_IPC_CMD_SEND_RETRY;
|
||||
|
||||
if (!msgs[i].require_resp)
|
||||
continue;
|
||||
|
||||
tout = wait_for_completion_timeout(&ipc->recv_complete,
|
||||
rx_timeout_jiffies);
|
||||
if (!tout) {
|
||||
dev_err(&isp->pdev->dev, "recv IPC response timeout\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ipc->nack_mask &&
|
||||
(ipc->recv_data & ipc->nack_mask) == ipc->nack) {
|
||||
dev_err(&isp->pdev->dev,
|
||||
"IPC NACK for cmd 0x%x\n", msgs[i].cmd);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ipc->recv_data != msgs[i].expected_resp) {
|
||||
dev_err(&isp->pdev->dev,
|
||||
"expected resp: 0x%x, IPC response: 0x%x ",
|
||||
msgs[i].expected_resp, ipc->recv_data);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(&isp->pdev->dev, "bulk IPC commands done\n");
|
||||
|
||||
out:
|
||||
ipu6_buttress_ipc_validity_close(isp, ipc);
|
||||
mutex_unlock(&b->ipc_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
ipu6_buttress_ipc_send(struct ipu6_device *isp,
|
||||
enum ipu6_buttress_ipc_domain ipc_domain,
|
||||
u32 ipc_msg, u32 size, bool require_resp,
|
||||
u32 expected_resp)
|
||||
{
|
||||
struct ipu6_ipc_buttress_bulk_msg msg = {
|
||||
.cmd = ipc_msg,
|
||||
.cmd_size = size,
|
||||
.require_resp = require_resp,
|
||||
.expected_resp = expected_resp,
|
||||
};
|
||||
|
||||
return ipu6_buttress_ipc_send_bulk(isp, ipc_domain, &msg, 1);
|
||||
}
|
||||
|
||||
static irqreturn_t ipu6_buttress_call_isr(struct ipu6_bus_device *adev)
|
||||
{
|
||||
irqreturn_t ret = IRQ_WAKE_THREAD;
|
||||
|
||||
if (!adev || !adev->auxdrv || !adev->auxdrv_data)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (adev->auxdrv_data->isr)
|
||||
ret = adev->auxdrv_data->isr(adev);
|
||||
|
||||
if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded)
|
||||
ret = IRQ_NONE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr)
|
||||
{
|
||||
struct ipu6_device *isp = isp_ptr;
|
||||
struct ipu6_bus_device *adev[] = { isp->isys, isp->psys };
|
||||
struct ipu6_buttress *b = &isp->buttress;
|
||||
u32 reg_irq_sts = BUTTRESS_REG_ISR_STATUS;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 disable_irqs = 0;
|
||||
u32 irq_status;
|
||||
u32 i, count = 0;
|
||||
|
||||
pm_runtime_get_noresume(&isp->pdev->dev);
|
||||
|
||||
irq_status = readl(isp->base + reg_irq_sts);
|
||||
if (!irq_status) {
|
||||
pm_runtime_put_noidle(&isp->pdev->dev);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
do {
|
||||
writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask); i++) {
|
||||
irqreturn_t r = ipu6_buttress_call_isr(adev[i]);
|
||||
|
||||
if (!(irq_status & ipu6_adev_irq_mask[i]))
|
||||
continue;
|
||||
|
||||
if (r == IRQ_WAKE_THREAD) {
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
disable_irqs |= ipu6_adev_irq_mask[i];
|
||||
} else if (ret == IRQ_NONE && r == IRQ_HANDLED) {
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
if ((irq_status & BUTTRESS_EVENT) && ret == IRQ_NONE)
|
||||
ret = IRQ_HANDLED;
|
||||
|
||||
if (irq_status & BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING) {
|
||||
dev_dbg(&isp->pdev->dev,
|
||||
"BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING\n");
|
||||
ipu6_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data);
|
||||
complete(&b->cse.recv_complete);
|
||||
}
|
||||
|
||||
if (irq_status & BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING) {
|
||||
dev_dbg(&isp->pdev->dev,
|
||||
"BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING\n");
|
||||
ipu6_buttress_ipc_recv(isp, &b->ish, &b->ish.recv_data);
|
||||
complete(&b->ish.recv_complete);
|
||||
}
|
||||
|
||||
if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE) {
|
||||
dev_dbg(&isp->pdev->dev,
|
||||
"BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
|
||||
complete(&b->cse.send_complete);
|
||||
}
|
||||
|
||||
if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH) {
|
||||
dev_dbg(&isp->pdev->dev,
|
||||
"BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
|
||||
complete(&b->ish.send_complete);
|
||||
}
|
||||
|
||||
if (irq_status & BUTTRESS_ISR_SAI_VIOLATION &&
|
||||
ipu6_buttress_get_secure_mode(isp))
|
||||
dev_err(&isp->pdev->dev,
|
||||
"BUTTRESS_ISR_SAI_VIOLATION\n");
|
||||
|
||||
if (irq_status & (BUTTRESS_ISR_IS_FATAL_MEM_ERR |
|
||||
BUTTRESS_ISR_PS_FATAL_MEM_ERR))
|
||||
dev_err(&isp->pdev->dev,
|
||||
"BUTTRESS_ISR_FATAL_MEM_ERR\n");
|
||||
|
||||
if (irq_status & BUTTRESS_ISR_UFI_ERROR)
|
||||
dev_err(&isp->pdev->dev, "BUTTRESS_ISR_UFI_ERROR\n");
|
||||
|
||||
if (++count == BUTTRESS_MAX_CONSECUTIVE_IRQS) {
|
||||
dev_err(&isp->pdev->dev, "too many consecutive IRQs\n");
|
||||
ret = IRQ_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
irq_status = readl(isp->base + reg_irq_sts);
|
||||
} while (irq_status);
|
||||
|
||||
if (disable_irqs)
|
||||
writel(BUTTRESS_IRQS & ~disable_irqs,
|
||||
isp->base + BUTTRESS_REG_ISR_ENABLE);
|
||||
|
||||
pm_runtime_put(&isp->pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr)
|
||||
{
|
||||
struct ipu6_device *isp = isp_ptr;
|
||||
struct ipu6_bus_device *adev[] = { isp->isys, isp->psys };
|
||||
const struct ipu6_auxdrv_data *drv_data = NULL;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask) && adev[i]; i++) {
|
||||
drv_data = adev[i]->auxdrv_data;
|
||||
if (!drv_data)
|
||||
continue;
|
||||
|
||||
if (drv_data->wake_isr_thread &&
|
||||
drv_data->isr_threaded(adev[i]) == IRQ_HANDLED)
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl,
|
||||
bool on)
|
||||
{
|
||||
struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp;
|
||||
u32 pwr_sts, val;
|
||||
int ret;
|
||||
|
||||
if (!ctrl)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&isp->buttress.power_mutex);
|
||||
|
||||
if (!on) {
|
||||
val = 0;
|
||||
pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift;
|
||||
} else {
|
||||
val = BUTTRESS_FREQ_CTL_START |
|
||||
FIELD_PREP(BUTTRESS_FREQ_CTL_RATIO_MASK,
|
||||
ctrl->ratio) |
|
||||
FIELD_PREP(BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK,
|
||||
ctrl->qos_floor) |
|
||||
BUTTRESS_FREQ_CTL_ICCMAX_LEVEL;
|
||||
|
||||
pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift;
|
||||
}
|
||||
|
||||
writel(val, isp->base + ctrl->freq_ctl);
|
||||
|
||||
ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE,
|
||||
val, (val & ctrl->pwr_sts_mask) == pwr_sts,
|
||||
100, BUTTRESS_POWER_TIMEOUT_US);
|
||||
if (ret)
|
||||
dev_err(&isp->pdev->dev,
|
||||
"Change power status timeout with 0x%x\n", val);
|
||||
|
||||
ctrl->started = !ret && on;
|
||||
|
||||
mutex_unlock(&isp->buttress.power_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
|
||||
|
||||
return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE;
|
||||
}
|
||||
|
||||
bool ipu6_buttress_auth_done(struct ipu6_device *isp)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (!isp->secure_mode)
|
||||
return true;
|
||||
|
||||
val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
|
||||
val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val);
|
||||
|
||||
return val == BUTTRESS_SECURITY_CTL_AUTH_DONE;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_buttress_auth_done, INTEL_IPU6);
|
||||
|
||||
int ipu6_buttress_reset_authentication(struct ipu6_device *isp)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
if (!isp->secure_mode) {
|
||||
dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
writel(BUTTRESS_FW_RESET_CTL_START, isp->base +
|
||||
BUTTRESS_REG_FW_RESET_CTL);
|
||||
|
||||
ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val,
|
||||
val & BUTTRESS_FW_RESET_CTL_DONE, 500,
|
||||
BUTTRESS_CSE_FWRESET_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(&isp->pdev->dev,
|
||||
"Time out while resetting authentication state\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&isp->pdev->dev, "FW reset for authentication done\n");
|
||||
writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL);
|
||||
/* leave some time for HW restore */
|
||||
usleep_range(800, 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys,
|
||||
const struct firmware *fw, struct sg_table *sgt)
|
||||
{
|
||||
bool is_vmalloc = is_vmalloc_addr(fw->data);
|
||||
struct page **pages;
|
||||
const void *addr;
|
||||
unsigned long n_pages;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (!is_vmalloc && !virt_addr_valid(fw->data))
|
||||
return -EDOM;
|
||||
|
||||
n_pages = PHYS_PFN(PAGE_ALIGN(fw->size));
|
||||
|
||||
pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL);
|
||||
if (!pages)
|
||||
return -ENOMEM;
|
||||
|
||||
addr = fw->data;
|
||||
for (i = 0; i < n_pages; i++) {
|
||||
struct page *p = is_vmalloc ?
|
||||
vmalloc_to_page(addr) : virt_to_page(addr);
|
||||
|
||||
if (!p) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
pages[i] = p;
|
||||
addr += PAGE_SIZE;
|
||||
}
|
||||
|
||||
ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, fw->size,
|
||||
GFP_KERNEL);
|
||||
if (ret) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = dma_map_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0);
|
||||
if (ret < 0) {
|
||||
ret = -ENOMEM;
|
||||
sg_free_table(sgt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dma_sync_sgtable_for_device(&sys->auxdev.dev, sgt, DMA_TO_DEVICE);
|
||||
|
||||
out:
|
||||
kfree(pages);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, INTEL_IPU6);
|
||||
|
||||
void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys,
|
||||
struct sg_table *sgt)
|
||||
{
|
||||
dma_unmap_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0);
|
||||
sg_free_table(sgt);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, INTEL_IPU6);
|
||||
|
||||
int ipu6_buttress_authenticate(struct ipu6_device *isp)
|
||||
{
|
||||
struct ipu6_buttress *b = &isp->buttress;
|
||||
struct ipu6_psys_pdata *psys_pdata;
|
||||
u32 data, mask, done, fail;
|
||||
int ret;
|
||||
|
||||
if (!isp->secure_mode) {
|
||||
dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
psys_pdata = isp->psys->pdata;
|
||||
|
||||
mutex_lock(&b->auth_mutex);
|
||||
|
||||
if (ipu6_buttress_auth_done(isp)) {
|
||||
ret = 0;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write address of FIT table to FW_SOURCE register
|
||||
* Let's use fw address. I.e. not using FIT table yet
|
||||
*/
|
||||
data = lower_32_bits(isp->psys->pkg_dir_dma_addr);
|
||||
writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO);
|
||||
|
||||
data = upper_32_bits(isp->psys->pkg_dir_dma_addr);
|
||||
writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI);
|
||||
|
||||
/*
|
||||
* Write boot_load into IU2CSEDATA0
|
||||
* Write sizeof(boot_load) | 0x2 << CLIENT_ID to
|
||||
* IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
|
||||
*/
|
||||
dev_info(&isp->pdev->dev, "Sending BOOT_LOAD to CSE\n");
|
||||
|
||||
ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE,
|
||||
BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD,
|
||||
1, true,
|
||||
BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE);
|
||||
if (ret) {
|
||||
dev_err(&isp->pdev->dev, "CSE boot_load failed\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK;
|
||||
done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE;
|
||||
fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED;
|
||||
ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
|
||||
((data & mask) == done ||
|
||||
(data & mask) == fail), 500,
|
||||
BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(&isp->pdev->dev, "CSE boot_load timeout\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if ((data & mask) == fail) {
|
||||
dev_err(&isp->pdev->dev, "CSE auth failed\n");
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = readl_poll_timeout(psys_pdata->base + BOOTLOADER_STATUS_OFFSET,
|
||||
data, data == BOOTLOADER_MAGIC_KEY, 500,
|
||||
BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(&isp->pdev->dev, "Unexpected magic number 0x%x\n",
|
||||
data);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write authenticate_run into IU2CSEDATA0
|
||||
* Write sizeof(boot_load) | 0x2 << CLIENT_ID to
|
||||
* IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
|
||||
*/
|
||||
dev_info(&isp->pdev->dev, "Sending AUTHENTICATE_RUN to CSE\n");
|
||||
ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE,
|
||||
BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN,
|
||||
1, true,
|
||||
BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE);
|
||||
if (ret) {
|
||||
dev_err(&isp->pdev->dev, "CSE authenticate_run failed\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
done = BUTTRESS_SECURITY_CTL_AUTH_DONE;
|
||||
ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
|
||||
((data & mask) == done ||
|
||||
(data & mask) == fail), 500,
|
||||
BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(&isp->pdev->dev, "CSE authenticate timeout\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if ((data & mask) == fail) {
|
||||
dev_err(&isp->pdev->dev, "CSE boot_load failed\n");
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
dev_info(&isp->pdev->dev, "CSE authenticate_run done\n");
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&b->auth_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp)
|
||||
{
|
||||
u32 val, mask, done;
|
||||
int ret;
|
||||
|
||||
mask = BUTTRESS_PWR_STATE_HH_STATUS_MASK;
|
||||
|
||||
writel(BUTTRESS_FABRIC_CMD_START_TSC_SYNC,
|
||||
isp->base + BUTTRESS_REG_FABRIC_CMD);
|
||||
|
||||
val = readl(isp->base + BUTTRESS_REG_PWR_STATE);
|
||||
val = FIELD_GET(mask, val);
|
||||
if (val == BUTTRESS_PWR_STATE_HH_STATE_ERR) {
|
||||
dev_err(&isp->pdev->dev, "Start tsc sync failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
done = BUTTRESS_PWR_STATE_HH_STATE_DONE;
|
||||
ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, val,
|
||||
FIELD_GET(mask, val) == done, 500,
|
||||
BUTTRESS_TSC_SYNC_TIMEOUT_US);
|
||||
if (ret)
|
||||
dev_err(&isp->pdev->dev, "Start tsc sync timeout\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) {
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = ipu6_buttress_send_tsc_request(isp);
|
||||
if (ret != -ETIMEDOUT)
|
||||
return ret;
|
||||
|
||||
val = readl(isp->base + BUTTRESS_REG_TSW_CTL);
|
||||
val = val | BUTTRESS_TSW_CTL_SOFT_RESET;
|
||||
writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
|
||||
val = val & ~BUTTRESS_TSW_CTL_SOFT_RESET;
|
||||
writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
|
||||
}
|
||||
|
||||
dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n");
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, INTEL_IPU6);
|
||||
|
||||
void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val)
|
||||
{
|
||||
u32 tsc_hi_1, tsc_hi_2, tsc_lo;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
tsc_hi_1 = readl(isp->base + BUTTRESS_REG_TSC_HI);
|
||||
tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO);
|
||||
tsc_hi_2 = readl(isp->base + BUTTRESS_REG_TSC_HI);
|
||||
if (tsc_hi_1 == tsc_hi_2) {
|
||||
*val = (u64)tsc_hi_1 << 32 | tsc_lo;
|
||||
} else {
|
||||
/* Check if TSC has rolled over */
|
||||
if (tsc_lo & BIT(31))
|
||||
*val = (u64)tsc_hi_1 << 32 | tsc_lo;
|
||||
else
|
||||
*val = (u64)tsc_hi_2 << 32 | tsc_lo;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_read, INTEL_IPU6);
|
||||
|
||||
u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp)
|
||||
{
|
||||
u64 ns = ticks * 10000;
|
||||
|
||||
/*
|
||||
* converting TSC tick count to ns is calculated by:
|
||||
* Example (TSC clock frequency is 19.2MHz):
|
||||
* ns = ticks * 1000 000 000 / 19.2Mhz
|
||||
* = ticks * 1000 000 000 / 19200000Hz
|
||||
* = ticks * 10000 / 192 ns
|
||||
*/
|
||||
return div_u64(ns, isp->buttress.ref_clk);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, INTEL_IPU6);
|
||||
|
||||
void ipu6_buttress_restore(struct ipu6_device *isp)
|
||||
{
|
||||
struct ipu6_buttress *b = &isp->buttress;
|
||||
|
||||
writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR);
|
||||
writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
|
||||
writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_WDT);
|
||||
}
|
||||
|
||||
int ipu6_buttress_init(struct ipu6_device *isp)
|
||||
{
|
||||
int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY;
|
||||
struct ipu6_buttress *b = &isp->buttress;
|
||||
u32 val;
|
||||
|
||||
mutex_init(&b->power_mutex);
|
||||
mutex_init(&b->auth_mutex);
|
||||
mutex_init(&b->cons_mutex);
|
||||
mutex_init(&b->ipc_mutex);
|
||||
init_completion(&b->ish.send_complete);
|
||||
init_completion(&b->cse.send_complete);
|
||||
init_completion(&b->ish.recv_complete);
|
||||
init_completion(&b->cse.recv_complete);
|
||||
|
||||
b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK;
|
||||
b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK;
|
||||
b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR;
|
||||
b->cse.csr_out = BUTTRESS_REG_IU2CSECSR;
|
||||
b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0;
|
||||
b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0;
|
||||
b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0;
|
||||
b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0;
|
||||
|
||||
/* no ISH on IPU6 */
|
||||
memset(&b->ish, 0, sizeof(b->ish));
|
||||
INIT_LIST_HEAD(&b->constraints);
|
||||
|
||||
isp->secure_mode = ipu6_buttress_get_secure_mode(isp);
|
||||
dev_info(&isp->pdev->dev, "IPU6 in %s mode touch 0x%x mask 0x%x\n",
|
||||
isp->secure_mode ? "secure" : "non-secure",
|
||||
readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH),
|
||||
readl(isp->base + BUTTRESS_REG_CAMERA_MASK));
|
||||
|
||||
b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_WDT);
|
||||
writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR);
|
||||
writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
|
||||
|
||||
/* get ref_clk frequency by reading the indication in btrs control */
|
||||
val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL);
|
||||
val = FIELD_GET(BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND, val);
|
||||
|
||||
switch (val) {
|
||||
case 0x0:
|
||||
b->ref_clk = 240;
|
||||
break;
|
||||
case 0x1:
|
||||
b->ref_clk = 192;
|
||||
break;
|
||||
case 0x2:
|
||||
b->ref_clk = 384;
|
||||
break;
|
||||
default:
|
||||
dev_warn(&isp->pdev->dev,
|
||||
"Unsupported ref clock, use 19.2Mhz by default.\n");
|
||||
b->ref_clk = 192;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Retry couple of times in case of CSE initialization is delayed */
|
||||
do {
|
||||
ret = ipu6_buttress_ipc_reset(isp, &b->cse);
|
||||
if (ret) {
|
||||
dev_warn(&isp->pdev->dev,
|
||||
"IPC reset protocol failed, retrying\n");
|
||||
} else {
|
||||
dev_dbg(&isp->pdev->dev, "IPC reset done\n");
|
||||
return 0;
|
||||
}
|
||||
} while (ipc_reset_retry--);
|
||||
|
||||
dev_err(&isp->pdev->dev, "IPC reset protocol failed\n");
|
||||
|
||||
mutex_destroy(&b->power_mutex);
|
||||
mutex_destroy(&b->auth_mutex);
|
||||
mutex_destroy(&b->cons_mutex);
|
||||
mutex_destroy(&b->ipc_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ipu6_buttress_exit(struct ipu6_device *isp)
|
||||
{
|
||||
struct ipu6_buttress *b = &isp->buttress;
|
||||
|
||||
writel(0, isp->base + BUTTRESS_REG_ISR_ENABLE);
|
||||
|
||||
mutex_destroy(&b->power_mutex);
|
||||
mutex_destroy(&b->auth_mutex);
|
||||
mutex_destroy(&b->cons_mutex);
|
||||
mutex_destroy(&b->ipc_mutex);
|
||||
}
|
92
drivers/media/pci/intel/ipu6/ipu6-buttress.h
Normal file
92
drivers/media/pci/intel/ipu6/ipu6-buttress.h
Normal file
@ -0,0 +1,92 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (C) 2013--2024 Intel Corporation */
|
||||
|
||||
#ifndef IPU6_BUTTRESS_H
|
||||
#define IPU6_BUTTRESS_H
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
struct device;
|
||||
struct firmware;
|
||||
struct ipu6_device;
|
||||
struct ipu6_bus_device;
|
||||
|
||||
#define BUTTRESS_PS_FREQ_STEP 25U
|
||||
#define BUTTRESS_MIN_FORCE_PS_FREQ (BUTTRESS_PS_FREQ_STEP * 8)
|
||||
#define BUTTRESS_MAX_FORCE_PS_FREQ (BUTTRESS_PS_FREQ_STEP * 32)
|
||||
|
||||
#define BUTTRESS_IS_FREQ_STEP 25U
|
||||
#define BUTTRESS_MIN_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 8)
|
||||
#define BUTTRESS_MAX_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 22)
|
||||
|
||||
struct ipu6_buttress_ctrl {
|
||||
u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off;
|
||||
unsigned int ratio;
|
||||
unsigned int qos_floor;
|
||||
bool started;
|
||||
};
|
||||
|
||||
struct ipu6_buttress_ipc {
|
||||
struct completion send_complete;
|
||||
struct completion recv_complete;
|
||||
u32 nack;
|
||||
u32 nack_mask;
|
||||
u32 recv_data;
|
||||
u32 csr_out;
|
||||
u32 csr_in;
|
||||
u32 db0_in;
|
||||
u32 db0_out;
|
||||
u32 data0_out;
|
||||
u32 data0_in;
|
||||
};
|
||||
|
||||
struct ipu6_buttress {
|
||||
struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex;
|
||||
struct ipu6_buttress_ipc cse;
|
||||
struct ipu6_buttress_ipc ish;
|
||||
struct list_head constraints;
|
||||
u32 wdt_cached_value;
|
||||
bool force_suspend;
|
||||
u32 ref_clk;
|
||||
};
|
||||
|
||||
enum ipu6_buttress_ipc_domain {
|
||||
IPU6_BUTTRESS_IPC_CSE,
|
||||
IPU6_BUTTRESS_IPC_ISH,
|
||||
};
|
||||
|
||||
struct ipu6_ipc_buttress_bulk_msg {
|
||||
u32 cmd;
|
||||
u32 expected_resp;
|
||||
bool require_resp;
|
||||
u8 cmd_size;
|
||||
};
|
||||
|
||||
int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
|
||||
struct ipu6_buttress_ipc *ipc);
|
||||
int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys,
|
||||
const struct firmware *fw,
|
||||
struct sg_table *sgt);
|
||||
void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys,
|
||||
struct sg_table *sgt);
|
||||
int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl,
|
||||
bool on);
|
||||
bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp);
|
||||
int ipu6_buttress_authenticate(struct ipu6_device *isp);
|
||||
int ipu6_buttress_reset_authentication(struct ipu6_device *isp);
|
||||
bool ipu6_buttress_auth_done(struct ipu6_device *isp);
|
||||
int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp);
|
||||
void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val);
|
||||
u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp);
|
||||
|
||||
irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr);
|
||||
irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr);
|
||||
int ipu6_buttress_init(struct ipu6_device *isp);
|
||||
void ipu6_buttress_exit(struct ipu6_device *isp);
|
||||
void ipu6_buttress_csi_port_config(struct ipu6_device *isp,
|
||||
u32 legacy, u32 combo);
|
||||
void ipu6_buttress_restore(struct ipu6_device *isp);
|
||||
#endif /* IPU6_BUTTRESS_H */
|
362
drivers/media/pci/intel/ipu6/ipu6-cpd.c
Normal file
362
drivers/media/pci/intel/ipu6/ipu6-cpd.c
Normal file
@ -0,0 +1,362 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2013--2024 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/gfp_types.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "ipu6.h"
|
||||
#include "ipu6-bus.h"
|
||||
#include "ipu6-cpd.h"
|
||||
|
||||
/* 15 entries + header*/
|
||||
#define MAX_PKG_DIR_ENT_CNT 16
|
||||
/* 2 qword per entry/header */
|
||||
#define PKG_DIR_ENT_LEN 2
|
||||
/* PKG_DIR size in bytes */
|
||||
#define PKG_DIR_SIZE ((MAX_PKG_DIR_ENT_CNT) * \
|
||||
(PKG_DIR_ENT_LEN) * sizeof(u64))
|
||||
/* _IUPKDR_ */
|
||||
#define PKG_DIR_HDR_MARK 0x5f4955504b44525fULL
|
||||
|
||||
/* $CPD */
|
||||
#define CPD_HDR_MARK 0x44504324
|
||||
|
||||
#define MAX_MANIFEST_SIZE (SZ_2K * sizeof(u32))
|
||||
#define MAX_METADATA_SIZE SZ_64K
|
||||
|
||||
#define MAX_COMPONENT_ID 127
|
||||
#define MAX_COMPONENT_VERSION 0xffff
|
||||
|
||||
#define MANIFEST_IDX 0
|
||||
#define METADATA_IDX 1
|
||||
#define MODULEDATA_IDX 2
|
||||
/*
|
||||
* PKG_DIR Entry (type == id)
|
||||
* 63:56 55 54:48 47:32 31:24 23:0
|
||||
* Rsvd Rsvd Type Version Rsvd Size
|
||||
*/
|
||||
#define PKG_DIR_SIZE_MASK GENMASK(23, 0)
|
||||
#define PKG_DIR_VERSION_MASK GENMASK(47, 32)
|
||||
#define PKG_DIR_TYPE_MASK GENMASK(54, 48)
|
||||
|
||||
static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd,
|
||||
u8 idx)
|
||||
{
|
||||
const struct ipu6_cpd_hdr *cpd_hdr = cpd;
|
||||
const struct ipu6_cpd_ent *ent;
|
||||
|
||||
ent = (const struct ipu6_cpd_ent *)((const u8 *)cpd + cpd_hdr->hdr_len);
|
||||
return ent + idx;
|
||||
}
|
||||
|
||||
#define ipu6_cpd_get_manifest(cpd) ipu6_cpd_get_entry(cpd, MANIFEST_IDX)
|
||||
#define ipu6_cpd_get_metadata(cpd) ipu6_cpd_get_entry(cpd, METADATA_IDX)
|
||||
#define ipu6_cpd_get_moduledata(cpd) ipu6_cpd_get_entry(cpd, MODULEDATA_IDX)
|
||||
|
||||
static const struct ipu6_cpd_metadata_cmpnt_hdr *
|
||||
ipu6_cpd_metadata_get_cmpnt(struct ipu6_device *isp, const void *metadata,
|
||||
unsigned int metadata_size, u8 idx)
|
||||
{
|
||||
size_t extn_size = sizeof(struct ipu6_cpd_metadata_extn);
|
||||
size_t cmpnt_count = metadata_size - extn_size;
|
||||
|
||||
cmpnt_count = div_u64(cmpnt_count, isp->cpd_metadata_cmpnt_size);
|
||||
|
||||
if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) {
|
||||
dev_err(&isp->pdev->dev, "Component index out of range (%d)\n",
|
||||
idx);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return metadata + extn_size + idx * isp->cpd_metadata_cmpnt_size;
|
||||
}
|
||||
|
||||
static u32 ipu6_cpd_metadata_cmpnt_version(struct ipu6_device *isp,
|
||||
const void *metadata,
|
||||
unsigned int metadata_size, u8 idx)
|
||||
{
|
||||
const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
|
||||
|
||||
cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
|
||||
if (IS_ERR(cmpnt))
|
||||
return PTR_ERR(cmpnt);
|
||||
|
||||
return cmpnt->ver;
|
||||
}
|
||||
|
||||
static int ipu6_cpd_metadata_get_cmpnt_id(struct ipu6_device *isp,
|
||||
const void *metadata,
|
||||
unsigned int metadata_size, u8 idx)
|
||||
{
|
||||
const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
|
||||
|
||||
cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
|
||||
if (IS_ERR(cmpnt))
|
||||
return PTR_ERR(cmpnt);
|
||||
|
||||
return cmpnt->id;
|
||||
}
|
||||
|
||||
static int ipu6_cpd_parse_module_data(struct ipu6_device *isp,
|
||||
const void *module_data,
|
||||
unsigned int module_data_size,
|
||||
dma_addr_t dma_addr_module_data,
|
||||
u64 *pkg_dir, const void *metadata,
|
||||
unsigned int metadata_size)
|
||||
{
|
||||
const struct ipu6_cpd_module_data_hdr *module_data_hdr;
|
||||
const struct ipu6_cpd_hdr *dir_hdr;
|
||||
const struct ipu6_cpd_ent *dir_ent;
|
||||
unsigned int i;
|
||||
u8 len;
|
||||
|
||||
if (!module_data)
|
||||
return -EINVAL;
|
||||
|
||||
module_data_hdr = module_data;
|
||||
dir_hdr = module_data + module_data_hdr->hdr_len;
|
||||
len = dir_hdr->hdr_len;
|
||||
dir_ent = (const struct ipu6_cpd_ent *)(((u8 *)dir_hdr) + len);
|
||||
|
||||
pkg_dir[0] = PKG_DIR_HDR_MARK;
|
||||
/* pkg_dir entry count = component count + pkg_dir header */
|
||||
pkg_dir[1] = dir_hdr->ent_cnt + 1;
|
||||
|
||||
for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) {
|
||||
u64 *p = &pkg_dir[PKG_DIR_ENT_LEN * (1 + i)];
|
||||
int ver, id;
|
||||
|
||||
*p++ = dma_addr_module_data + dir_ent->offset;
|
||||
id = ipu6_cpd_metadata_get_cmpnt_id(isp, metadata,
|
||||
metadata_size, i);
|
||||
if (id < 0 || id > MAX_COMPONENT_ID) {
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD component id\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ver = ipu6_cpd_metadata_cmpnt_version(isp, metadata,
|
||||
metadata_size, i);
|
||||
if (ver < 0 || ver > MAX_COMPONENT_VERSION) {
|
||||
dev_err(&isp->pdev->dev,
|
||||
"Invalid CPD component version\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*p = FIELD_PREP(PKG_DIR_SIZE_MASK, dir_ent->len) |
|
||||
FIELD_PREP(PKG_DIR_TYPE_MASK, id) |
|
||||
FIELD_PREP(PKG_DIR_VERSION_MASK, ver);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src)
|
||||
{
|
||||
dma_addr_t dma_addr_src = sg_dma_address(adev->fw_sgt.sgl);
|
||||
const struct ipu6_cpd_ent *ent, *man_ent, *met_ent;
|
||||
struct device *dev = &adev->auxdev.dev;
|
||||
struct ipu6_device *isp = adev->isp;
|
||||
unsigned int man_sz, met_sz;
|
||||
void *pkg_dir_pos;
|
||||
int ret;
|
||||
|
||||
man_ent = ipu6_cpd_get_manifest(src);
|
||||
man_sz = man_ent->len;
|
||||
|
||||
met_ent = ipu6_cpd_get_metadata(src);
|
||||
met_sz = met_ent->len;
|
||||
|
||||
adev->pkg_dir_size = PKG_DIR_SIZE + man_sz + met_sz;
|
||||
adev->pkg_dir = dma_alloc_attrs(dev, adev->pkg_dir_size,
|
||||
&adev->pkg_dir_dma_addr, GFP_KERNEL, 0);
|
||||
if (!adev->pkg_dir)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* pkg_dir entry/header:
|
||||
* qword | 63:56 | 55 | 54:48 | 47:32 | 31:24 | 23:0
|
||||
* N Address/Offset/"_IUPKDR_"
|
||||
* N + 1 | rsvd | rsvd | type | ver | rsvd | size
|
||||
*
|
||||
* We can ignore other fields that size in N + 1 qword as they
|
||||
* are 0 anyway. Just setting size for now.
|
||||
*/
|
||||
|
||||
ent = ipu6_cpd_get_moduledata(src);
|
||||
|
||||
ret = ipu6_cpd_parse_module_data(isp, src + ent->offset,
|
||||
ent->len, dma_addr_src + ent->offset,
|
||||
adev->pkg_dir, src + met_ent->offset,
|
||||
met_ent->len);
|
||||
if (ret) {
|
||||
dev_err(&isp->pdev->dev, "Failed to parse module data\n");
|
||||
dma_free_attrs(dev, adev->pkg_dir_size,
|
||||
adev->pkg_dir, adev->pkg_dir_dma_addr, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Copy manifest after pkg_dir */
|
||||
pkg_dir_pos = adev->pkg_dir + PKG_DIR_ENT_LEN * MAX_PKG_DIR_ENT_CNT;
|
||||
memcpy(pkg_dir_pos, src + man_ent->offset, man_sz);
|
||||
|
||||
/* Copy metadata after manifest */
|
||||
pkg_dir_pos += man_sz;
|
||||
memcpy(pkg_dir_pos, src + met_ent->offset, met_sz);
|
||||
|
||||
dma_sync_single_range_for_device(dev, adev->pkg_dir_dma_addr,
|
||||
0, adev->pkg_dir_size, DMA_TO_DEVICE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6);
|
||||
|
||||
void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev)
|
||||
{
|
||||
dma_free_attrs(&adev->auxdev.dev, adev->pkg_dir_size, adev->pkg_dir,
|
||||
adev->pkg_dir_dma_addr, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6);
|
||||
|
||||
static int ipu6_cpd_validate_cpd(struct ipu6_device *isp, const void *cpd,
|
||||
unsigned long cpd_size,
|
||||
unsigned long data_size)
|
||||
{
|
||||
const struct ipu6_cpd_hdr *cpd_hdr = cpd;
|
||||
const struct ipu6_cpd_ent *ent;
|
||||
unsigned int i;
|
||||
u8 len;
|
||||
|
||||
len = cpd_hdr->hdr_len;
|
||||
|
||||
/* Ensure cpd hdr is within moduledata */
|
||||
if (cpd_size < len) {
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Sanity check for CPD header */
|
||||
if ((cpd_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) {
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Ensure that all entries are within moduledata */
|
||||
ent = (const struct ipu6_cpd_ent *)(((const u8 *)cpd_hdr) + len);
|
||||
for (i = 0; i < cpd_hdr->ent_cnt; i++, ent++) {
|
||||
if (data_size < ent->offset ||
|
||||
data_size - ent->offset < ent->len) {
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD entry (%d)\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp,
|
||||
const void *moduledata,
|
||||
u32 moduledata_size)
|
||||
{
|
||||
const struct ipu6_cpd_module_data_hdr *mod_hdr = moduledata;
|
||||
int ret;
|
||||
|
||||
/* Ensure moduledata hdr is within moduledata */
|
||||
if (moduledata_size < sizeof(*mod_hdr) ||
|
||||
moduledata_size < mod_hdr->hdr_len) {
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_info(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date);
|
||||
ret = ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len,
|
||||
moduledata_size - mod_hdr->hdr_len,
|
||||
moduledata_size);
|
||||
if (ret) {
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD in moduledata\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipu6_cpd_validate_metadata(struct ipu6_device *isp,
|
||||
const void *metadata, u32 meta_size)
|
||||
{
|
||||
const struct ipu6_cpd_metadata_extn *extn = metadata;
|
||||
|
||||
/* Sanity check for metadata size */
|
||||
if (meta_size < sizeof(*extn) || meta_size > MAX_METADATA_SIZE) {
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Validate extension and image types */
|
||||
if (extn->extn_type != IPU6_CPD_METADATA_EXTN_TYPE_IUNIT ||
|
||||
extn->img_type != IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE) {
|
||||
dev_err(&isp->pdev->dev,
|
||||
"Invalid CPD metadata descriptor img_type (%d)\n",
|
||||
extn->img_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Validate metadata size multiple of metadata components */
|
||||
if ((meta_size - sizeof(*extn)) % isp->cpd_metadata_cmpnt_size) {
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD metadata size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
|
||||
unsigned long cpd_file_size)
|
||||
{
|
||||
const struct ipu6_cpd_hdr *hdr = cpd_file;
|
||||
const struct ipu6_cpd_ent *ent;
|
||||
int ret;
|
||||
|
||||
ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size,
|
||||
cpd_file_size);
|
||||
if (ret) {
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD in file\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check for CPD file marker */
|
||||
if (hdr->hdr_mark != CPD_HDR_MARK) {
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Sanity check for manifest size */
|
||||
ent = ipu6_cpd_get_manifest(cpd_file);
|
||||
if (ent->len > MAX_MANIFEST_SIZE) {
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Validate metadata */
|
||||
ent = ipu6_cpd_get_metadata(cpd_file);
|
||||
ret = ipu6_cpd_validate_metadata(isp, cpd_file + ent->offset, ent->len);
|
||||
if (ret) {
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Validate moduledata */
|
||||
ent = ipu6_cpd_get_moduledata(cpd_file);
|
||||
ret = ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset,
|
||||
ent->len);
|
||||
if (ret)
|
||||
dev_err(&isp->pdev->dev, "Invalid CPD moduledata\n");
|
||||
|
||||
return ret;
|
||||
}
|
105
drivers/media/pci/intel/ipu6/ipu6-cpd.h
Normal file
105
drivers/media/pci/intel/ipu6/ipu6-cpd.h
Normal file
@ -0,0 +1,105 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (C) 2015--2024 Intel Corporation */
|
||||
|
||||
#ifndef IPU6_CPD_H
|
||||
#define IPU6_CPD_H
|
||||
|
||||
struct ipu6_device;
|
||||
struct ipu6_bus_device;
|
||||
|
||||
#define IPU6_CPD_SIZE_OF_FW_ARCH_VERSION 7
|
||||
#define IPU6_CPD_SIZE_OF_SYSTEM_VERSION 11
|
||||
#define IPU6_CPD_SIZE_OF_COMPONENT_NAME 12
|
||||
|
||||
#define IPU6_CPD_METADATA_EXTN_TYPE_IUNIT 0x10
|
||||
|
||||
#define IPU6_CPD_METADATA_IMAGE_TYPE_RESERVED 0
|
||||
#define IPU6_CPD_METADATA_IMAGE_TYPE_BOOTLOADER 1
|
||||
#define IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE 2
|
||||
|
||||
#define IPU6_CPD_PKG_DIR_PSYS_SERVER_IDX 0
|
||||
#define IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX 1
|
||||
|
||||
#define IPU6_CPD_PKG_DIR_CLIENT_PG_TYPE 3
|
||||
|
||||
#define IPU6_CPD_METADATA_HASH_KEY_SIZE 48
|
||||
#define IPU6SE_CPD_METADATA_HASH_KEY_SIZE 32
|
||||
|
||||
struct ipu6_cpd_module_data_hdr {
|
||||
u32 hdr_len;
|
||||
u32 endian;
|
||||
u32 fw_pkg_date;
|
||||
u32 hive_sdk_date;
|
||||
u32 compiler_date;
|
||||
u32 target_platform_type;
|
||||
u8 sys_ver[IPU6_CPD_SIZE_OF_SYSTEM_VERSION];
|
||||
u8 fw_arch_ver[IPU6_CPD_SIZE_OF_FW_ARCH_VERSION];
|
||||
u8 rsvd[2];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* ipu6_cpd_hdr structure updated as the chksum and
|
||||
* sub_partition_name is unused on host side
|
||||
* CSE layout version 1.6 for IPU6SE (hdr_len = 0x10)
|
||||
* CSE layout version 1.7 for IPU6 (hdr_len = 0x14)
|
||||
*/
|
||||
struct ipu6_cpd_hdr {
|
||||
u32 hdr_mark;
|
||||
u32 ent_cnt;
|
||||
u8 hdr_ver;
|
||||
u8 ent_ver;
|
||||
u8 hdr_len;
|
||||
} __packed;
|
||||
|
||||
struct ipu6_cpd_ent {
|
||||
u8 name[IPU6_CPD_SIZE_OF_COMPONENT_NAME];
|
||||
u32 offset;
|
||||
u32 len;
|
||||
u8 rsvd[4];
|
||||
} __packed;
|
||||
|
||||
struct ipu6_cpd_metadata_cmpnt_hdr {
|
||||
u32 id;
|
||||
u32 size;
|
||||
u32 ver;
|
||||
} __packed;
|
||||
|
||||
struct ipu6_cpd_metadata_cmpnt {
|
||||
struct ipu6_cpd_metadata_cmpnt_hdr hdr;
|
||||
u8 sha2_hash[IPU6_CPD_METADATA_HASH_KEY_SIZE];
|
||||
u32 entry_point;
|
||||
u32 icache_base_offs;
|
||||
u8 attrs[16];
|
||||
} __packed;
|
||||
|
||||
struct ipu6se_cpd_metadata_cmpnt {
|
||||
struct ipu6_cpd_metadata_cmpnt_hdr hdr;
|
||||
u8 sha2_hash[IPU6SE_CPD_METADATA_HASH_KEY_SIZE];
|
||||
u32 entry_point;
|
||||
u32 icache_base_offs;
|
||||
u8 attrs[16];
|
||||
} __packed;
|
||||
|
||||
struct ipu6_cpd_metadata_extn {
|
||||
u32 extn_type;
|
||||
u32 len;
|
||||
u32 img_type;
|
||||
u8 rsvd[16];
|
||||
} __packed;
|
||||
|
||||
struct ipu6_cpd_client_pkg_hdr {
|
||||
u32 prog_list_offs;
|
||||
u32 prog_list_size;
|
||||
u32 prog_desc_offs;
|
||||
u32 prog_desc_size;
|
||||
u32 pg_manifest_offs;
|
||||
u32 pg_manifest_size;
|
||||
u32 prog_bin_offs;
|
||||
u32 prog_bin_size;
|
||||
} __packed;
|
||||
|
||||
int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src);
|
||||
void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev);
|
||||
int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
|
||||
unsigned long cpd_file_size);
|
||||
#endif /* IPU6_CPD_H */
|
502
drivers/media/pci/intel/ipu6/ipu6-dma.c
Normal file
502
drivers/media/pci/intel/ipu6/ipu6-dma.c
Normal file
@ -0,0 +1,502 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2013--2024 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/cacheflush.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/iova.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "ipu6.h"
|
||||
#include "ipu6-bus.h"
|
||||
#include "ipu6-dma.h"
|
||||
#include "ipu6-mmu.h"
|
||||
|
||||
struct vm_info {
|
||||
struct list_head list;
|
||||
struct page **pages;
|
||||
dma_addr_t ipu6_iova;
|
||||
void *vaddr;
|
||||
unsigned long size;
|
||||
};
|
||||
|
||||
static struct vm_info *get_vm_info(struct ipu6_mmu *mmu, dma_addr_t iova)
|
||||
{
|
||||
struct vm_info *info, *save;
|
||||
|
||||
list_for_each_entry_safe(info, save, &mmu->vma_list, list) {
|
||||
if (iova >= info->ipu6_iova &&
|
||||
iova < (info->ipu6_iova + info->size))
|
||||
return info;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void __dma_clear_buffer(struct page *page, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
if (!page)
|
||||
return;
|
||||
/*
|
||||
* Ensure that the allocated pages are zeroed, and that any data
|
||||
* lurking in the kernel direct-mapped region is invalidated.
|
||||
*/
|
||||
ptr = page_address(page);
|
||||
memset(ptr, 0, size);
|
||||
if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
|
||||
clflush_cache_range(ptr, size);
|
||||
}
|
||||
|
||||
static struct page **__dma_alloc_buffer(struct device *dev, size_t size,
|
||||
gfp_t gfp, unsigned long attrs)
|
||||
{
|
||||
int count = PHYS_PFN(size);
|
||||
int array_size = count * sizeof(struct page *);
|
||||
struct page **pages;
|
||||
int i = 0;
|
||||
|
||||
pages = kvzalloc(array_size, GFP_KERNEL);
|
||||
if (!pages)
|
||||
return NULL;
|
||||
|
||||
gfp |= __GFP_NOWARN;
|
||||
|
||||
while (count) {
|
||||
int j, order = __fls(count);
|
||||
|
||||
pages[i] = alloc_pages(gfp, order);
|
||||
while (!pages[i] && order)
|
||||
pages[i] = alloc_pages(gfp, --order);
|
||||
if (!pages[i])
|
||||
goto error;
|
||||
|
||||
if (order) {
|
||||
split_page(pages[i], order);
|
||||
j = 1 << order;
|
||||
while (j--)
|
||||
pages[i + j] = pages[i] + j;
|
||||
}
|
||||
|
||||
__dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs);
|
||||
i += 1 << order;
|
||||
count -= 1 << order;
|
||||
}
|
||||
|
||||
return pages;
|
||||
error:
|
||||
while (i--)
|
||||
if (pages[i])
|
||||
__free_pages(pages[i], 0);
|
||||
kvfree(pages);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void __dma_free_buffer(struct device *dev, struct page **pages,
|
||||
size_t size, unsigned long attrs)
|
||||
{
|
||||
int count = PHYS_PFN(size);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < count && pages[i]; i++) {
|
||||
__dma_clear_buffer(pages[i], PAGE_SIZE, attrs);
|
||||
__free_pages(pages[i], 0);
|
||||
}
|
||||
|
||||
kvfree(pages);
|
||||
}
|
||||
|
||||
static void ipu6_dma_sync_single_for_cpu(struct device *dev,
|
||||
dma_addr_t dma_handle,
|
||||
size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
void *vaddr;
|
||||
u32 offset;
|
||||
struct vm_info *info;
|
||||
struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
|
||||
|
||||
info = get_vm_info(mmu, dma_handle);
|
||||
if (WARN_ON(!info))
|
||||
return;
|
||||
|
||||
offset = dma_handle - info->ipu6_iova;
|
||||
if (WARN_ON(size > (info->size - offset)))
|
||||
return;
|
||||
|
||||
vaddr = info->vaddr + offset;
|
||||
clflush_cache_range(vaddr, size);
|
||||
}
|
||||
|
||||
static void ipu6_dma_sync_sg_for_cpu(struct device *dev,
|
||||
struct scatterlist *sglist,
|
||||
int nents, enum dma_data_direction dir)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
for_each_sg(sglist, sg, nents, i)
|
||||
clflush_cache_range(page_to_virt(sg_page(sg)), sg->length);
|
||||
}
|
||||
|
||||
static void *ipu6_dma_alloc(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t gfp,
|
||||
unsigned long attrs)
|
||||
{
|
||||
struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
|
||||
struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
|
||||
dma_addr_t pci_dma_addr, ipu6_iova;
|
||||
struct vm_info *info;
|
||||
unsigned long count;
|
||||
struct page **pages;
|
||||
struct iova *iova;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
count = PHYS_PFN(size);
|
||||
|
||||
iova = alloc_iova(&mmu->dmap->iovad, count,
|
||||
PHYS_PFN(dma_get_mask(dev)), 0);
|
||||
if (!iova)
|
||||
goto out_kfree;
|
||||
|
||||
pages = __dma_alloc_buffer(dev, size, gfp, attrs);
|
||||
if (!pages)
|
||||
goto out_free_iova;
|
||||
|
||||
dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n",
|
||||
size, iova->pfn_lo, iova->pfn_hi);
|
||||
for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) {
|
||||
pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0,
|
||||
PAGE_SIZE, DMA_BIDIRECTIONAL,
|
||||
attrs);
|
||||
dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n",
|
||||
&pci_dma_addr);
|
||||
if (dma_mapping_error(&pdev->dev, pci_dma_addr)) {
|
||||
dev_err(dev, "pci_dma_mapping for page[%d] failed", i);
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
ret = ipu6_mmu_map(mmu->dmap->mmu_info,
|
||||
PFN_PHYS(iova->pfn_lo + i), pci_dma_addr,
|
||||
PAGE_SIZE);
|
||||
if (ret) {
|
||||
dev_err(dev, "ipu6_mmu_map for pci_dma[%d] %pad failed",
|
||||
i, &pci_dma_addr);
|
||||
dma_unmap_page_attrs(&pdev->dev, pci_dma_addr,
|
||||
PAGE_SIZE, DMA_BIDIRECTIONAL,
|
||||
attrs);
|
||||
goto out_unmap;
|
||||
}
|
||||
}
|
||||
|
||||
info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL);
|
||||
if (!info->vaddr)
|
||||
goto out_unmap;
|
||||
|
||||
*dma_handle = PFN_PHYS(iova->pfn_lo);
|
||||
|
||||
info->pages = pages;
|
||||
info->ipu6_iova = *dma_handle;
|
||||
info->size = size;
|
||||
list_add(&info->list, &mmu->vma_list);
|
||||
|
||||
return info->vaddr;
|
||||
|
||||
out_unmap:
|
||||
while (i--) {
|
||||
ipu6_iova = PFN_PHYS(iova->pfn_lo + i);
|
||||
pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
|
||||
ipu6_iova);
|
||||
dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE,
|
||||
DMA_BIDIRECTIONAL, attrs);
|
||||
|
||||
ipu6_mmu_unmap(mmu->dmap->mmu_info, ipu6_iova, PAGE_SIZE);
|
||||
}
|
||||
|
||||
__dma_free_buffer(dev, pages, size, attrs);
|
||||
|
||||
out_free_iova:
|
||||
__free_iova(&mmu->dmap->iovad, iova);
|
||||
out_kfree:
|
||||
kfree(info);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ipu6_dma_free(struct device *dev, size_t size, void *vaddr,
|
||||
dma_addr_t dma_handle,
|
||||
unsigned long attrs)
|
||||
{
|
||||
struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
|
||||
struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
|
||||
struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle));
|
||||
dma_addr_t pci_dma_addr, ipu6_iova;
|
||||
struct vm_info *info;
|
||||
struct page **pages;
|
||||
unsigned int i;
|
||||
|
||||
if (WARN_ON(!iova))
|
||||
return;
|
||||
|
||||
info = get_vm_info(mmu, dma_handle);
|
||||
if (WARN_ON(!info))
|
||||
return;
|
||||
|
||||
if (WARN_ON(!info->vaddr))
|
||||
return;
|
||||
|
||||
if (WARN_ON(!info->pages))
|
||||
return;
|
||||
|
||||
list_del(&info->list);
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
pages = info->pages;
|
||||
|
||||
vunmap(vaddr);
|
||||
|
||||
for (i = 0; i < PHYS_PFN(size); i++) {
|
||||
ipu6_iova = PFN_PHYS(iova->pfn_lo + i);
|
||||
pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
|
||||
ipu6_iova);
|
||||
dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE,
|
||||
DMA_BIDIRECTIONAL, attrs);
|
||||
}
|
||||
|
||||
ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
|
||||
PFN_PHYS(iova_size(iova)));
|
||||
|
||||
__dma_free_buffer(dev, pages, size, attrs);
|
||||
|
||||
mmu->tlb_invalidate(mmu);
|
||||
|
||||
__free_iova(&mmu->dmap->iovad, iova);
|
||||
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
static int ipu6_dma_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
void *addr, dma_addr_t iova, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
|
||||
size_t count = PHYS_PFN(PAGE_ALIGN(size));
|
||||
struct vm_info *info;
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
info = get_vm_info(mmu, iova);
|
||||
if (!info)
|
||||
return -EFAULT;
|
||||
|
||||
if (!info->vaddr)
|
||||
return -EFAULT;
|
||||
|
||||
if (vma->vm_start & ~PAGE_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
if (size > info->size)
|
||||
return -EFAULT;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i),
|
||||
info->pages[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipu6_dma_unmap_sg(struct device *dev,
|
||||
struct scatterlist *sglist,
|
||||
int nents, enum dma_data_direction dir,
|
||||
unsigned long attrs)
|
||||
{
|
||||
struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
|
||||
struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
|
||||
struct iova *iova = find_iova(&mmu->dmap->iovad,
|
||||
PHYS_PFN(sg_dma_address(sglist)));
|
||||
int i, npages, count;
|
||||
struct scatterlist *sg;
|
||||
dma_addr_t pci_dma_addr;
|
||||
|
||||
if (!nents)
|
||||
return;
|
||||
|
||||
if (WARN_ON(!iova))
|
||||
return;
|
||||
|
||||
if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
|
||||
ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL);
|
||||
|
||||
/* get the nents as orig_nents given by caller */
|
||||
count = 0;
|
||||
npages = iova_size(iova);
|
||||
for_each_sg(sglist, sg, nents, i) {
|
||||
if (sg_dma_len(sg) == 0 ||
|
||||
sg_dma_address(sg) == DMA_MAPPING_ERROR)
|
||||
break;
|
||||
|
||||
npages -= PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
|
||||
count++;
|
||||
if (npages <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Before IPU6 mmu unmap, return the pci dma address back to sg
|
||||
* assume the nents is less than orig_nents as the least granule
|
||||
* is 1 SZ_4K page
|
||||
*/
|
||||
dev_dbg(dev, "trying to unmap concatenated %u ents\n", count);
|
||||
for_each_sg(sglist, sg, count, i) {
|
||||
dev_dbg(dev, "ipu unmap sg[%d] %pad\n", i, &sg_dma_address(sg));
|
||||
pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
|
||||
sg_dma_address(sg));
|
||||
dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n",
|
||||
&pci_dma_addr, i);
|
||||
sg_dma_address(sg) = pci_dma_addr;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "ipu6_mmu_unmap low pfn %lu high pfn %lu\n",
|
||||
iova->pfn_lo, iova->pfn_hi);
|
||||
ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
|
||||
PFN_PHYS(iova_size(iova)));
|
||||
|
||||
mmu->tlb_invalidate(mmu);
|
||||
|
||||
dma_unmap_sg_attrs(&pdev->dev, sglist, nents, dir, attrs);
|
||||
|
||||
__free_iova(&mmu->dmap->iovad, iova);
|
||||
}
|
||||
|
||||
static int ipu6_dma_map_sg(struct device *dev, struct scatterlist *sglist,
|
||||
int nents, enum dma_data_direction dir,
|
||||
unsigned long attrs)
|
||||
{
|
||||
struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
|
||||
struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
|
||||
struct scatterlist *sg;
|
||||
struct iova *iova;
|
||||
size_t npages = 0;
|
||||
unsigned long iova_addr;
|
||||
int i, count;
|
||||
|
||||
for_each_sg(sglist, sg, nents, i) {
|
||||
if (sg->offset) {
|
||||
dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n",
|
||||
i, sg->offset);
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(dev, "pci_dma_map_sg trying to map %d ents\n", nents);
|
||||
count = dma_map_sg_attrs(&pdev->dev, sglist, nents, dir, attrs);
|
||||
if (count <= 0) {
|
||||
dev_err(dev, "pci_dma_map_sg %d ents failed\n", nents);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "pci_dma_map_sg %d ents mapped\n", count);
|
||||
|
||||
for_each_sg(sglist, sg, count, i)
|
||||
npages += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
|
||||
|
||||
iova = alloc_iova(&mmu->dmap->iovad, npages,
|
||||
PHYS_PFN(dma_get_mask(dev)), 0);
|
||||
if (!iova)
|
||||
return 0;
|
||||
|
||||
dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo,
|
||||
iova->pfn_hi);
|
||||
|
||||
iova_addr = iova->pfn_lo;
|
||||
for_each_sg(sglist, sg, count, i) {
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "mapping entry %d: iova 0x%llx phy %pad size %d\n",
|
||||
i, PFN_PHYS(iova_addr), &sg_dma_address(sg),
|
||||
sg_dma_len(sg));
|
||||
|
||||
ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr),
|
||||
sg_dma_address(sg),
|
||||
PAGE_ALIGN(sg_dma_len(sg)));
|
||||
if (ret)
|
||||
goto out_fail;
|
||||
|
||||
sg_dma_address(sg) = PFN_PHYS(iova_addr);
|
||||
|
||||
iova_addr += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
|
||||
}
|
||||
|
||||
if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
|
||||
ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL);
|
||||
|
||||
return count;
|
||||
|
||||
out_fail:
|
||||
ipu6_dma_unmap_sg(dev, sglist, i, dir, attrs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create scatter-list for the already allocated DMA buffer
|
||||
*/
|
||||
static int ipu6_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
|
||||
void *cpu_addr, dma_addr_t handle, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
|
||||
struct vm_info *info;
|
||||
int n_pages;
|
||||
int ret = 0;
|
||||
|
||||
info = get_vm_info(mmu, handle);
|
||||
if (!info)
|
||||
return -EFAULT;
|
||||
|
||||
if (!info->vaddr)
|
||||
return -EFAULT;
|
||||
|
||||
if (WARN_ON(!info->pages))
|
||||
return -ENOMEM;
|
||||
|
||||
n_pages = PHYS_PFN(PAGE_ALIGN(size));
|
||||
|
||||
ret = sg_alloc_table_from_pages(sgt, info->pages, n_pages, 0, size,
|
||||
GFP_KERNEL);
|
||||
if (ret)
|
||||
dev_warn(dev, "IPU6 get sgt table failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct dma_map_ops ipu6_dma_ops = {
|
||||
.alloc = ipu6_dma_alloc,
|
||||
.free = ipu6_dma_free,
|
||||
.mmap = ipu6_dma_mmap,
|
||||
.map_sg = ipu6_dma_map_sg,
|
||||
.unmap_sg = ipu6_dma_unmap_sg,
|
||||
.sync_single_for_cpu = ipu6_dma_sync_single_for_cpu,
|
||||
.sync_single_for_device = ipu6_dma_sync_single_for_cpu,
|
||||
.sync_sg_for_cpu = ipu6_dma_sync_sg_for_cpu,
|
||||
.sync_sg_for_device = ipu6_dma_sync_sg_for_cpu,
|
||||
.get_sgtable = ipu6_dma_get_sgtable,
|
||||
};
|
19
drivers/media/pci/intel/ipu6/ipu6-dma.h
Normal file
19
drivers/media/pci/intel/ipu6/ipu6-dma.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (C) 2013--2024 Intel Corporation */
|
||||
|
||||
#ifndef IPU6_DMA_H
|
||||
#define IPU6_DMA_H
|
||||
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/iova.h>
|
||||
|
||||
struct ipu6_mmu_info;
|
||||
|
||||
struct ipu6_dma_mapping {
|
||||
struct ipu6_mmu_info *mmu_info;
|
||||
struct iova_domain iovad;
|
||||
};
|
||||
|
||||
extern const struct dma_map_ops ipu6_dma_ops;
|
||||
|
||||
#endif /* IPU6_DMA_H */
|
413
drivers/media/pci/intel/ipu6/ipu6-fw-com.c
Normal file
413
drivers/media/pci/intel/ipu6/ipu6-fw-com.c
Normal file
@ -0,0 +1,413 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2013--2024 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "ipu6-bus.h"
|
||||
#include "ipu6-fw-com.h"
|
||||
|
||||
/*
|
||||
* FWCOM layer is a shared resource between FW and driver. It consist
|
||||
* of token queues to both send and receive directions. Queue is simply
|
||||
* an array of structures with read and write indexes to the queue.
|
||||
* There are 1...n queues to both directions. Queues locates in
|
||||
* system RAM and are mapped to ISP MMU so that both CPU and ISP can
|
||||
* see the same buffer. Indexes are located in ISP DMEM so that FW code
|
||||
* can poll those with very low latency and cost. CPU access to indexes is
|
||||
* more costly but that happens only at message sending time and
|
||||
* interrupt triggered message handling. CPU doesn't need to poll indexes.
|
||||
* wr_reg / rd_reg are offsets to those dmem location. They are not
|
||||
* the indexes itself.
|
||||
*/
|
||||
|
||||
/* Shared structure between driver and FW - do not modify */
|
||||
struct ipu6_fw_sys_queue {
|
||||
u64 host_address;
|
||||
u32 vied_address;
|
||||
u32 size;
|
||||
u32 token_size;
|
||||
u32 wr_reg; /* reg number in subsystem's regmem */
|
||||
u32 rd_reg;
|
||||
u32 _align;
|
||||
} __packed;
|
||||
|
||||
struct ipu6_fw_sys_queue_res {
|
||||
u64 host_address;
|
||||
u32 vied_address;
|
||||
u32 reg;
|
||||
} __packed;
|
||||
|
||||
enum syscom_state {
|
||||
/* Program load or explicit host setting should init to this */
|
||||
SYSCOM_STATE_UNINIT = 0x57a7e000,
|
||||
/* SP Syscom sets this when it is ready for use */
|
||||
SYSCOM_STATE_READY = 0x57a7e001,
|
||||
/* SP Syscom sets this when no more syscom accesses will happen */
|
||||
SYSCOM_STATE_INACTIVE = 0x57a7e002,
|
||||
};
|
||||
|
||||
enum syscom_cmd {
|
||||
/* Program load or explicit host setting should init to this */
|
||||
SYSCOM_COMMAND_UNINIT = 0x57a7f000,
|
||||
/* Host Syscom requests syscom to become inactive */
|
||||
SYSCOM_COMMAND_INACTIVE = 0x57a7f001,
|
||||
};
|
||||
|
||||
/* firmware config: data that sent from the host to SP via DDR */
|
||||
/* Cell copies data into a context */
|
||||
|
||||
struct ipu6_fw_syscom_config {
|
||||
u32 firmware_address;
|
||||
|
||||
u32 num_input_queues;
|
||||
u32 num_output_queues;
|
||||
|
||||
/* ISP pointers to an array of ipu6_fw_sys_queue structures */
|
||||
u32 input_queue;
|
||||
u32 output_queue;
|
||||
|
||||
/* ISYS / PSYS private data */
|
||||
u32 specific_addr;
|
||||
u32 specific_size;
|
||||
};
|
||||
|
||||
struct ipu6_fw_com_context {
|
||||
struct ipu6_bus_device *adev;
|
||||
void __iomem *dmem_addr;
|
||||
int (*cell_ready)(struct ipu6_bus_device *adev);
|
||||
void (*cell_start)(struct ipu6_bus_device *adev);
|
||||
|
||||
void *dma_buffer;
|
||||
dma_addr_t dma_addr;
|
||||
unsigned int dma_size;
|
||||
unsigned long attrs;
|
||||
|
||||
struct ipu6_fw_sys_queue *input_queue; /* array of host to SP queues */
|
||||
struct ipu6_fw_sys_queue *output_queue; /* array of SP to host */
|
||||
|
||||
u32 config_vied_addr;
|
||||
|
||||
unsigned int buttress_boot_offset;
|
||||
void __iomem *base_addr;
|
||||
};
|
||||
|
||||
#define FW_COM_WR_REG 0
|
||||
#define FW_COM_RD_REG 4
|
||||
|
||||
#define REGMEM_OFFSET 0
|
||||
#define TUNIT_MAGIC_PATTERN 0x5a5a5a5a
|
||||
|
||||
enum regmem_id {
|
||||
/* pass pkg_dir address to SPC in non-secure mode */
|
||||
PKG_DIR_ADDR_REG = 0,
|
||||
/* Tunit CFG blob for secure - provided by host.*/
|
||||
TUNIT_CFG_DWR_REG = 1,
|
||||
/* syscom commands - modified by the host */
|
||||
SYSCOM_COMMAND_REG = 2,
|
||||
/* Store interrupt status - updated by SP */
|
||||
SYSCOM_IRQ_REG = 3,
|
||||
/* first syscom queue pointer register */
|
||||
SYSCOM_QPR_BASE_REG = 4
|
||||
};
|
||||
|
||||
#define BUTTRESS_FW_BOOT_PARAMS_0 0x4000
|
||||
#define BUTTRESS_FW_BOOT_PARAM_REG(base, offset, id) \
|
||||
((base) + BUTTRESS_FW_BOOT_PARAMS_0 + ((offset) + (id)) * 4)
|
||||
|
||||
enum buttress_syscom_id {
|
||||
/* pass syscom configuration to SPC */
|
||||
SYSCOM_CONFIG_ID = 0,
|
||||
/* syscom state - modified by SP */
|
||||
SYSCOM_STATE_ID = 1,
|
||||
/* syscom vtl0 addr mask */
|
||||
SYSCOM_VTL0_ADDR_MASK_ID = 2,
|
||||
SYSCOM_ID_MAX
|
||||
};
|
||||
|
||||
static void ipu6_sys_queue_init(struct ipu6_fw_sys_queue *q, unsigned int size,
|
||||
unsigned int token_size,
|
||||
struct ipu6_fw_sys_queue_res *res)
|
||||
{
|
||||
unsigned int buf_size = (size + 1) * token_size;
|
||||
|
||||
q->size = size + 1;
|
||||
q->token_size = token_size;
|
||||
|
||||
/* acquire the shared buffer space */
|
||||
q->host_address = res->host_address;
|
||||
res->host_address += buf_size;
|
||||
q->vied_address = res->vied_address;
|
||||
res->vied_address += buf_size;
|
||||
|
||||
/* acquire the shared read and writer pointers */
|
||||
q->wr_reg = res->reg;
|
||||
res->reg++;
|
||||
q->rd_reg = res->reg;
|
||||
res->reg++;
|
||||
}
|
||||
|
||||
void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg,
|
||||
struct ipu6_bus_device *adev, void __iomem *base)
|
||||
{
|
||||
size_t conf_size, inq_size, outq_size, specific_size;
|
||||
struct ipu6_fw_syscom_config *config_host_addr;
|
||||
unsigned int sizeinput = 0, sizeoutput = 0;
|
||||
struct ipu6_fw_sys_queue_res res;
|
||||
struct ipu6_fw_com_context *ctx;
|
||||
struct device *dev = &adev->auxdev.dev;
|
||||
size_t sizeall, offset;
|
||||
unsigned long attrs = 0;
|
||||
void *specific_host_addr;
|
||||
unsigned int i;
|
||||
|
||||
if (!cfg || !cfg->cell_start || !cfg->cell_ready)
|
||||
return NULL;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
ctx->dmem_addr = base + cfg->dmem_addr + REGMEM_OFFSET;
|
||||
ctx->adev = adev;
|
||||
ctx->cell_start = cfg->cell_start;
|
||||
ctx->cell_ready = cfg->cell_ready;
|
||||
ctx->buttress_boot_offset = cfg->buttress_boot_offset;
|
||||
ctx->base_addr = base;
|
||||
|
||||
/*
|
||||
* Allocate DMA mapped memory. Allocate one big chunk.
|
||||
*/
|
||||
/* Base cfg for FW */
|
||||
conf_size = roundup(sizeof(struct ipu6_fw_syscom_config), 8);
|
||||
/* Descriptions of the queues */
|
||||
inq_size = size_mul(cfg->num_input_queues,
|
||||
sizeof(struct ipu6_fw_sys_queue));
|
||||
outq_size = size_mul(cfg->num_output_queues,
|
||||
sizeof(struct ipu6_fw_sys_queue));
|
||||
/* FW specific information structure */
|
||||
specific_size = roundup(cfg->specific_size, 8);
|
||||
|
||||
sizeall = conf_size + inq_size + outq_size + specific_size;
|
||||
|
||||
for (i = 0; i < cfg->num_input_queues; i++)
|
||||
sizeinput += size_mul(cfg->input[i].queue_size + 1,
|
||||
cfg->input[i].token_size);
|
||||
|
||||
for (i = 0; i < cfg->num_output_queues; i++)
|
||||
sizeoutput += size_mul(cfg->output[i].queue_size + 1,
|
||||
cfg->output[i].token_size);
|
||||
|
||||
sizeall += sizeinput + sizeoutput;
|
||||
|
||||
ctx->dma_buffer = dma_alloc_attrs(dev, sizeall, &ctx->dma_addr,
|
||||
GFP_KERNEL, attrs);
|
||||
ctx->attrs = attrs;
|
||||
if (!ctx->dma_buffer) {
|
||||
dev_err(dev, "failed to allocate dma memory\n");
|
||||
kfree(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->dma_size = sizeall;
|
||||
|
||||
config_host_addr = ctx->dma_buffer;
|
||||
ctx->config_vied_addr = ctx->dma_addr;
|
||||
|
||||
offset = conf_size;
|
||||
ctx->input_queue = ctx->dma_buffer + offset;
|
||||
config_host_addr->input_queue = ctx->dma_addr + offset;
|
||||
config_host_addr->num_input_queues = cfg->num_input_queues;
|
||||
|
||||
offset += inq_size;
|
||||
ctx->output_queue = ctx->dma_buffer + offset;
|
||||
config_host_addr->output_queue = ctx->dma_addr + offset;
|
||||
config_host_addr->num_output_queues = cfg->num_output_queues;
|
||||
|
||||
/* copy firmware specific data */
|
||||
offset += outq_size;
|
||||
specific_host_addr = ctx->dma_buffer + offset;
|
||||
config_host_addr->specific_addr = ctx->dma_addr + offset;
|
||||
config_host_addr->specific_size = cfg->specific_size;
|
||||
if (cfg->specific_addr && cfg->specific_size)
|
||||
memcpy(specific_host_addr, cfg->specific_addr,
|
||||
cfg->specific_size);
|
||||
|
||||
/* initialize input queues */
|
||||
offset += specific_size;
|
||||
res.reg = SYSCOM_QPR_BASE_REG;
|
||||
res.host_address = (u64)(ctx->dma_buffer + offset);
|
||||
res.vied_address = ctx->dma_addr + offset;
|
||||
for (i = 0; i < cfg->num_input_queues; i++)
|
||||
ipu6_sys_queue_init(ctx->input_queue + i,
|
||||
cfg->input[i].queue_size,
|
||||
cfg->input[i].token_size, &res);
|
||||
|
||||
/* initialize output queues */
|
||||
offset += sizeinput;
|
||||
res.host_address = (u64)(ctx->dma_buffer + offset);
|
||||
res.vied_address = ctx->dma_addr + offset;
|
||||
for (i = 0; i < cfg->num_output_queues; i++) {
|
||||
ipu6_sys_queue_init(ctx->output_queue + i,
|
||||
cfg->output[i].queue_size,
|
||||
cfg->output[i].token_size, &res);
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_prepare, INTEL_IPU6);
|
||||
|
||||
int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx)
|
||||
{
|
||||
/* write magic pattern to disable the tunit trace */
|
||||
writel(TUNIT_MAGIC_PATTERN, ctx->dmem_addr + TUNIT_CFG_DWR_REG * 4);
|
||||
/* Check if SP is in valid state */
|
||||
if (!ctx->cell_ready(ctx->adev))
|
||||
return -EIO;
|
||||
|
||||
/* store syscom uninitialized command */
|
||||
writel(SYSCOM_COMMAND_UNINIT, ctx->dmem_addr + SYSCOM_COMMAND_REG * 4);
|
||||
|
||||
/* store syscom uninitialized state */
|
||||
writel(SYSCOM_STATE_UNINIT,
|
||||
BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
|
||||
ctx->buttress_boot_offset,
|
||||
SYSCOM_STATE_ID));
|
||||
|
||||
/* store firmware configuration address */
|
||||
writel(ctx->config_vied_addr,
|
||||
BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
|
||||
ctx->buttress_boot_offset,
|
||||
SYSCOM_CONFIG_ID));
|
||||
ctx->cell_start(ctx->adev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_open, INTEL_IPU6);
|
||||
|
||||
int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx)
|
||||
{
|
||||
int state;
|
||||
|
||||
state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
|
||||
ctx->buttress_boot_offset,
|
||||
SYSCOM_STATE_ID));
|
||||
if (state != SYSCOM_STATE_READY)
|
||||
return -EBUSY;
|
||||
|
||||
/* set close request flag */
|
||||
writel(SYSCOM_COMMAND_INACTIVE, ctx->dmem_addr +
|
||||
SYSCOM_COMMAND_REG * 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_close, INTEL_IPU6);
|
||||
|
||||
int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force)
|
||||
{
|
||||
/* check if release is forced, an verify cell state if it is not */
|
||||
if (!force && !ctx->cell_ready(ctx->adev))
|
||||
return -EBUSY;
|
||||
|
||||
dma_free_attrs(&ctx->adev->auxdev.dev, ctx->dma_size,
|
||||
ctx->dma_buffer, ctx->dma_addr, ctx->attrs);
|
||||
kfree(ctx);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_release, INTEL_IPU6);
|
||||
|
||||
bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx)
|
||||
{
|
||||
int state;
|
||||
|
||||
state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
|
||||
ctx->buttress_boot_offset,
|
||||
SYSCOM_STATE_ID));
|
||||
|
||||
return state == SYSCOM_STATE_READY;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_ready, INTEL_IPU6);
|
||||
|
||||
void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr)
|
||||
{
|
||||
struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr];
|
||||
void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
|
||||
unsigned int wr, rd;
|
||||
unsigned int packets;
|
||||
unsigned int index;
|
||||
|
||||
wr = readl(q_dmem + FW_COM_WR_REG);
|
||||
rd = readl(q_dmem + FW_COM_RD_REG);
|
||||
|
||||
if (WARN_ON_ONCE(wr >= q->size || rd >= q->size))
|
||||
return NULL;
|
||||
|
||||
if (wr < rd)
|
||||
packets = rd - wr - 1;
|
||||
else
|
||||
packets = q->size - (wr - rd + 1);
|
||||
|
||||
if (!packets)
|
||||
return NULL;
|
||||
|
||||
index = readl(q_dmem + FW_COM_WR_REG);
|
||||
|
||||
return (void *)(q->host_address + index * q->token_size);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_send_get_token, INTEL_IPU6);
|
||||
|
||||
void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr)
|
||||
{
|
||||
struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr];
|
||||
void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
|
||||
unsigned int wr = readl(q_dmem + FW_COM_WR_REG) + 1;
|
||||
|
||||
if (wr >= q->size)
|
||||
wr = 0;
|
||||
|
||||
writel(wr, q_dmem + FW_COM_WR_REG);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_send_put_token, INTEL_IPU6);
|
||||
|
||||
void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr)
|
||||
{
|
||||
struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr];
|
||||
void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
|
||||
unsigned int wr, rd;
|
||||
unsigned int packets;
|
||||
|
||||
wr = readl(q_dmem + FW_COM_WR_REG);
|
||||
rd = readl(q_dmem + FW_COM_RD_REG);
|
||||
|
||||
if (WARN_ON_ONCE(wr >= q->size || rd >= q->size))
|
||||
return NULL;
|
||||
|
||||
if (wr < rd)
|
||||
wr += q->size;
|
||||
|
||||
packets = wr - rd;
|
||||
if (!packets)
|
||||
return NULL;
|
||||
|
||||
return (void *)(q->host_address + rd * q->token_size);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_recv_get_token, INTEL_IPU6);
|
||||
|
||||
void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr)
|
||||
{
|
||||
struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr];
|
||||
void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
|
||||
unsigned int rd = readl(q_dmem + FW_COM_RD_REG) + 1;
|
||||
|
||||
if (rd >= q->size)
|
||||
rd = 0;
|
||||
|
||||
writel(rd, q_dmem + FW_COM_RD_REG);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ipu6_recv_put_token, INTEL_IPU6);
|
47
drivers/media/pci/intel/ipu6/ipu6-fw-com.h
Normal file
47
drivers/media/pci/intel/ipu6/ipu6-fw-com.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (C) 2013--2024 Intel Corporation */
|
||||
|
||||
#ifndef IPU6_FW_COM_H
|
||||
#define IPU6_FW_COM_H
|
||||
|
||||
struct ipu6_fw_com_context;
|
||||
struct ipu6_bus_device;
|
||||
|
||||
struct ipu6_fw_syscom_queue_config {
|
||||
unsigned int queue_size; /* tokens per queue */
|
||||
unsigned int token_size; /* bytes per token */
|
||||
};
|
||||
|
||||
#define SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET 0
|
||||
|
||||
struct ipu6_fw_com_cfg {
|
||||
unsigned int num_input_queues;
|
||||
unsigned int num_output_queues;
|
||||
struct ipu6_fw_syscom_queue_config *input;
|
||||
struct ipu6_fw_syscom_queue_config *output;
|
||||
|
||||
unsigned int dmem_addr;
|
||||
|
||||
/* firmware-specific configuration data */
|
||||
void *specific_addr;
|
||||
unsigned int specific_size;
|
||||
int (*cell_ready)(struct ipu6_bus_device *adev);
|
||||
void (*cell_start)(struct ipu6_bus_device *adev);
|
||||
|
||||
unsigned int buttress_boot_offset;
|
||||
};
|
||||
|
||||
void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg,
|
||||
struct ipu6_bus_device *adev, void __iomem *base);
|
||||
|
||||
int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx);
|
||||
bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx);
|
||||
int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx);
|
||||
int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force);
|
||||
|
||||
void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr);
|
||||
void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr);
|
||||
void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr);
|
||||
void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr);
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user