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 ...
Unified · Split
| @@ -0,0 +1,161 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +.. include:: <isonum.txt> | |
| 4 | + | |
| 5 | +======================================================== | |
| 6 | +Intel Image Processing Unit 6 (IPU6) Input System driver | |
| 7 | +======================================================== | |
| 8 | + | |
| 9 | +Copyright |copy| 2023--2024 Intel Corporation | |
| 10 | + | |
| 11 | +Introduction | |
| 12 | +============ | |
| 13 | + | |
| 14 | +This file documents the Intel IPU6 (6th generation Image Processing Unit) | |
| 15 | +Input System (MIPI CSI2 receiver) drivers located under | |
| 16 | +drivers/media/pci/intel/ipu6. | |
| 17 | + | |
| 18 | +The Intel IPU6 can be found in certain Intel SoCs but not in all SKUs: | |
| 19 | + | |
| 20 | +* Tiger Lake | |
| 21 | +* Jasper Lake | |
| 22 | +* Alder Lake | |
| 23 | +* Raptor Lake | |
| 24 | +* Meteor Lake | |
| 25 | + | |
| 26 | +Intel IPU6 is made up of two components - Input System (ISYS) and Processing | |
| 27 | +System (PSYS). | |
| 28 | + | |
| 29 | +The Input System mainly works as MIPI CSI-2 receiver which receives and | |
| 30 | +processes the image data from the sensors and outputs the frames to memory. | |
| 31 | + | |
| 32 | +There are 2 driver modules - intel-ipu6 and intel-ipu6-isys. intel-ipu6 is an | |
| 33 | +IPU6 common driver which does PCI configuration, firmware loading and parsing, | |
| 34 | +firmware authentication, DMA mapping and IPU-MMU (internal Memory mapping Unit) | |
| 35 | +configuration. intel_ipu6_isys implements V4L2, Media Controller and V4L2 | |
| 36 | +sub-device interfaces. The IPU6 ISYS driver supports camera sensors connected | |
| 37 | +to the IPU6 ISYS through V4L2 sub-device sensor drivers. | |
| 38 | + | |
| 39 | +.. Note:: See Documentation/driver-api/media/drivers/ipu6.rst for more | |
| 40 | + information about the IPU6 hardware. | |
| 41 | + | |
| 42 | +Input system driver | |
| 43 | +=================== | |
| 44 | + | |
| 45 | +The Input System driver mainly configures CSI-2 D-PHY, constructs the firmware | |
| 46 | +stream configuration, sends commands to firmware, gets response from hardware | |
| 47 | +and firmware and then returns buffers to user. The ISYS is represented as | |
| 48 | +several V4L2 sub-devices as well as video nodes. | |
| 49 | + | |
| 50 | +.. kernel-figure:: ipu6_isys_graph.svg | |
| 51 | + :alt: ipu6 isys media graph with multiple streams support | |
| 52 | + | |
| 53 | + IPU6 ISYS media graph with multiple streams support | |
| 54 | + | |
| 55 | +The graph has been produced using the following command: | |
| 56 | + | |
| 57 | +.. code-block:: none | |
| 58 | + | |
| 59 | + fdp -Gsplines=true -Tsvg < dot > dot.svg | |
| 60 | + | |
| 61 | +Capturing frames with IPU6 ISYS | |
| 62 | +------------------------------- | |
| 63 | + | |
| 64 | +IPU6 ISYS is used to capture frames from the camera sensors connected to the | |
| 65 | +CSI2 ports. The supported input formats of ISYS are listed in table below: | |
| 66 | + | |
| 67 | +.. tabularcolumns:: |p{0.8cm}|p{4.0cm}|p{4.0cm}| | |
| 68 | + | |
| 69 | +.. flat-table:: | |
| 70 | + :header-rows: 1 | |
| 71 | + | |
| 72 | + * - IPU6 ISYS supported input formats | |
| 73 | + | |
| 74 | + * - RGB565, RGB888 | |
| 75 | + | |
| 76 | + * - UYVY8, YUYV8 | |
| 77 | + | |
| 78 | + * - RAW8, RAW10, RAW12 | |
| 79 | + | |
| 80 | +.. _ipu6_isys_capture_examples: | |
| 81 | + | |
| 82 | +Examples | |
| 83 | +~~~~~~~~ | |
| 84 | + | |
| 85 | +Here is an example of IPU6 ISYS raw capture on Dell XPS 9315 laptop. On this | |
| 86 | +machine, ov01a10 sensor is connected to IPU ISYS CSI-2 port 2, which can | |
| 87 | +generate images at sBGGR10 with resolution 1280x800. | |
| 88 | + | |
| 89 | +Using the media controller APIs, we can configure ov01a10 sensor by | |
| 90 | +media-ctl [#f1]_ and yavta [#f2]_ to transmit frames to IPU6 ISYS. | |
| 91 | + | |
| 92 | +.. code-block:: none | |
| 93 | + | |
| 94 | + # Example 1 capture frame from ov01a10 camera sensor | |
| 95 | + # This example assumes /dev/media0 as the IPU ISYS media device | |
| 96 | + export MDEV=/dev/media0 | |
| 97 | + | |
| 98 | + # Establish the link for the media devices using media-ctl | |
| 99 | + media-ctl -d $MDEV -l "\"ov01a10 3-0036\":0 -> \"Intel IPU6 CSI2 2\":0[1]" | |
| 100 | + | |
| 101 | + # Set the format for the media devices | |
| 102 | + media-ctl -d $MDEV -V "ov01a10:0 [fmt:SBGGR10/1280x800]" | |
| 103 | + media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:0 [fmt:SBGGR10/1280x800]" | |
| 104 | + media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:1 [fmt:SBGGR10/1280x800]" | |
| 105 | + | |
| 106 | +Once the media pipeline is configured, desired sensor specific settings | |
| 107 | +(such as exposure and gain settings) can be set, using the yavta tool. | |
| 108 | + | |
| 109 | +e.g | |
| 110 | + | |
| 111 | +.. code-block:: none | |
| 112 | + | |
| 113 | + # and that ov01a10 sensor is connected to i2c bus 3 with address 0x36 | |
| 114 | + export SDEV=$(media-ctl -d $MDEV -e "ov01a10 3-0036") | |
| 115 | + | |
| 116 | + yavta -w 0x009e0903 400 $SDEV | |
| 117 | + yavta -w 0x009e0913 1000 $SDEV | |
| 118 | + yavta -w 0x009e0911 2000 $SDEV | |
| 119 | + | |
| 120 | +Once the desired sensor settings are set, frame captures can be done as below. | |
| 121 | + | |
| 122 | +e.g | |
| 123 | + | |
| 124 | +.. code-block:: none | |
| 125 | + | |
| 126 | + yavta --data-prefix -u -c10 -n5 -I -s 1280x800 --file=/tmp/frame-#.bin \ | |
| 127 | + -f SBGGR10 $(media-ctl -d $MDEV -e "Intel IPU6 ISYS Capture 0") | |
| 128 | + | |
| 129 | +With the above command, 10 frames are captured at 1280x800 resolution with | |
| 130 | +sBGGR10 format. The captured frames are available as /tmp/frame-#.bin files. | |
| 131 | + | |
| 132 | +Here is another example of IPU6 ISYS RAW and metadata capture from camera | |
| 133 | +sensor ov2740 on Lenovo X1 Yoga laptop. | |
| 134 | + | |
| 135 | +.. code-block:: none | |
| 136 | + | |
| 137 | + media-ctl -l "\"ov2740 14-0036\":0 -> \"Intel IPU6 CSI2 1\":0[1]" | |
| 138 | + media-ctl -l "\"Intel IPU6 CSI2 1\":1 -> \"Intel IPU6 ISYS Capture 0\":0[5]" | |
| 139 | + media-ctl -l "\"Intel IPU6 CSI2 1\":2 -> \"Intel IPU6 ISYS Capture 1\":0[5]" | |
| 140 | + | |
| 141 | + # set routing | |
| 142 | + media-ctl -v -R "\"Intel IPU6 CSI2 1\" [0/0->1/0[1],0/1->2/1[1]]" | |
| 143 | + | |
| 144 | + media-ctl -v "\"Intel IPU6 CSI2 1\":0/0 [fmt:SGRBG10/1932x1092]" | |
| 145 | + media-ctl -v "\"Intel IPU6 CSI2 1\":0/1 [fmt:GENERIC_8/97x1]" | |
| 146 | + media-ctl -v "\"Intel IPU6 CSI2 1\":1/0 [fmt:SGRBG10/1932x1092]" | |
| 147 | + media-ctl -v "\"Intel IPU6 CSI2 1\":2/1 [fmt:GENERIC_8/97x1]" | |
| 148 | + | |
| 149 | + CAPTURE_DEV=$(media-ctl -e "Intel IPU6 ISYS Capture 0") | |
| 150 | + ./yavta --data-prefix -c100 -n5 -I -s1932x1092 --file=/tmp/frame-#.bin \ | |
| 151 | + -f SGRBG10 ${CAPTURE_DEV} | |
| 152 | + | |
| 153 | + CAPTURE_META=$(media-ctl -e "Intel IPU6 ISYS Capture 1") | |
| 154 | + ./yavta --data-prefix -c100 -n5 -I -s97x1 -B meta-capture \ | |
| 155 | + --file=/tmp/meta-#.bin -f GENERIC_8 ${CAPTURE_META} | |
| 156 | + | |
| 157 | +References | |
| 158 | +========== | |
| 159 | + | |
| 160 | +.. [#f1] https://git.ideasonboard.org/media-ctl.git | |
| 161 | +.. [#f2] https://git.ideasonboard.org/yavta.git | |
| @@ -0,0 +1,548 @@ | ||
| 1 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |
| 2 | +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" | |
| 3 | + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> | |
| 4 | +<!-- Generated by graphviz version 2.43.0 (0) | |
| 5 | + --> | |
| 6 | +<!-- Title: board Pages: 1 --> | |
| 7 | +<svg width="1703pt" height="1473pt" | |
| 8 | + viewBox="0.00 0.00 1703.00 1473.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | |
| 9 | +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1469)"> | |
| 10 | +<title>board</title> | |
| 11 | +<polygon fill="white" stroke="transparent" points="-4,4 -4,-1469 1699,-1469 1699,4 -4,4"/> | |
| 12 | +<!-- n00000001 --> | |
| 13 | +<g id="node1" class="node"> | |
| 14 | +<title>n00000001</title> | |
| 15 | +<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"/> | |
| 16 | +<text text-anchor="middle" x="731.49" y="-734.88" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 0</text> | |
| 17 | +<text text-anchor="middle" x="731.49" y="-719.88" font-family="Times,serif" font-size="14.00">/dev/video0</text> | |
| 18 | +</g> | |
| 19 | +<!-- n00000005 --> | |
| 20 | +<g id="node2" class="node"> | |
| 21 | +<title>n00000005</title> | |
| 22 | +<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"/> | |
| 23 | +<text text-anchor="middle" x="1295.09" y="-756.68" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 1</text> | |
| 24 | +<text text-anchor="middle" x="1295.09" y="-741.68" font-family="Times,serif" font-size="14.00">/dev/video1</text> | |
| 25 | +</g> | |
| 26 | +<!-- n00000009 --> | |
| 27 | +<g id="node3" class="node"> | |
| 28 | +<title>n00000009</title> | |
| 29 | +<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"/> | |
| 30 | +<text text-anchor="middle" x="1017.02" y="-675.27" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 2</text> | |
| 31 | +<text text-anchor="middle" x="1017.02" y="-660.27" font-family="Times,serif" font-size="14.00">/dev/video2</text> | |
| 32 | +</g> | |
| 33 | +<!-- n0000000d --> | |
| 34 | +<g id="node4" class="node"> | |
| 35 | +<title>n0000000d</title> | |
| 36 | +<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"/> | |
| 37 | +<text text-anchor="middle" x="1004.39" y="-823.64" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 3</text> | |
| 38 | +<text text-anchor="middle" x="1004.39" y="-808.64" font-family="Times,serif" font-size="14.00">/dev/video3</text> | |
| 39 | +</g> | |
| 40 | +<!-- n00000011 --> | |
| 41 | +<g id="node5" class="node"> | |
| 42 | +<title>n00000011</title> | |
| 43 | +<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"/> | |
| 44 | +<text text-anchor="middle" x="1177.72" y="-977.75" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 4</text> | |
| 45 | +<text text-anchor="middle" x="1177.72" y="-962.75" font-family="Times,serif" font-size="14.00">/dev/video4</text> | |
| 46 | +</g> | |
| 47 | +<!-- n00000015 --> | |
| 48 | +<g id="node6" class="node"> | |
| 49 | +<title>n00000015</title> | |
| 50 | +<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"/> | |
| 51 | +<text text-anchor="middle" x="837.68" y="-969.71" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 5</text> | |
| 52 | +<text text-anchor="middle" x="837.68" y="-954.71" font-family="Times,serif" font-size="14.00">/dev/video5</text> | |
| 53 | +</g> | |
| 54 | +<!-- n00000019 --> | |
| 55 | +<g id="node7" class="node"> | |
| 56 | +<title>n00000019</title> | |
| 57 | +<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"/> | |
| 58 | +<text text-anchor="middle" x="856.37" y="-512.79" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 6</text> | |
| 59 | +<text text-anchor="middle" x="856.37" y="-497.79" font-family="Times,serif" font-size="14.00">/dev/video6</text> | |
| 60 | +</g> | |
| 61 | +<!-- n0000001d --> | |
| 62 | +<g id="node8" class="node"> | |
| 63 | +<title>n0000001d</title> | |
| 64 | +<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"/> | |
| 65 | +<text text-anchor="middle" x="1189.52" y="-526.95" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 7</text> | |
| 66 | +<text text-anchor="middle" x="1189.52" y="-511.95" font-family="Times,serif" font-size="14.00">/dev/video7</text> | |
| 67 | +</g> | |
| 68 | +<!-- n00000021 --> | |
| 69 | +<g id="node9" class="node"> | |
| 70 | +<title>n00000021</title> | |
| 71 | +<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"/> | |
| 72 | +<text text-anchor="middle" x="101.24" y="-596.26" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 8</text> | |
| 73 | +<text text-anchor="middle" x="101.24" y="-581.26" font-family="Times,serif" font-size="14.00">/dev/video8</text> | |
| 74 | +</g> | |
| 75 | +<!-- n00000025 --> | |
| 76 | +<g id="node10" class="node"> | |
| 77 | +<title>n00000025</title> | |
| 78 | +<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"/> | |
| 79 | +<text text-anchor="middle" x="663.36" y="-622.69" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 9</text> | |
| 80 | +<text text-anchor="middle" x="663.36" y="-607.69" font-family="Times,serif" font-size="14.00">/dev/video9</text> | |
| 81 | +</g> | |
| 82 | +<!-- n00000029 --> | |
| 83 | +<g id="node11" class="node"> | |
| 84 | +<title>n00000029</title> | |
| 85 | +<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"/> | |
| 86 | +<text text-anchor="middle" x="252.62" y="-504.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 10</text> | |
| 87 | +<text text-anchor="middle" x="252.62" y="-489.3" font-family="Times,serif" font-size="14.00">/dev/video10</text> | |
| 88 | +</g> | |
| 89 | +<!-- n0000002d --> | |
| 90 | +<g id="node12" class="node"> | |
| 91 | +<title>n0000002d</title> | |
| 92 | +<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"/> | |
| 93 | +<text text-anchor="middle" x="375.4" y="-647.39" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 11</text> | |
| 94 | +<text text-anchor="middle" x="375.4" y="-632.39" font-family="Times,serif" font-size="14.00">/dev/video11</text> | |
| 95 | +</g> | |
| 96 | +<!-- n00000031 --> | |
| 97 | +<g id="node13" class="node"> | |
| 98 | +<title>n00000031</title> | |
| 99 | +<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"/> | |
| 100 | +<text text-anchor="middle" x="531.17" y="-822.27" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 12</text> | |
| 101 | +<text text-anchor="middle" x="531.17" y="-807.27" font-family="Times,serif" font-size="14.00">/dev/video12</text> | |
| 102 | +</g> | |
| 103 | +<!-- n00000035 --> | |
| 104 | +<g id="node14" class="node"> | |
| 105 | +<title>n00000035</title> | |
| 106 | +<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"/> | |
| 107 | +<text text-anchor="middle" x="231.75" y="-818.47" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 13</text> | |
| 108 | +<text text-anchor="middle" x="231.75" y="-803.47" font-family="Times,serif" font-size="14.00">/dev/video13</text> | |
| 109 | +</g> | |
| 110 | +<!-- n00000039 --> | |
| 111 | +<g id="node15" class="node"> | |
| 112 | +<title>n00000039</title> | |
| 113 | +<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"/> | |
| 114 | +<text text-anchor="middle" x="287.07" y="-302.76" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 14</text> | |
| 115 | +<text text-anchor="middle" x="287.07" y="-287.76" font-family="Times,serif" font-size="14.00">/dev/video14</text> | |
| 116 | +</g> | |
| 117 | +<!-- n0000003d --> | |
| 118 | +<g id="node16" class="node"> | |
| 119 | +<title>n0000003d</title> | |
| 120 | +<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"/> | |
| 121 | +<text text-anchor="middle" x="595.46" y="-375.84" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 15</text> | |
| 122 | +<text text-anchor="middle" x="595.46" y="-360.84" font-family="Times,serif" font-size="14.00">/dev/video15</text> | |
| 123 | +</g> | |
| 124 | +<!-- n00000041 --> | |
| 125 | +<g id="node17" class="node"> | |
| 126 | +<title>n00000041</title> | |
| 127 | +<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"/> | |
| 128 | +<text text-anchor="middle" x="106.45" y="-1213.6" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 16</text> | |
| 129 | +<text text-anchor="middle" x="106.45" y="-1198.6" font-family="Times,serif" font-size="14.00">/dev/video16</text> | |
| 130 | +</g> | |
| 131 | +<!-- n00000045 --> | |
| 132 | +<g id="node18" class="node"> | |
| 133 | +<title>n00000045</title> | |
| 134 | +<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"/> | |
| 135 | +<text text-anchor="middle" x="678.86" y="-1237.18" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 17</text> | |
| 136 | +<text text-anchor="middle" x="678.86" y="-1222.18" font-family="Times,serif" font-size="14.00">/dev/video17</text> | |
| 137 | +</g> | |
| 138 | +<!-- n00000049 --> | |
| 139 | +<g id="node19" class="node"> | |
| 140 | +<title>n00000049</title> | |
| 141 | +<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"/> | |
| 142 | +<text text-anchor="middle" x="397.14" y="-1154.76" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 18</text> | |
| 143 | +<text text-anchor="middle" x="397.14" y="-1139.76" font-family="Times,serif" font-size="14.00">/dev/video18</text> | |
| 144 | +</g> | |
| 145 | +<!-- n0000004d --> | |
| 146 | +<g id="node20" class="node"> | |
| 147 | +<title>n0000004d</title> | |
| 148 | +<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"/> | |
| 149 | +<text text-anchor="middle" x="386.62" y="-1304.2" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 19</text> | |
| 150 | +<text text-anchor="middle" x="386.62" y="-1289.2" font-family="Times,serif" font-size="14.00">/dev/video19</text> | |
| 151 | +</g> | |
| 152 | +<!-- n00000051 --> | |
| 153 | +<g id="node21" class="node"> | |
| 154 | +<title>n00000051</title> | |
| 155 | +<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"/> | |
| 156 | +<text text-anchor="middle" x="574.74" y="-1449.46" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 20</text> | |
| 157 | +<text text-anchor="middle" x="574.74" y="-1434.46" font-family="Times,serif" font-size="14.00">/dev/video20</text> | |
| 158 | +</g> | |
| 159 | +<!-- n00000055 --> | |
| 160 | +<g id="node22" class="node"> | |
| 161 | +<title>n00000055</title> | |
| 162 | +<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"/> | |
| 163 | +<text text-anchor="middle" x="196.42" y="-1437.36" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 21</text> | |
| 164 | +<text text-anchor="middle" x="196.42" y="-1422.36" font-family="Times,serif" font-size="14.00">/dev/video21</text> | |
| 165 | +</g> | |
| 166 | +<!-- n00000059 --> | |
| 167 | +<g id="node23" class="node"> | |
| 168 | +<title>n00000059</title> | |
| 169 | +<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"/> | |
| 170 | +<text text-anchor="middle" x="213.89" y="-1003.12" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 22</text> | |
| 171 | +<text text-anchor="middle" x="213.89" y="-988.12" font-family="Times,serif" font-size="14.00">/dev/video22</text> | |
| 172 | +</g> | |
| 173 | +<!-- n0000005d --> | |
| 174 | +<g id="node24" class="node"> | |
| 175 | +<title>n0000005d</title> | |
| 176 | +<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"/> | |
| 177 | +<text text-anchor="middle" x="586.62" y="-1016.19" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 23</text> | |
| 178 | +<text text-anchor="middle" x="586.62" y="-1001.19" font-family="Times,serif" font-size="14.00">/dev/video23</text> | |
| 179 | +</g> | |
| 180 | +<!-- n00000061 --> | |
| 181 | +<g id="node25" class="node"> | |
| 182 | +<title>n00000061</title> | |
| 183 | +<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"/> | |
| 184 | +<text text-anchor="middle" x="1016.45" y="-233.6" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 24</text> | |
| 185 | +<text text-anchor="middle" x="1016.45" y="-218.6" font-family="Times,serif" font-size="14.00">/dev/video24</text> | |
| 186 | +</g> | |
| 187 | +<!-- n00000065 --> | |
| 188 | +<g id="node26" class="node"> | |
| 189 | +<title>n00000065</title> | |
| 190 | +<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"/> | |
| 191 | +<text text-anchor="middle" x="1588.86" y="-257.18" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 25</text> | |
| 192 | +<text text-anchor="middle" x="1588.86" y="-242.18" font-family="Times,serif" font-size="14.00">/dev/video25</text> | |
| 193 | +</g> | |
| 194 | +<!-- n00000069 --> | |
| 195 | +<g id="node27" class="node"> | |
| 196 | +<title>n00000069</title> | |
| 197 | +<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"/> | |
| 198 | +<text text-anchor="middle" x="1307.14" y="-174.76" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 26</text> | |
| 199 | +<text text-anchor="middle" x="1307.14" y="-159.76" font-family="Times,serif" font-size="14.00">/dev/video26</text> | |
| 200 | +</g> | |
| 201 | +<!-- n0000006d --> | |
| 202 | +<g id="node28" class="node"> | |
| 203 | +<title>n0000006d</title> | |
| 204 | +<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"/> | |
| 205 | +<text text-anchor="middle" x="1296.62" y="-324.2" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 27</text> | |
| 206 | +<text text-anchor="middle" x="1296.62" y="-309.2" font-family="Times,serif" font-size="14.00">/dev/video27</text> | |
| 207 | +</g> | |
| 208 | +<!-- n00000071 --> | |
| 209 | +<g id="node29" class="node"> | |
| 210 | +<title>n00000071</title> | |
| 211 | +<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"/> | |
| 212 | +<text text-anchor="middle" x="1484.74" y="-469.46" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 28</text> | |
| 213 | +<text text-anchor="middle" x="1484.74" y="-454.46" font-family="Times,serif" font-size="14.00">/dev/video28</text> | |
| 214 | +</g> | |
| 215 | +<!-- n00000075 --> | |
| 216 | +<g id="node30" class="node"> | |
| 217 | +<title>n00000075</title> | |
| 218 | +<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"/> | |
| 219 | +<text text-anchor="middle" x="1106.42" y="-457.36" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 29</text> | |
| 220 | +<text text-anchor="middle" x="1106.42" y="-442.36" font-family="Times,serif" font-size="14.00">/dev/video29</text> | |
| 221 | +</g> | |
| 222 | +<!-- n00000079 --> | |
| 223 | +<g id="node31" class="node"> | |
| 224 | +<title>n00000079</title> | |
| 225 | +<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"/> | |
| 226 | +<text text-anchor="middle" x="1123.89" y="-23.12" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 30</text> | |
| 227 | +<text text-anchor="middle" x="1123.89" y="-8.12" font-family="Times,serif" font-size="14.00">/dev/video30</text> | |
| 228 | +</g> | |
| 229 | +<!-- n0000007d --> | |
| 230 | +<g id="node32" class="node"> | |
| 231 | +<title>n0000007d</title> | |
| 232 | +<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"/> | |
| 233 | +<text text-anchor="middle" x="1496.62" y="-36.19" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 31</text> | |
| 234 | +<text text-anchor="middle" x="1496.62" y="-21.19" font-family="Times,serif" font-size="14.00">/dev/video31</text> | |
| 235 | +</g> | |
| 236 | +<!-- n00000081 --> | |
| 237 | +<g id="node33" class="node"> | |
| 238 | +<title>n00000081</title> | |
| 239 | +<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"/> | |
| 240 | +<text text-anchor="middle" x="1016.28" y="-769.08" font-family="Times,serif" font-size="14.00">0</text> | |
| 241 | +<polyline fill="none" stroke="black" points="912.28,-761.28 1120.28,-761.28 "/> | |
| 242 | +<text text-anchor="middle" x="1016.28" y="-746.08" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 0</text> | |
| 243 | +<text text-anchor="middle" x="1016.28" y="-731.08" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev0</text> | |
| 244 | +<polyline fill="none" stroke="black" points="912.28,-723.28 1120.28,-723.28 "/> | |
| 245 | +<text text-anchor="middle" x="925.28" y="-708.08" font-family="Times,serif" font-size="14.00">1</text> | |
| 246 | +<polyline fill="none" stroke="black" points="938.28,-700.28 938.28,-723.28 "/> | |
| 247 | +<text text-anchor="middle" x="951.28" y="-708.08" font-family="Times,serif" font-size="14.00">2</text> | |
| 248 | +<polyline fill="none" stroke="black" points="964.28,-700.28 964.28,-723.28 "/> | |
| 249 | +<text text-anchor="middle" x="977.28" y="-708.08" font-family="Times,serif" font-size="14.00">3</text> | |
| 250 | +<polyline fill="none" stroke="black" points="990.28,-700.28 990.28,-723.28 "/> | |
| 251 | +<text text-anchor="middle" x="1003.28" y="-708.08" font-family="Times,serif" font-size="14.00">4</text> | |
| 252 | +<polyline fill="none" stroke="black" points="1016.28,-700.28 1016.28,-723.28 "/> | |
| 253 | +<text text-anchor="middle" x="1029.28" y="-708.08" font-family="Times,serif" font-size="14.00">5</text> | |
| 254 | +<polyline fill="none" stroke="black" points="1042.28,-700.28 1042.28,-723.28 "/> | |
| 255 | +<text text-anchor="middle" x="1055.28" y="-708.08" font-family="Times,serif" font-size="14.00">6</text> | |
| 256 | +<polyline fill="none" stroke="black" points="1068.28,-700.28 1068.28,-723.28 "/> | |
| 257 | +<text text-anchor="middle" x="1081.28" y="-708.08" font-family="Times,serif" font-size="14.00">7</text> | |
| 258 | +<polyline fill="none" stroke="black" points="1094.28,-700.28 1094.28,-723.28 "/> | |
| 259 | +<text text-anchor="middle" x="1107.28" y="-708.08" font-family="Times,serif" font-size="14.00">8</text> | |
| 260 | +</g> | |
| 261 | +<!-- n00000081->n00000001 --> | |
| 262 | +<g id="edge1" class="edge"> | |
| 263 | +<title>n00000081:port1->n00000001</title> | |
| 264 | +<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"/> | |
| 265 | +<polygon fill="black" stroke="black" points="842.81,-715.37 833.25,-719.94 843.57,-722.33 842.81,-715.37"/> | |
| 266 | +</g> | |
| 267 | +<!-- n00000081->n00000005 --> | |
| 268 | +<g id="edge2" class="edge"> | |
| 269 | +<title>n00000081:port2->n00000005</title> | |
| 270 | +<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"/> | |
| 271 | +<polygon fill="black" stroke="black" points="1189.45,-734.06 1200.05,-733.86 1191.64,-727.41 1189.45,-734.06"/> | |
| 272 | +</g> | |
| 273 | +<!-- n00000081->n00000009 --> | |
| 274 | +<g id="edge3" class="edge"> | |
| 275 | +<title>n00000081:port3->n00000009</title> | |
| 276 | +<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"/> | |
| 277 | +<polygon fill="black" stroke="black" points="984.7,-699.23 990.74,-690.53 980.59,-693.56 984.7,-699.23"/> | |
| 278 | +</g> | |
| 279 | +<!-- n00000081->n0000000d --> | |
| 280 | +<g id="edge4" class="edge"> | |
| 281 | +<title>n00000081:port4->n0000000d</title> | |
| 282 | +<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"/> | |
| 283 | +<polygon fill="black" stroke="black" points="928.69,-800.82 939.28,-800.79 930.98,-794.21 928.69,-800.82"/> | |
| 284 | +</g> | |
| 285 | +<!-- n00000081->n00000011 --> | |
| 286 | +<g id="edge5" class="edge"> | |
| 287 | +<title>n00000081:port5->n00000011</title> | |
| 288 | +<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"/> | |
| 289 | +<polygon fill="black" stroke="black" points="1179.16,-944.31 1181.18,-954.71 1186.09,-945.32 1179.16,-944.31"/> | |
| 290 | +</g> | |
| 291 | +<!-- n00000081->n00000015 --> | |
| 292 | +<g id="edge6" class="edge"> | |
| 293 | +<title>n00000081:port6->n00000015</title> | |
| 294 | +<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"/> | |
| 295 | +<polygon fill="black" stroke="black" points="831.58,-936.99 835.74,-946.73 838.56,-936.52 831.58,-936.99"/> | |
| 296 | +</g> | |
| 297 | +<!-- n00000081->n00000019 --> | |
| 298 | +<g id="edge7" class="edge"> | |
| 299 | +<title>n00000081:port7->n00000019</title> | |
| 300 | +<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"/> | |
| 301 | +<polygon fill="black" stroke="black" points="859.11,-538.05 855.59,-528.06 852.11,-538.07 859.11,-538.05"/> | |
| 302 | +</g> | |
| 303 | +<!-- n00000081->n0000001d --> | |
| 304 | +<g id="edge8" class="edge"> | |
| 305 | +<title>n00000081:port8->n0000001d</title> | |
| 306 | +<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"/> | |
| 307 | +<polygon fill="black" stroke="black" points="1188.29,-552.76 1186.69,-542.29 1181.41,-551.47 1188.29,-552.76"/> | |
| 308 | +</g> | |
| 309 | +<!-- n0000008b --> | |
| 310 | +<g id="node34" class="node"> | |
| 311 | +<title>n0000008b</title> | |
| 312 | +<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"/> | |
| 313 | +<text text-anchor="middle" x="385.1" y="-600.88" font-family="Times,serif" font-size="14.00">0</text> | |
| 314 | +<polyline fill="none" stroke="black" points="281.1,-593.08 489.1,-593.08 "/> | |
| 315 | +<text text-anchor="middle" x="385.1" y="-577.88" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 1</text> | |
| 316 | +<text text-anchor="middle" x="385.1" y="-562.88" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev1</text> | |
| 317 | +<polyline fill="none" stroke="black" points="281.1,-555.08 489.1,-555.08 "/> | |
| 318 | +<text text-anchor="middle" x="294.1" y="-539.88" font-family="Times,serif" font-size="14.00">1</text> | |
| 319 | +<polyline fill="none" stroke="black" points="307.1,-532.08 307.1,-555.08 "/> | |
| 320 | +<text text-anchor="middle" x="320.1" y="-539.88" font-family="Times,serif" font-size="14.00">2</text> | |
| 321 | +<polyline fill="none" stroke="black" points="333.1,-532.08 333.1,-555.08 "/> | |
| 322 | +<text text-anchor="middle" x="346.1" y="-539.88" font-family="Times,serif" font-size="14.00">3</text> | |
| 323 | +<polyline fill="none" stroke="black" points="359.1,-532.08 359.1,-555.08 "/> | |
| 324 | +<text text-anchor="middle" x="372.1" y="-539.88" font-family="Times,serif" font-size="14.00">4</text> | |
| 325 | +<polyline fill="none" stroke="black" points="385.1,-532.08 385.1,-555.08 "/> | |
| 326 | +<text text-anchor="middle" x="398.1" y="-539.88" font-family="Times,serif" font-size="14.00">5</text> | |
| 327 | +<polyline fill="none" stroke="black" points="411.1,-532.08 411.1,-555.08 "/> | |
| 328 | +<text text-anchor="middle" x="424.1" y="-539.88" font-family="Times,serif" font-size="14.00">6</text> | |
| 329 | +<polyline fill="none" stroke="black" points="437.1,-532.08 437.1,-555.08 "/> | |
| 330 | +<text text-anchor="middle" x="450.1" y="-539.88" font-family="Times,serif" font-size="14.00">7</text> | |
| 331 | +<polyline fill="none" stroke="black" points="463.1,-532.08 463.1,-555.08 "/> | |
| 332 | +<text text-anchor="middle" x="476.1" y="-539.88" font-family="Times,serif" font-size="14.00">8</text> | |
| 333 | +</g> | |
| 334 | +<!-- n0000008b->n00000021 --> | |
| 335 | +<g id="edge9" class="edge"> | |
| 336 | +<title>n0000008b:port1->n00000021</title> | |
| 337 | +<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"/> | |
| 338 | +<polygon fill="black" stroke="black" points="203.2,-567.39 194.47,-573.39 205.03,-574.15 203.2,-567.39"/> | |
| 339 | +</g> | |
| 340 | +<!-- n0000008b->n00000025 --> | |
| 341 | +<g id="edge10" class="edge"> | |
| 342 | +<title>n0000008b:port2->n00000025</title> | |
| 343 | +<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"/> | |
| 344 | +<polygon fill="black" stroke="black" points="554.54,-597.67 564.9,-599.88 558.18,-591.69 554.54,-597.67"/> | |
| 345 | +</g> | |
| 346 | +<!-- n0000008b->n00000029 --> | |
| 347 | +<g id="edge11" class="edge"> | |
| 348 | +<title>n0000008b:port3->n00000029</title> | |
| 349 | +<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"/> | |
| 350 | +<polygon fill="black" stroke="black" points="319.48,-519.39 308.88,-519.5 317.24,-526.02 319.48,-519.39"/> | |
| 351 | +</g> | |
| 352 | +<!-- n0000008b->n0000002d --> | |
| 353 | +<g id="edge12" class="edge"> | |
| 354 | +<title>n0000008b:port4->n0000002d</title> | |
| 355 | +<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"/> | |
| 356 | +<polygon fill="black" stroke="black" points="282.71,-625.14 293.29,-624.58 284.67,-618.42 282.71,-625.14"/> | |
| 357 | +</g> | |
| 358 | +<!-- n0000008b->n00000031 --> | |
| 359 | +<g id="edge13" class="edge"> | |
| 360 | +<title>n0000008b:port5->n00000031</title> | |
| 361 | +<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"/> | |
| 362 | +<polygon fill="black" stroke="black" points="534.69,-788.79 535.99,-799.31 541.53,-790.28 534.69,-788.79"/> | |
| 363 | +</g> | |
| 364 | +<!-- n0000008b->n00000035 --> | |
| 365 | +<g id="edge14" class="edge"> | |
| 366 | +<title>n0000008b:port6->n00000035</title> | |
| 367 | +<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"/> | |
| 368 | +<polygon fill="black" stroke="black" points="222.29,-786.14 227.54,-795.35 229.17,-784.88 222.29,-786.14"/> | |
| 369 | +</g> | |
| 370 | +<!-- n0000008b->n00000039 --> | |
| 371 | +<g id="edge15" class="edge"> | |
| 372 | +<title>n0000008b:port7->n00000039</title> | |
| 373 | +<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"/> | |
| 374 | +<polygon fill="black" stroke="black" points="301.96,-326.05 294.92,-318.14 295.49,-328.72 301.96,-326.05"/> | |
| 375 | +</g> | |
| 376 | +<!-- n0000008b->n0000003d --> | |
| 377 | +<g id="edge16" class="edge"> | |
| 378 | +<title>n0000008b:port8->n0000003d</title> | |
| 379 | +<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"/> | |
| 380 | +<polygon fill="black" stroke="black" points="593.08,-401.69 591.93,-391.16 586.26,-400.11 593.08,-401.69"/> | |
| 381 | +</g> | |
| 382 | +<!-- n00000095 --> | |
| 383 | +<g id="node35" class="node"> | |
| 384 | +<title>n00000095</title> | |
| 385 | +<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"/> | |
| 386 | +<text text-anchor="middle" x="393.38" y="-1248.91" font-family="Times,serif" font-size="14.00">0</text> | |
| 387 | +<polyline fill="none" stroke="black" points="289.38,-1241.11 497.38,-1241.11 "/> | |
| 388 | +<text text-anchor="middle" x="393.38" y="-1225.91" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 2</text> | |
| 389 | +<text text-anchor="middle" x="393.38" y="-1210.91" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev2</text> | |
| 390 | +<polyline fill="none" stroke="black" points="289.38,-1203.11 497.38,-1203.11 "/> | |
| 391 | +<text text-anchor="middle" x="302.38" y="-1187.91" font-family="Times,serif" font-size="14.00">1</text> | |
| 392 | +<polyline fill="none" stroke="black" points="315.38,-1180.11 315.38,-1203.11 "/> | |
| 393 | +<text text-anchor="middle" x="328.38" y="-1187.91" font-family="Times,serif" font-size="14.00">2</text> | |
| 394 | +<polyline fill="none" stroke="black" points="341.38,-1180.11 341.38,-1203.11 "/> | |
| 395 | +<text text-anchor="middle" x="354.38" y="-1187.91" font-family="Times,serif" font-size="14.00">3</text> | |
| 396 | +<polyline fill="none" stroke="black" points="367.38,-1180.11 367.38,-1203.11 "/> | |
| 397 | +<text text-anchor="middle" x="380.38" y="-1187.91" font-family="Times,serif" font-size="14.00">4</text> | |
| 398 | +<polyline fill="none" stroke="black" points="393.38,-1180.11 393.38,-1203.11 "/> | |
| 399 | +<text text-anchor="middle" x="406.38" y="-1187.91" font-family="Times,serif" font-size="14.00">5</text> | |
| 400 | +<polyline fill="none" stroke="black" points="419.38,-1180.11 419.38,-1203.11 "/> | |
| 401 | +<text text-anchor="middle" x="432.38" y="-1187.91" font-family="Times,serif" font-size="14.00">6</text> | |
| 402 | +<polyline fill="none" stroke="black" points="445.38,-1180.11 445.38,-1203.11 "/> | |
| 403 | +<text text-anchor="middle" x="458.38" y="-1187.91" font-family="Times,serif" font-size="14.00">7</text> | |
| 404 | +<polyline fill="none" stroke="black" points="471.38,-1180.11 471.38,-1203.11 "/> | |
| 405 | +<text text-anchor="middle" x="484.38" y="-1187.91" font-family="Times,serif" font-size="14.00">8</text> | |
| 406 | +</g> | |
| 407 | +<!-- n00000095->n00000041 --> | |
| 408 | +<g id="edge17" class="edge"> | |
| 409 | +<title>n00000095:port1->n00000041</title> | |
| 410 | +<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"/> | |
| 411 | +<polygon fill="black" stroke="black" points="222.19,-1194.46 212.6,-1198.96 222.9,-1201.42 222.19,-1194.46"/> | |
| 412 | +</g> | |
| 413 | +<!-- n00000095->n00000045 --> | |
| 414 | +<g id="edge18" class="edge"> | |
| 415 | +<title>n00000095:port2->n00000045</title> | |
| 416 | +<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"/> | |
| 417 | +<polygon fill="black" stroke="black" points="568.86,-1214.61 579.45,-1214.34 571,-1207.95 568.86,-1214.61"/> | |
| 418 | +</g> | |
| 419 | +<!-- n00000095->n00000049 --> | |
| 420 | +<g id="edge19" class="edge"> | |
| 421 | +<title>n00000095:port3->n00000049</title> | |
| 422 | +<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"/> | |
| 423 | +<polygon fill="black" stroke="black" points="362.56,-1178.77 368.86,-1170.24 358.62,-1172.98 362.56,-1178.77"/> | |
| 424 | +</g> | |
| 425 | +<!-- n00000095->n0000004d --> | |
| 426 | +<g id="edge20" class="edge"> | |
| 427 | +<title>n00000095:port4->n0000004d</title> | |
| 428 | +<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"/> | |
| 429 | +<polygon fill="black" stroke="black" points="308.49,-1281.53 319.09,-1281.36 310.7,-1274.88 308.49,-1281.53"/> | |
| 430 | +</g> | |
| 431 | +<!-- n00000095->n00000051 --> | |
| 432 | +<g id="edge21" class="edge"> | |
| 433 | +<title>n00000095:port5->n00000051</title> | |
| 434 | +<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"/> | |
| 435 | +<polygon fill="black" stroke="black" points="573.59,-1416.23 576.51,-1426.41 580.58,-1416.63 573.59,-1416.23"/> | |
| 436 | +</g> | |
| 437 | +<!-- n00000095->n00000055 --> | |
| 438 | +<g id="edge22" class="edge"> | |
| 439 | +<title>n00000095:port6->n00000055</title> | |
| 440 | +<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"/> | |
| 441 | +<polygon fill="black" stroke="black" points="192.94,-1404.28 196.21,-1414.36 199.94,-1404.44 192.94,-1404.28"/> | |
| 442 | +</g> | |
| 443 | +<!-- n00000095->n00000059 --> | |
| 444 | +<g id="edge23" class="edge"> | |
| 445 | +<title>n00000095:port7->n00000059</title> | |
| 446 | +<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"/> | |
| 447 | +<polygon fill="black" stroke="black" points="220.39,-1028.06 215.6,-1018.61 213.46,-1028.98 220.39,-1028.06"/> | |
| 448 | +</g> | |
| 449 | +<!-- n00000095->n0000005d --> | |
| 450 | +<g id="edge24" class="edge"> | |
| 451 | +<title>n00000095:port8->n0000005d</title> | |
| 452 | +<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"/> | |
| 453 | +<polygon fill="black" stroke="black" points="583.06,-1042.09 582.28,-1031.53 576.3,-1040.27 583.06,-1042.09"/> | |
| 454 | +</g> | |
| 455 | +<!-- n0000009f --> | |
| 456 | +<g id="node36" class="node"> | |
| 457 | +<title>n0000009f</title> | |
| 458 | +<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"/> | |
| 459 | +<text text-anchor="middle" x="1303.38" y="-268.91" font-family="Times,serif" font-size="14.00">0</text> | |
| 460 | +<polyline fill="none" stroke="black" points="1199.38,-261.11 1407.38,-261.11 "/> | |
| 461 | +<text text-anchor="middle" x="1303.38" y="-245.91" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 3</text> | |
| 462 | +<text text-anchor="middle" x="1303.38" y="-230.91" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev3</text> | |
| 463 | +<polyline fill="none" stroke="black" points="1199.38,-223.11 1407.38,-223.11 "/> | |
| 464 | +<text text-anchor="middle" x="1212.38" y="-207.91" font-family="Times,serif" font-size="14.00">1</text> | |
| 465 | +<polyline fill="none" stroke="black" points="1225.38,-200.11 1225.38,-223.11 "/> | |
| 466 | +<text text-anchor="middle" x="1238.38" y="-207.91" font-family="Times,serif" font-size="14.00">2</text> | |
| 467 | +<polyline fill="none" stroke="black" points="1251.38,-200.11 1251.38,-223.11 "/> | |
| 468 | +<text text-anchor="middle" x="1264.38" y="-207.91" font-family="Times,serif" font-size="14.00">3</text> | |
| 469 | +<polyline fill="none" stroke="black" points="1277.38,-200.11 1277.38,-223.11 "/> | |
| 470 | +<text text-anchor="middle" x="1290.38" y="-207.91" font-family="Times,serif" font-size="14.00">4</text> | |
| 471 | +<polyline fill="none" stroke="black" points="1303.38,-200.11 1303.38,-223.11 "/> | |
| 472 | +<text text-anchor="middle" x="1316.38" y="-207.91" font-family="Times,serif" font-size="14.00">5</text> | |
| 473 | +<polyline fill="none" stroke="black" points="1329.38,-200.11 1329.38,-223.11 "/> | |
| 474 | +<text text-anchor="middle" x="1342.38" y="-207.91" font-family="Times,serif" font-size="14.00">6</text> | |
| 475 | +<polyline fill="none" stroke="black" points="1355.38,-200.11 1355.38,-223.11 "/> | |
| 476 | +<text text-anchor="middle" x="1368.38" y="-207.91" font-family="Times,serif" font-size="14.00">7</text> | |
| 477 | +<polyline fill="none" stroke="black" points="1381.38,-200.11 1381.38,-223.11 "/> | |
| 478 | +<text text-anchor="middle" x="1394.38" y="-207.91" font-family="Times,serif" font-size="14.00">8</text> | |
| 479 | +</g> | |
| 480 | +<!-- n0000009f->n00000061 --> | |
| 481 | +<g id="edge25" class="edge"> | |
| 482 | +<title>n0000009f:port1->n00000061</title> | |
| 483 | +<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"/> | |
| 484 | +<polygon fill="black" stroke="black" points="1132.19,-214.46 1122.6,-218.96 1132.9,-221.42 1132.19,-214.46"/> | |
| 485 | +</g> | |
| 486 | +<!-- n0000009f->n00000065 --> | |
| 487 | +<g id="edge26" class="edge"> | |
| 488 | +<title>n0000009f:port2->n00000065</title> | |
| 489 | +<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"/> | |
| 490 | +<polygon fill="black" stroke="black" points="1478.86,-234.61 1489.45,-234.34 1481,-227.95 1478.86,-234.61"/> | |
| 491 | +</g> | |
| 492 | +<!-- n0000009f->n00000069 --> | |
| 493 | +<g id="edge27" class="edge"> | |
| 494 | +<title>n0000009f:port3->n00000069</title> | |
| 495 | +<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"/> | |
| 496 | +<polygon fill="black" stroke="black" points="1272.56,-198.77 1278.86,-190.24 1268.62,-192.98 1272.56,-198.77"/> | |
| 497 | +</g> | |
| 498 | +<!-- n0000009f->n0000006d --> | |
| 499 | +<g id="edge28" class="edge"> | |
| 500 | +<title>n0000009f:port4->n0000006d</title> | |
| 501 | +<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"/> | |
| 502 | +<polygon fill="black" stroke="black" points="1218.49,-301.53 1229.09,-301.36 1220.7,-294.88 1218.49,-301.53"/> | |
| 503 | +</g> | |
| 504 | +<!-- n0000009f->n00000071 --> | |
| 505 | +<g id="edge29" class="edge"> | |
| 506 | +<title>n0000009f:port5->n00000071</title> | |
| 507 | +<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"/> | |
| 508 | +<polygon fill="black" stroke="black" points="1483.59,-436.23 1486.51,-446.41 1490.58,-436.63 1483.59,-436.23"/> | |
| 509 | +</g> | |
| 510 | +<!-- n0000009f->n00000075 --> | |
| 511 | +<g id="edge30" class="edge"> | |
| 512 | +<title>n0000009f:port6->n00000075</title> | |
| 513 | +<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"/> | |
| 514 | +<polygon fill="black" stroke="black" points="1102.94,-424.28 1106.21,-434.36 1109.94,-424.44 1102.94,-424.28"/> | |
| 515 | +</g> | |
| 516 | +<!-- n0000009f->n00000079 --> | |
| 517 | +<g id="edge31" class="edge"> | |
| 518 | +<title>n0000009f:port7->n00000079</title> | |
| 519 | +<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"/> | |
| 520 | +<polygon fill="black" stroke="black" points="1130.39,-48.06 1125.6,-38.61 1123.46,-48.98 1130.39,-48.06"/> | |
| 521 | +</g> | |
| 522 | +<!-- n0000009f->n0000007d --> | |
| 523 | +<g id="edge32" class="edge"> | |
| 524 | +<title>n0000009f:port8->n0000007d</title> | |
| 525 | +<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"/> | |
| 526 | +<polygon fill="black" stroke="black" points="1493.06,-62.09 1492.28,-51.53 1486.3,-60.27 1493.06,-62.09"/> | |
| 527 | +</g> | |
| 528 | +<!-- n000000e9 --> | |
| 529 | +<g id="node37" class="node"> | |
| 530 | +<title>n000000e9</title> | |
| 531 | +<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"/> | |
| 532 | +<text text-anchor="middle" x="420.65" y="-500.25" font-family="Times,serif" font-size="14.00">1</text> | |
| 533 | +<polyline fill="none" stroke="black" points="454.65,-492.45 454.65,-515.45 "/> | |
| 534 | +<text text-anchor="middle" x="489.15" y="-500.25" font-family="Times,serif" font-size="14.00">2</text> | |
| 535 | +<polyline fill="none" stroke="black" points="386.65,-492.45 523.65,-492.45 "/> | |
| 536 | +<text text-anchor="middle" x="455.15" y="-477.25" font-family="Times,serif" font-size="14.00">ov2740 4-0036</text> | |
| 537 | +<text text-anchor="middle" x="455.15" y="-462.25" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev4</text> | |
| 538 | +<polyline fill="none" stroke="black" points="386.65,-454.45 523.65,-454.45 "/> | |
| 539 | +<text text-anchor="middle" x="455.15" y="-439.25" font-family="Times,serif" font-size="14.00">0</text> | |
| 540 | +</g> | |
| 541 | +<!-- n000000e9->n0000008b --> | |
| 542 | +<g id="edge33" class="edge"> | |
| 543 | +<title>n000000e9:port0->n0000008b:port0</title> | |
| 544 | +<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"/> | |
| 545 | +<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"/> | |
| 546 | +</g> | |
| 547 | +</g> | |
| 548 | +</svg> | |
| @@ -1,8 +1,10 @@ | ||
| 1 | 1 | .. SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | |
| 3 | −==================== | |
| 4 | −mgb4 sysfs interface | |
| 5 | −==================== | |
| 3 | +The mgb4 driver | |
| 4 | +=============== | |
| 5 | + | |
| 6 | +sysfs interface | |
| 7 | +--------------- | |
| 6 | 8 | |
| 7 | 9 | The mgb4 driver provides a sysfs interface, that is used to configure video |
| 8 | 10 | 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 · 3 unmodified lines | ||
| 12 | 14 | ``/sys/class/video4linux/videoX/device`` and module specific found under |
| 13 | 15 | ``/sys/class/video4linux/videoX``. |
| 14 | 16 | |
| 15 | − | |
| 16 | 17 | Global (PCI card) parameters |
| 17 | −============================ | |
| 18 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 18 | 19 | |
| 19 | 20 | **module_type** (R): |
| 20 | 21 | Module type. |
| @@ -42,9 +43,8 @@ Global (PCI card) parameters · 21 unmodified lines | ||
| 42 | 43 | |
| 43 | 44 | where each component is a 8b number. |
| 44 | 45 | |
| 45 | − | |
| 46 | 46 | Common FPDL3/GMSL input parameters |
| 47 | −================================== | |
| 47 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 48 | 48 | |
| 49 | 49 | **input_id** (R): |
| 50 | 50 | Input number ID, zero based. |
| @@ -190,9 +190,8 @@ Common FPDL3/GMSL input parameters · 139 unmodified lines | ||
| 190 | 190 | *Note: This parameter can not be changed while the input v4l2 device is |
| 191 | 191 | open.* |
| 192 | 192 | |
| 193 | − | |
| 194 | 193 | Common FPDL3/GMSL output parameters |
| 195 | −=================================== | |
| 194 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 196 | 195 | |
| 197 | 196 | **output_id** (R): |
| 198 | 197 | Output number ID, zero based. |
| @@ -282,9 +281,8 @@ Common FPDL3/GMSL output parameters · 83 unmodified lines | ||
| 282 | 281 | Number of video lines between the end of the last valid pixel line (marked |
| 283 | 282 | by DE=1) and assertion of the VSYNC signal. The default value is 2. |
| 284 | 283 | |
| 285 | − | |
| 286 | 284 | FPDL3 specific input parameters |
| 287 | −=============================== | |
| 285 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 288 | 286 | |
| 289 | 287 | **fpdl3_input_width** (RW): |
| 290 | 288 | Number of deserializer input lines. |
| @@ -294,7 +292,7 @@ FPDL3 specific input parameters · 3 unmodified lines | ||
| 294 | 292 | | 2 - dual |
| 295 | 293 | |
| 296 | 294 | FPDL3 specific output parameters |
| 297 | −================================ | |
| 295 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 298 | 296 | |
| 299 | 297 | **fpdl3_output_width** (RW): |
| 300 | 298 | Number of serializer output lines. |
| @@ -304,7 +302,7 @@ FPDL3 specific output parameters · 3 unmodified lines | ||
| 304 | 302 | | 2 - dual |
| 305 | 303 | |
| 306 | 304 | GMSL specific input parameters |
| 307 | −============================== | |
| 305 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 308 | 306 | |
| 309 | 307 | **gmsl_mode** (RW): |
| 310 | 308 | GMSL speed mode. |
| @@ -328,10 +326,8 @@ GMSL specific input parameters · 17 unmodified lines | ||
| 328 | 326 | | 0 - disabled |
| 329 | 327 | | 1 - enabled (default) |
| 330 | 328 | |
| 331 | − | |
| 332 | −==================== | |
| 333 | −mgb4 mtd partitions | |
| 334 | −==================== | |
| 329 | +MTD partitions | |
| 330 | +-------------- | |
| 335 | 331 | |
| 336 | 332 | The mgb4 driver creates a MTD device with two partitions: |
| 337 | 333 | - mgb4-fw.X - FPGA firmware. |
| @@ -344,9 +340,8 @@ also have a third partition named *mgb4-flash* available in the system. This · 6 unmodified lines | ||
| 344 | 340 | partition represents the whole, unpartitioned, card's FLASH memory and one should |
| 345 | 341 | not fiddle with it... |
| 346 | 342 | |
| 347 | −==================== | |
| 348 | −mgb4 iio (triggers) | |
| 349 | −==================== | |
| 343 | +IIO (triggers) | |
| 344 | +-------------- | |
| 350 | 345 | |
| 351 | 346 | The mgb4 driver creates an Industrial I/O (IIO) device that provides trigger and |
| 352 | 347 | signal level status capability. The following scan elements are available: |
| @@ -16,6 +16,7 @@ Video4Linux (V4L) driver-specific documentation · 15 unmodified lines | ||
| 16 | 16 | imx |
| 17 | 17 | imx7 |
| 18 | 18 | ipu3 |
| 19 | + ipu6-isys | |
| 19 | 20 | ivtv |
| 20 | 21 | mgb4 |
| 21 | 22 | omap3isp |
| @@ -0,0 +1,127 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/media/brcm,bcm2835-unicam.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: Broadcom BCM283x Camera Interface (Unicam) | |
| 8 | + | |
| 9 | +maintainers: | |
| 10 | + - Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com> | |
| 11 | + | |
| 12 | +description: |- | |
| 13 | + The Unicam block on BCM283x SoCs is the receiver for either | |
| 14 | + CSI-2 or CCP2 data from image sensors or similar devices. | |
| 15 | + | |
| 16 | + The main platform using this SoC is the Raspberry Pi family of boards. On | |
| 17 | + the Pi the VideoCore firmware can also control this hardware block, and | |
| 18 | + driving it from two different processors will cause issues. To avoid this, | |
| 19 | + the firmware checks the device tree configuration during boot. If it finds | |
| 20 | + device tree nodes whose name starts with 'csi' then it will stop the firmware | |
| 21 | + accessing the block, and it can then safely be used via the device tree | |
| 22 | + binding. | |
| 23 | + | |
| 24 | +properties: | |
| 25 | + compatible: | |
| 26 | + const: brcm,bcm2835-unicam | |
| 27 | + | |
| 28 | + reg: | |
| 29 | + items: | |
| 30 | + - description: Unicam block. | |
| 31 | + - description: Clock Manager Image (CMI) block. | |
| 32 | + | |
| 33 | + reg-names: | |
| 34 | + items: | |
| 35 | + - const: unicam | |
| 36 | + - const: cmi | |
| 37 | + | |
| 38 | + interrupts: | |
| 39 | + maxItems: 1 | |
| 40 | + | |
| 41 | + clocks: | |
| 42 | + items: | |
| 43 | + - description: Clock to drive the LP state machine of Unicam. | |
| 44 | + - description: Clock for the VPU (core clock). | |
| 45 | + | |
| 46 | + clock-names: | |
| 47 | + items: | |
| 48 | + - const: lp | |
| 49 | + - const: vpu | |
| 50 | + | |
| 51 | + power-domains: | |
| 52 | + items: | |
| 53 | + - description: Unicam power domain | |
| 54 | + | |
| 55 | + brcm,num-data-lanes: | |
| 56 | + $ref: /schemas/types.yaml#/definitions/uint32 | |
| 57 | + enum: [ 2, 4 ] | |
| 58 | + description: | | |
| 59 | + Number of CSI-2 data lanes supported by this Unicam instance. The number | |
| 60 | + of data lanes actively used is specified with the data-lanes endpoint | |
| 61 | + property. | |
| 62 | + | |
| 63 | + port: | |
| 64 | + $ref: /schemas/graph.yaml#/$defs/port-base | |
| 65 | + unevaluatedProperties: false | |
| 66 | + | |
| 67 | + properties: | |
| 68 | + endpoint: | |
| 69 | + $ref: /schemas/media/video-interfaces.yaml# | |
| 70 | + additionalProperties: false | |
| 71 | + | |
| 72 | + properties: | |
| 73 | + bus-type: | |
| 74 | + enum: [ 3, 4 ] | |
| 75 | + | |
| 76 | + clock-noncontinuous: true | |
| 77 | + data-lanes: true | |
| 78 | + remote-endpoint: true | |
| 79 | + | |
| 80 | + required: | |
| 81 | + - bus-type | |
| 82 | + - data-lanes | |
| 83 | + - remote-endpoint | |
| 84 | + | |
| 85 | + required: | |
| 86 | + - endpoint | |
| 87 | + | |
| 88 | +required: | |
| 89 | + - compatible | |
| 90 | + - reg | |
| 91 | + - reg-names | |
| 92 | + - interrupts | |
| 93 | + - clocks | |
| 94 | + - clock-names | |
| 95 | + - power-domains | |
| 96 | + - brcm,num-data-lanes | |
| 97 | + - port | |
| 98 | + | |
| 99 | +additionalProperties: False | |
| 100 | + | |
| 101 | +examples: | |
| 102 | + - | | |
| 103 | + #include <dt-bindings/clock/bcm2835.h> | |
| 104 | + #include <dt-bindings/interrupt-controller/arm-gic.h> | |
| 105 | + #include <dt-bindings/media/video-interfaces.h> | |
| 106 | + #include <dt-bindings/power/raspberrypi-power.h> | |
| 107 | + | |
| 108 | + csi1: csi@7e801000 { | |
| 109 | + compatible = "brcm,bcm2835-unicam"; | |
| 110 | + reg = <0x7e801000 0x800>, | |
| 111 | + <0x7e802004 0x4>; | |
| 112 | + reg-names = "unicam", "cmi"; | |
| 113 | + interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>; | |
| 114 | + clocks = <&clocks BCM2835_CLOCK_CAM1>, | |
| 115 | + <&firmware_clocks 4>; | |
| 116 | + clock-names = "lp", "vpu"; | |
| 117 | + power-domains = <&power RPI_POWER_DOMAIN_UNICAM1>; | |
| 118 | + brcm,num-data-lanes = <2>; | |
| 119 | + port { | |
| 120 | + csi1_ep: endpoint { | |
| 121 | + remote-endpoint = <&imx219_0>; | |
| 122 | + bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>; | |
| 123 | + data-lanes = <1 2>; | |
| 124 | + }; | |
| 125 | + }; | |
| 126 | + }; | |
| 127 | +... | |
| @@ -15,7 +15,7 @@ description: | · 14 unmodified lines | ||
| 15 | 15 | They include an ISP capable of auto exposure and auto white balance. |
| 16 | 16 | |
| 17 | 17 | allOf: |
| 18 | − - $ref: ../video-interface-devices.yaml# | |
| 18 | + - $ref: /schemas/media/video-interface-devices.yaml# | |
| 19 | 19 | |
| 20 | 20 | properties: |
| 21 | 21 | compatible: |
| @@ -19,7 +19,7 @@ description: · 18 unmodified lines | ||
| 19 | 19 | either through a parallel interface or through MIPI CSI-2. |
| 20 | 20 | |
| 21 | 21 | allOf: |
| 22 | − - $ref: ../video-interface-devices.yaml# | |
| 22 | + - $ref: /schemas/media/video-interface-devices.yaml# | |
| 23 | 23 | |
| 24 | 24 | properties: |
| 25 | 25 | compatible: |
| @@ -37,31 +37,45 @@ properties: · 36 unmodified lines | ||
| 37 | 37 | active low. |
| 38 | 38 | maxItems: 1 |
| 39 | 39 | |
| 40 | − dovdd-supply: | |
| 40 | + DOVDD-supply: | |
| 41 | 41 | description: |
| 42 | 42 | Definition of the regulator used as interface power supply. |
| 43 | 43 | |
| 44 | − avdd-supply: | |
| 44 | + AVDD-supply: | |
| 45 | 45 | description: |
| 46 | 46 | Definition of the regulator used as analog power supply. |
| 47 | 47 | |
| 48 | − dvdd-supply: | |
| 48 | + DVDD-supply: | |
| 49 | 49 | description: |
| 50 | 50 | Definition of the regulator used as digital power supply. |
| 51 | 51 | |
| 52 | 52 | port: |
| 53 | − $ref: /schemas/graph.yaml#/properties/port | |
| 54 | 53 | description: |
| 55 | 54 | A node containing an output port node. |
| 55 | + $ref: /schemas/graph.yaml#/$defs/port-base | |
| 56 | + additionalProperties: false | |
| 57 | + | |
| 58 | + properties: | |
| 59 | + endpoint: | |
| 60 | + $ref: /schemas/media/video-interfaces.yaml# | |
| 61 | + additionalProperties: false | |
| 62 | + | |
| 63 | + properties: | |
| 64 | + link-frequencies: true | |
| 65 | + | |
| 66 | + remote-endpoint: true | |
| 67 | + | |
| 68 | + required: | |
| 69 | + - link-frequencies | |
| 56 | 70 | |
| 57 | 71 | required: |
| 58 | 72 | - compatible |
| 59 | 73 | - reg |
| 60 | 74 | - clocks |
| 61 | 75 | - clock-names |
| 62 | − - dovdd-supply | |
| 63 | − - avdd-supply | |
| 64 | − - dvdd-supply | |
| 76 | + - DOVDD-supply | |
| 77 | + - AVDD-supply | |
| 78 | + - DVDD-supply | |
| 65 | 79 | - reset-gpios |
| 66 | 80 | - port |
| 67 | 81 | |
| @@ -82,13 +96,14 @@ examples: · 14 unmodified lines | ||
| 82 | 96 | clock-names = "xvclk"; |
| 83 | 97 | reset-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; |
| 84 | 98 | |
| 85 | − dovdd-supply = <&sw2_reg>; | |
| 86 | − dvdd-supply = <&sw2_reg>; | |
| 87 | − avdd-supply = <®_peri_3p15v>; | |
| 99 | + DOVDD-supply = <&sw2_reg>; | |
| 100 | + DVDD-supply = <&sw2_reg>; | |
| 101 | + AVDD-supply = <®_peri_3p15v>; | |
| 88 | 102 | |
| 89 | 103 | port { |
| 90 | 104 | ov2680_to_mipi: endpoint { |
| 91 | 105 | remote-endpoint = <&mipi_from_sensor>; |
| 106 | + link-frequencies = /bits/ 64 <330000000>; | |
| 92 | 107 | }; |
| 93 | 108 | }; |
| 94 | 109 | }; |
| @@ -2,7 +2,7 @@ · 1 unmodified line | ||
| 2 | 2 | # Copyright (c) 2019 MediaTek Inc. |
| 3 | 3 | %YAML 1.2 |
| 4 | 4 | --- |
| 5 | −$id: http://devicetree.org/schemas/media/i2c/ov8856.yaml# | |
| 5 | +$id: http://devicetree.org/schemas/media/i2c/ovti,ov8856.yaml# | |
| 6 | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | |
| 8 | 8 | title: Omnivision OV8856 CMOS Sensor |
| @@ -16,7 +16,7 @@ description: | · 15 unmodified lines | ||
| 16 | 16 | maximum throughput of 1.2Gbps/lane. |
| 17 | 17 | |
| 18 | 18 | allOf: |
| 19 | − - $ref: ../video-interface-devices.yaml# | |
| 19 | + - $ref: /schemas/media/video-interface-devices.yaml# | |
| 20 | 20 | |
| 21 | 21 | properties: |
| 22 | 22 | compatible: |
| @@ -23,6 +23,9 @@ description: |- · 22 unmodified lines | ||
| 23 | 23 | is treated the same as this as it was the original compatible string. |
| 24 | 24 | imx290llr is the mono version of the sensor. |
| 25 | 25 | |
| 26 | +allOf: | |
| 27 | + - $ref: /schemas/media/video-interface-devices.yaml# | |
| 28 | + | |
| 26 | 29 | properties: |
| 27 | 30 | compatible: |
| 28 | 31 | oneOf: |
| @@ -101,7 +104,7 @@ required: · 72 unmodified lines | ||
| 101 | 104 | - vdddo-supply |
| 102 | 105 | - port |
| 103 | 106 | |
| 104 | −additionalProperties: false | |
| 107 | +unevaluatedProperties: false | |
| 105 | 108 | |
| 106 | 109 | examples: |
| 107 | 110 | - | |
| @@ -18,7 +18,7 @@ description: |- · 17 unmodified lines | ||
| 18 | 18 | available via CSI-2 serial data output (two or four lanes). |
| 19 | 19 | |
| 20 | 20 | allOf: |
| 21 | − - $ref: ../video-interface-devices.yaml# | |
| 21 | + - $ref: /schemas/media/video-interface-devices.yaml# | |
| 22 | 22 | |
| 23 | 23 | properties: |
| 24 | 24 | compatible: |
| @@ -84,6 +84,7 @@ allOf: · 83 unmodified lines | ||
| 84 | 84 | properties: |
| 85 | 85 | port@0: |
| 86 | 86 | description: MIPI CSI-2 RX |
| 87 | + port@1: false | |
| 87 | 88 | required: |
| 88 | 89 | - port@0 |
| 89 | 90 | |
| @@ -31,6 +31,11 @@ properties: · 30 unmodified lines | ||
| 31 | 31 | reg: |
| 32 | 32 | maxItems: 1 |
| 33 | 33 | |
| 34 | + clocks: | |
| 35 | + items: | |
| 36 | + - description: AXI DMA engine clock for fetching JPEG bitstream from memory (per) | |
| 37 | + - description: IP bus clock for register access (ipg) | |
| 38 | + | |
| 34 | 39 | interrupts: |
| 35 | 40 | description: | |
| 36 | 41 | There are 4 slots available in the IP, which the driver may use |
| @@ -49,6 +54,7 @@ properties: · 12 unmodified lines | ||
| 49 | 54 | required: |
| 50 | 55 | - compatible |
| 51 | 56 | - reg |
| 57 | + - clocks | |
| 52 | 58 | - interrupts |
| 53 | 59 | - power-domains |
| 54 | 60 | |
| @@ -56,12 +62,15 @@ additionalProperties: false · 1 unmodified line | ||
| 56 | 62 | |
| 57 | 63 | examples: |
| 58 | 64 | - | |
| 65 | + #include <dt-bindings/clock/imx8-lpcg.h> | |
| 59 | 66 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| 60 | 67 | #include <dt-bindings/firmware/imx/rsrc.h> |
| 61 | 68 | |
| 62 | 69 | jpegdec: jpegdec@58400000 { |
| 63 | 70 | compatible = "nxp,imx8qxp-jpgdec"; |
| 64 | 71 | reg = <0x58400000 0x00050000 >; |
| 72 | + clocks = <&img_jpeg_dec_lpcg IMX_LPCG_CLK_0>, | |
| 73 | + <&img_jpeg_dec_lpcg IMX_LPCG_CLK_4>; | |
| 65 | 74 | interrupts = <GIC_SPI 309 IRQ_TYPE_LEVEL_HIGH>, |
| 66 | 75 | <GIC_SPI 310 IRQ_TYPE_LEVEL_HIGH>, |
| 67 | 76 | <GIC_SPI 311 IRQ_TYPE_LEVEL_HIGH>, |
| @@ -76,6 +85,8 @@ examples: · 8 unmodified lines | ||
| 76 | 85 | jpegenc: jpegenc@58450000 { |
| 77 | 86 | compatible = "nxp,imx8qm-jpgenc", "nxp,imx8qxp-jpgenc"; |
| 78 | 87 | reg = <0x58450000 0x00050000 >; |
| 88 | + clocks = <&img_jpeg_enc_lpcg IMX_LPCG_CLK_0>, | |
| 89 | + <&img_jpeg__lpcg IMX_LPCG_CLK_4>; | |
| 79 | 90 | interrupts = <GIC_SPI 305 IRQ_TYPE_LEVEL_HIGH>, |
| 80 | 91 | <GIC_SPI 306 IRQ_TYPE_LEVEL_HIGH>, |
| 81 | 92 | <GIC_SPI 307 IRQ_TYPE_LEVEL_HIGH>, |
| @@ -0,0 +1,512 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/media/qcom,sc8280xp-camss.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: Qualcomm SC8280XP Camera Subsystem (CAMSS) | |
| 8 | + | |
| 9 | +maintainers: | |
| 10 | + - Bryan O'Donoghue <bryan.odonoghue@linaro.org> | |
| 11 | + | |
| 12 | +description: | | |
| 13 | + The CAMSS IP is a CSI decoder and ISP present on Qualcomm platforms. | |
| 14 | + | |
| 15 | +properties: | |
| 16 | + compatible: | |
| 17 | + const: qcom,sc8280xp-camss | |
| 18 | + | |
| 19 | + clocks: | |
| 20 | + maxItems: 40 | |
| 21 | + | |
| 22 | + clock-names: | |
| 23 | + items: | |
| 24 | + - const: camnoc_axi | |
| 25 | + - const: cpas_ahb | |
| 26 | + - const: csiphy0 | |
| 27 | + - const: csiphy0_timer | |
| 28 | + - const: csiphy1 | |
| 29 | + - const: csiphy1_timer | |
| 30 | + - const: csiphy2 | |
| 31 | + - const: csiphy2_timer | |
| 32 | + - const: csiphy3 | |
| 33 | + - const: csiphy3_timer | |
| 34 | + - const: vfe0_axi | |
| 35 | + - const: vfe0 | |
| 36 | + - const: vfe0_cphy_rx | |
| 37 | + - const: vfe0_csid | |
| 38 | + - const: vfe1_axi | |
| 39 | + - const: vfe1 | |
| 40 | + - const: vfe1_cphy_rx | |
| 41 | + - const: vfe1_csid | |
| 42 | + - const: vfe2_axi | |
| 43 | + - const: vfe2 | |
| 44 | + - const: vfe2_cphy_rx | |
| 45 | + - const: vfe2_csid | |
| 46 | + - const: vfe3_axi | |
| 47 | + - const: vfe3 | |
| 48 | + - const: vfe3_cphy_rx | |
| 49 | + - const: vfe3_csid | |
| 50 | + - const: vfe_lite0 | |
| 51 | + - const: vfe_lite0_cphy_rx | |
| 52 | + - const: vfe_lite0_csid | |
| 53 | + - const: vfe_lite1 | |
| 54 | + - const: vfe_lite1_cphy_rx | |
| 55 | + - const: vfe_lite1_csid | |
| 56 | + - const: vfe_lite2 | |
| 57 | + - const: vfe_lite2_cphy_rx | |
| 58 | + - const: vfe_lite2_csid | |
| 59 | + - const: vfe_lite3 | |
| 60 | + - const: vfe_lite3_cphy_rx | |
| 61 | + - const: vfe_lite3_csid | |
| 62 | + - const: gcc_axi_hf | |
| 63 | + - const: gcc_axi_sf | |
| 64 | + | |
| 65 | + interrupts: | |
| 66 | + maxItems: 20 | |
| 67 | + | |
| 68 | + interrupt-names: | |
| 69 | + items: | |
| 70 | + - const: csid1_lite | |
| 71 | + - const: vfe_lite1 | |
| 72 | + - const: csiphy3 | |
| 73 | + - const: csid0 | |
| 74 | + - const: vfe0 | |
| 75 | + - const: csid1 | |
| 76 | + - const: vfe1 | |
| 77 | + - const: csid0_lite | |
| 78 | + - const: vfe_lite0 | |
| 79 | + - const: csiphy0 | |
| 80 | + - const: csiphy1 | |
| 81 | + - const: csiphy2 | |
| 82 | + - const: csid2 | |
| 83 | + - const: vfe2 | |
| 84 | + - const: csid3_lite | |
| 85 | + - const: csid2_lite | |
| 86 | + - const: vfe_lite3 | |
| 87 | + - const: vfe_lite2 | |
| 88 | + - const: csid3 | |
| 89 | + - const: vfe3 | |
| 90 | + | |
| 91 | + iommus: | |
| 92 | + maxItems: 16 | |
| 93 | + | |
| 94 | + interconnects: | |
| 95 | + maxItems: 4 | |
| 96 | + | |
| 97 | + interconnect-names: | |
| 98 | + items: | |
| 99 | + - const: cam_ahb | |
| 100 | + - const: cam_hf_mnoc | |
| 101 | + - const: cam_sf_mnoc | |
| 102 | + - const: cam_sf_icp_mnoc | |
| 103 | + | |
| 104 | + power-domains: | |
| 105 | + items: | |
| 106 | + - description: IFE0 GDSC - Image Front End, Global Distributed Switch Controller. | |
| 107 | + - description: IFE1 GDSC - Image Front End, Global Distributed Switch Controller. | |
| 108 | + - description: IFE2 GDSC - Image Front End, Global Distributed Switch Controller. | |
| 109 | + - description: IFE3 GDSC - Image Front End, Global Distributed Switch Controller. | |
| 110 | + - description: Titan Top GDSC - Titan ISP Block, Global Distributed Switch Controller. | |
| 111 | + | |
| 112 | + power-domain-names: | |
| 113 | + items: | |
| 114 | + - const: ife0 | |
| 115 | + - const: ife1 | |
| 116 | + - const: ife2 | |
| 117 | + - const: ife3 | |
| 118 | + - const: top | |
| 119 | + | |
| 120 | + ports: | |
| 121 | + $ref: /schemas/graph.yaml#/properties/ports | |
| 122 | + | |
| 123 | + description: | |
| 124 | + CSI input ports. | |
| 125 | + | |
| 126 | + properties: | |
| 127 | + port@0: | |
| 128 | + $ref: /schemas/graph.yaml#/$defs/port-base | |
| 129 | + unevaluatedProperties: false | |
| 130 | + description: | |
| 131 | + Input port for receiving CSI data from CSIPHY0. | |
| 132 | + | |
| 133 | + properties: | |
| 134 | + endpoint: | |
| 135 | + $ref: video-interfaces.yaml# | |
| 136 | + unevaluatedProperties: false | |
| 137 | + | |
| 138 | + properties: | |
| 139 | + clock-lanes: | |
| 140 | + maxItems: 1 | |
| 141 | + | |
| 142 | + data-lanes: | |
| 143 | + minItems: 1 | |
| 144 | + maxItems: 4 | |
| 145 | + | |
| 146 | + required: | |
| 147 | + - clock-lanes | |
| 148 | + - data-lanes | |
| 149 | + | |
| 150 | + port@1: | |
| 151 | + $ref: /schemas/graph.yaml#/$defs/port-base | |
| 152 | + unevaluatedProperties: false | |
| 153 | + description: | |
| 154 | + Input port for receiving CSI data from CSIPHY1. | |
| 155 | + | |
| 156 | + properties: | |
| 157 | + endpoint: | |
| 158 | + $ref: video-interfaces.yaml# | |
| 159 | + unevaluatedProperties: false | |
| 160 | + | |
| 161 | + properties: | |
| 162 | + clock-lanes: | |
| 163 | + maxItems: 1 | |
| 164 | + | |
| 165 | + data-lanes: | |
| 166 | + minItems: 1 | |
| 167 | + maxItems: 4 | |
| 168 | + | |
| 169 | + required: | |
| 170 | + - clock-lanes | |
| 171 | + - data-lanes | |
| 172 | + | |
| 173 | + port@2: | |
| 174 | + $ref: /schemas/graph.yaml#/$defs/port-base | |
| 175 | + unevaluatedProperties: false | |
| 176 | + description: | |
| 177 | + Input port for receiving CSI data from CSIPHY2. | |
| 178 | + | |
| 179 | + properties: | |
| 180 | + endpoint: | |
| 181 | + $ref: video-interfaces.yaml# | |
| 182 | + unevaluatedProperties: false | |
| 183 | + | |
| 184 | + properties: | |
| 185 | + clock-lanes: | |
| 186 | + maxItems: 1 | |
| 187 | + | |
| 188 | + data-lanes: | |
| 189 | + minItems: 1 | |
| 190 | + maxItems: 4 | |
| 191 | + | |
| 192 | + required: | |
| 193 | + - clock-lanes | |
| 194 | + - data-lanes | |
| 195 | + | |
| 196 | + port@3: | |
| 197 | + $ref: /schemas/graph.yaml#/$defs/port-base | |
| 198 | + unevaluatedProperties: false | |
| 199 | + description: | |
| 200 | + Input port for receiving CSI data from CSIPHY3. | |
| 201 | + | |
| 202 | + properties: | |
| 203 | + endpoint: | |
| 204 | + $ref: video-interfaces.yaml# | |
| 205 | + unevaluatedProperties: false | |
| 206 | + | |
| 207 | + properties: | |
| 208 | + clock-lanes: | |
| 209 | + maxItems: 1 | |
| 210 | + | |
| 211 | + data-lanes: | |
| 212 | + minItems: 1 | |
| 213 | + maxItems: 4 | |
| 214 | + | |
| 215 | + required: | |
| 216 | + - clock-lanes | |
| 217 | + - data-lanes | |
| 218 | + | |
| 219 | + reg: | |
| 220 | + maxItems: 20 | |
| 221 | + | |
| 222 | + reg-names: | |
| 223 | + items: | |
| 224 | + - const: csiphy2 | |
| 225 | + - const: csiphy3 | |
| 226 | + - const: csiphy0 | |
| 227 | + - const: csiphy1 | |
| 228 | + - const: vfe0 | |
| 229 | + - const: csid0 | |
| 230 | + - const: vfe1 | |
| 231 | + - const: csid1 | |
| 232 | + - const: vfe2 | |
| 233 | + - const: csid2 | |
| 234 | + - const: vfe_lite0 | |
| 235 | + - const: csid0_lite | |
| 236 | + - const: vfe_lite1 | |
| 237 | + - const: csid1_lite | |
| 238 | + - const: vfe_lite2 | |
| 239 | + - const: csid2_lite | |
| 240 | + - const: vfe_lite3 | |
| 241 | + - const: csid3_lite | |
| 242 | + - const: vfe3 | |
| 243 | + - const: csid3 | |
| 244 | + | |
| 245 | + vdda-phy-supply: | |
| 246 | + description: | |
| 247 | + Phandle to a regulator supply to PHY core block. | |
| 248 | + | |
| 249 | + vdda-pll-supply: | |
| 250 | + description: | |
| 251 | + Phandle to 1.8V regulator supply to PHY refclk pll block. | |
| 252 | + | |
| 253 | +required: | |
| 254 | + - clock-names | |
| 255 | + - clocks | |
| 256 | + - compatible | |
| 257 | + - interconnects | |
| 258 | + - interconnect-names | |
| 259 | + - interrupts | |
| 260 | + - interrupt-names | |
| 261 | + - iommus | |
| 262 | + - power-domains | |
| 263 | + - power-domain-names | |
| 264 | + - reg | |
| 265 | + - reg-names | |
| 266 | + - vdda-phy-supply | |
| 267 | + - vdda-pll-supply | |
| 268 | + | |
| 269 | +additionalProperties: false | |
| 270 | + | |
| 271 | +examples: | |
| 272 | + - | | |
| 273 | + #include <dt-bindings/interrupt-controller/arm-gic.h> | |
| 274 | + #include <dt-bindings/clock/qcom,gcc-sc8280xp.h> | |
| 275 | + #include <dt-bindings/clock/qcom,sc8280xp-camcc.h> | |
| 276 | + #include <dt-bindings/interconnect/qcom,sc8280xp.h> | |
| 277 | + #include <dt-bindings/power/qcom-rpmpd.h> | |
| 278 | + | |
| 279 | + soc { | |
| 280 | + #address-cells = <2>; | |
| 281 | + #size-cells = <2>; | |
| 282 | + | |
| 283 | + camss: camss@ac5a000 { | |
| 284 | + compatible = "qcom,sc8280xp-camss"; | |
| 285 | + | |
| 286 | + reg = <0 0x0ac5a000 0 0x2000>, | |
| 287 | + <0 0x0ac5c000 0 0x2000>, | |
| 288 | + <0 0x0ac65000 0 0x2000>, | |
| 289 | + <0 0x0ac67000 0 0x2000>, | |
| 290 | + <0 0x0acaf000 0 0x4000>, | |
| 291 | + <0 0x0acb3000 0 0x1000>, | |
| 292 | + <0 0x0acb6000 0 0x4000>, | |
| 293 | + <0 0x0acba000 0 0x1000>, | |
| 294 | + <0 0x0acbd000 0 0x4000>, | |
| 295 | + <0 0x0acc1000 0 0x1000>, | |
| 296 | + <0 0x0acc4000 0 0x4000>, | |
| 297 | + <0 0x0acc8000 0 0x1000>, | |
| 298 | + <0 0x0accb000 0 0x4000>, | |
| 299 | + <0 0x0accf000 0 0x1000>, | |
| 300 | + <0 0x0acd2000 0 0x4000>, | |
| 301 | + <0 0x0acd6000 0 0x1000>, | |
| 302 | + <0 0x0acd9000 0 0x4000>, | |
| 303 | + <0 0x0acdd000 0 0x1000>, | |
| 304 | + <0 0x0ace0000 0 0x4000>, | |
| 305 | + <0 0x0ace4000 0 0x1000>; | |
| 306 | + | |
| 307 | + reg-names = "csiphy2", | |
| 308 | + "csiphy3", | |
| 309 | + "csiphy0", | |
| 310 | + "csiphy1", | |
| 311 | + "vfe0", | |
| 312 | + "csid0", | |
| 313 | + "vfe1", | |
| 314 | + "csid1", | |
| 315 | + "vfe2", | |
| 316 | + "csid2", | |
| 317 | + "vfe_lite0", | |
| 318 | + "csid0_lite", | |
| 319 | + "vfe_lite1", | |
| 320 | + "csid1_lite", | |
| 321 | + "vfe_lite2", | |
| 322 | + "csid2_lite", | |
| 323 | + "vfe_lite3", | |
| 324 | + "csid3_lite", | |
| 325 | + "vfe3", | |
| 326 | + "csid3"; | |
| 327 | + | |
| 328 | + vdda-phy-supply = <&vreg_l6d>; | |
| 329 | + vdda-pll-supply = <&vreg_l4d>; | |
| 330 | + | |
| 331 | + interrupts = <GIC_SPI 359 IRQ_TYPE_LEVEL_HIGH>, | |
| 332 | + <GIC_SPI 360 IRQ_TYPE_LEVEL_HIGH>, | |
| 333 | + <GIC_SPI 448 IRQ_TYPE_LEVEL_HIGH>, | |
| 334 | + <GIC_SPI 464 IRQ_TYPE_LEVEL_HIGH>, | |
| 335 | + <GIC_SPI 465 IRQ_TYPE_LEVEL_HIGH>, | |
| 336 | + <GIC_SPI 466 IRQ_TYPE_LEVEL_HIGH>, | |
| 337 | + <GIC_SPI 467 IRQ_TYPE_LEVEL_HIGH>, | |
| 338 | + <GIC_SPI 468 IRQ_TYPE_LEVEL_HIGH>, | |
| 339 | + <GIC_SPI 469 IRQ_TYPE_LEVEL_HIGH>, | |
| 340 | + <GIC_SPI 477 IRQ_TYPE_LEVEL_HIGH>, | |
| 341 | + <GIC_SPI 478 IRQ_TYPE_LEVEL_HIGH>, | |
| 342 | + <GIC_SPI 479 IRQ_TYPE_LEVEL_HIGH>, | |
| 343 | + <GIC_SPI 640 IRQ_TYPE_LEVEL_HIGH>, | |
| 344 | + <GIC_SPI 641 IRQ_TYPE_LEVEL_HIGH>, | |
| 345 | + <GIC_SPI 758 IRQ_TYPE_LEVEL_HIGH>, | |
| 346 | + <GIC_SPI 759 IRQ_TYPE_LEVEL_HIGH>, | |
| 347 | + <GIC_SPI 760 IRQ_TYPE_LEVEL_HIGH>, | |
| 348 | + <GIC_SPI 761 IRQ_TYPE_LEVEL_HIGH>, | |
| 349 | + <GIC_SPI 762 IRQ_TYPE_LEVEL_HIGH>, | |
| 350 | + <GIC_SPI 764 IRQ_TYPE_LEVEL_HIGH>; | |
| 351 | + | |
| 352 | + interrupt-names = "csid1_lite", | |
| 353 | + "vfe_lite1", | |
| 354 | + "csiphy3", | |
| 355 | + "csid0", | |
| 356 | + "vfe0", | |
| 357 | + "csid1", | |
| 358 | + "vfe1", | |
| 359 | + "csid0_lite", | |
| 360 | + "vfe_lite0", | |
| 361 | + "csiphy0", | |
| 362 | + "csiphy1", | |
| 363 | + "csiphy2", | |
| 364 | + "csid2", | |
| 365 | + "vfe2", | |
| 366 | + "csid3_lite", | |
| 367 | + "csid2_lite", | |
| 368 | + "vfe_lite3", | |
| 369 | + "vfe_lite2", | |
| 370 | + "csid3", | |
| 371 | + "vfe3"; | |
| 372 | + | |
| 373 | + power-domains = <&camcc IFE_0_GDSC>, | |
| 374 | + <&camcc IFE_1_GDSC>, | |
| 375 | + <&camcc IFE_2_GDSC>, | |
| 376 | + <&camcc IFE_3_GDSC>, | |
| 377 | + <&camcc TITAN_TOP_GDSC>; | |
| 378 | + | |
| 379 | + power-domain-names = "ife0", | |
| 380 | + "ife1", | |
| 381 | + "ife2", | |
| 382 | + "ife3", | |
| 383 | + "top"; | |
| 384 | + | |
| 385 | + clocks = <&camcc CAMCC_CAMNOC_AXI_CLK>, | |
| 386 | + <&camcc CAMCC_CPAS_AHB_CLK>, | |
| 387 | + <&camcc CAMCC_CSIPHY0_CLK>, | |
| 388 | + <&camcc CAMCC_CSI0PHYTIMER_CLK>, | |
| 389 | + <&camcc CAMCC_CSIPHY1_CLK>, | |
| 390 | + <&camcc CAMCC_CSI1PHYTIMER_CLK>, | |
| 391 | + <&camcc CAMCC_CSIPHY2_CLK>, | |
| 392 | + <&camcc CAMCC_CSI2PHYTIMER_CLK>, | |
| 393 | + <&camcc CAMCC_CSIPHY3_CLK>, | |
| 394 | + <&camcc CAMCC_CSI3PHYTIMER_CLK>, | |
| 395 | + <&camcc CAMCC_IFE_0_AXI_CLK>, | |
| 396 | + <&camcc CAMCC_IFE_0_CLK>, | |
| 397 | + <&camcc CAMCC_IFE_0_CPHY_RX_CLK>, | |
| 398 | + <&camcc CAMCC_IFE_0_CSID_CLK>, | |
| 399 | + <&camcc CAMCC_IFE_1_AXI_CLK>, | |
| 400 | + <&camcc CAMCC_IFE_1_CLK>, | |
| 401 | + <&camcc CAMCC_IFE_1_CPHY_RX_CLK>, | |
| 402 | + <&camcc CAMCC_IFE_1_CSID_CLK>, | |
| 403 | + <&camcc CAMCC_IFE_2_AXI_CLK>, | |
| 404 | + <&camcc CAMCC_IFE_2_CLK>, | |
| 405 | + <&camcc CAMCC_IFE_2_CPHY_RX_CLK>, | |
| 406 | + <&camcc CAMCC_IFE_2_CSID_CLK>, | |
| 407 | + <&camcc CAMCC_IFE_3_AXI_CLK>, | |
| 408 | + <&camcc CAMCC_IFE_3_CLK>, | |
| 409 | + <&camcc CAMCC_IFE_3_CPHY_RX_CLK>, | |
| 410 | + <&camcc CAMCC_IFE_3_CSID_CLK>, | |
| 411 | + <&camcc CAMCC_IFE_LITE_0_CLK>, | |
| 412 | + <&camcc CAMCC_IFE_LITE_0_CPHY_RX_CLK>, | |
| 413 | + <&camcc CAMCC_IFE_LITE_0_CSID_CLK>, | |
| 414 | + <&camcc CAMCC_IFE_LITE_1_CLK>, | |
| 415 | + <&camcc CAMCC_IFE_LITE_1_CPHY_RX_CLK>, | |
| 416 | + <&camcc CAMCC_IFE_LITE_1_CSID_CLK>, | |
| 417 | + <&camcc CAMCC_IFE_LITE_2_CLK>, | |
| 418 | + <&camcc CAMCC_IFE_LITE_2_CPHY_RX_CLK>, | |
| 419 | + <&camcc CAMCC_IFE_LITE_2_CSID_CLK>, | |
| 420 | + <&camcc CAMCC_IFE_LITE_3_CLK>, | |
| 421 | + <&camcc CAMCC_IFE_LITE_3_CPHY_RX_CLK>, | |
| 422 | + <&camcc CAMCC_IFE_LITE_3_CSID_CLK>, | |
| 423 | + <&gcc GCC_CAMERA_HF_AXI_CLK>, | |
| 424 | + <&gcc GCC_CAMERA_SF_AXI_CLK>; | |
| 425 | + | |
| 426 | + clock-names = "camnoc_axi", | |
| 427 | + "cpas_ahb", | |
| 428 | + "csiphy0", | |
| 429 | + "csiphy0_timer", | |
| 430 | + "csiphy1", | |
| 431 | + "csiphy1_timer", | |
| 432 | + "csiphy2", | |
| 433 | + "csiphy2_timer", | |
| 434 | + "csiphy3", | |
| 435 | + "csiphy3_timer", | |
| 436 | + "vfe0_axi", | |
| 437 | + "vfe0", | |
| 438 | + "vfe0_cphy_rx", | |
| 439 | + "vfe0_csid", | |
| 440 | + "vfe1_axi", | |
| 441 | + "vfe1", | |
| 442 | + "vfe1_cphy_rx", | |
| 443 | + "vfe1_csid", | |
| 444 | + "vfe2_axi", | |
| 445 | + "vfe2", | |
| 446 | + "vfe2_cphy_rx", | |
| 447 | + "vfe2_csid", | |
| 448 | + "vfe3_axi", | |
| 449 | + "vfe3", | |
| 450 | + "vfe3_cphy_rx", | |
| 451 | + "vfe3_csid", | |
| 452 | + "vfe_lite0", | |
| 453 | + "vfe_lite0_cphy_rx", | |
| 454 | + "vfe_lite0_csid", | |
| 455 | + "vfe_lite1", | |
| 456 | + "vfe_lite1_cphy_rx", | |
| 457 | + "vfe_lite1_csid", | |
| 458 | + "vfe_lite2", | |
| 459 | + "vfe_lite2_cphy_rx", | |
| 460 | + "vfe_lite2_csid", | |
| 461 | + "vfe_lite3", | |
| 462 | + "vfe_lite3_cphy_rx", | |
| 463 | + "vfe_lite3_csid", | |
| 464 | + "gcc_axi_hf", | |
| 465 | + "gcc_axi_sf"; | |
| 466 | + | |
| 467 | + | |
| 468 | + iommus = <&apps_smmu 0x2000 0x4e0>, | |
| 469 | + <&apps_smmu 0x2020 0x4e0>, | |
| 470 | + <&apps_smmu 0x2040 0x4e0>, | |
| 471 | + <&apps_smmu 0x2060 0x4e0>, | |
| 472 | + <&apps_smmu 0x2080 0x4e0>, | |
| 473 | + <&apps_smmu 0x20e0 0x4e0>, | |
| 474 | + <&apps_smmu 0x20c0 0x4e0>, | |
| 475 | + <&apps_smmu 0x20a0 0x4e0>, | |
| 476 | + <&apps_smmu 0x2400 0x4e0>, | |
| 477 | + <&apps_smmu 0x2420 0x4e0>, | |
| 478 | + <&apps_smmu 0x2440 0x4e0>, | |
| 479 | + <&apps_smmu 0x2460 0x4e0>, | |
| 480 | + <&apps_smmu 0x2480 0x4e0>, | |
| 481 | + <&apps_smmu 0x24e0 0x4e0>, | |
| 482 | + <&apps_smmu 0x24c0 0x4e0>, | |
| 483 | + <&apps_smmu 0x24a0 0x4e0>; | |
| 484 | + | |
| 485 | + interconnects = <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_CAMERA_CFG 0>, | |
| 486 | + <&mmss_noc MASTER_CAMNOC_HF 0 &mc_virt SLAVE_EBI1 0>, | |
| 487 | + <&mmss_noc MASTER_CAMNOC_SF 0 &mc_virt SLAVE_EBI1 0>, | |
| 488 | + <&mmss_noc MASTER_CAMNOC_ICP 0 &mc_virt SLAVE_EBI1 0>; | |
| 489 | + interconnect-names = "cam_ahb", | |
| 490 | + "cam_hf_mnoc", | |
| 491 | + "cam_sf_mnoc", | |
| 492 | + "cam_sf_icp_mnoc"; | |
| 493 | + | |
| 494 | + ports { | |
| 495 | + #address-cells = <1>; | |
| 496 | + #size-cells = <0>; | |
| 497 | + | |
| 498 | + port@0 { | |
| 499 | + reg = <0>; | |
| 500 | + #address-cells = <1>; | |
| 501 | + #size-cells = <0>; | |
| 502 | + | |
| 503 | + csiphy_ep0: endpoint@0 { | |
| 504 | + reg = <0>; | |
| 505 | + clock-lanes = <7>; | |
| 506 | + data-lanes = <0 1>; | |
| 507 | + remote-endpoint = <&sensor_ep>; | |
| 508 | + }; | |
| 509 | + }; | |
| 510 | + }; | |
| 511 | + }; | |
| 512 | + }; | |
| @@ -26,6 +26,7 @@ Video4Linux (V4L) drivers · 25 unmodified lines | ||
| 26 | 26 | vimc-devel |
| 27 | 27 | zoran |
| 28 | 28 | ccs/ccs |
| 29 | + ipu6 | |
| 29 | 30 | |
| 30 | 31 | |
| 31 | 32 | Digital TV drivers |
| @@ -0,0 +1,205 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +================== | |
| 4 | +Intel IPU6 Driver | |
| 5 | +================== | |
| 6 | + | |
| 7 | +Author: Bingbu Cao <bingbu.cao@intel.com> | |
| 8 | + | |
| 9 | +Overview | |
| 10 | +========= | |
| 11 | + | |
| 12 | +Intel IPU6 is the sixth generation of Intel Image Processing Unit used in some | |
| 13 | +Intel Chipsets such as Tiger Lake, Jasper Lake, Alder Lake, Raptor Lake and | |
| 14 | +Meteor Lake. IPU6 consists of two major systems: Input System (ISYS) and | |
| 15 | +Processing System (PSYS). IPU6 are visible on the PCI bus as a single device, it | |
| 16 | +can be found by ``lspci``: | |
| 17 | + | |
| 18 | +``0000:00:05.0 Multimedia controller: Intel Corporation Device xxxx (rev xx)`` | |
| 19 | + | |
| 20 | +IPU6 has a 16 MB BAR in PCI configuration Space for MMIO registers which is | |
| 21 | +visible for driver. | |
| 22 | + | |
| 23 | +Buttress | |
| 24 | +========= | |
| 25 | + | |
| 26 | +The IPU6 is connecting to the system fabric with Buttress which is enabling host | |
| 27 | +driver to control the IPU6, it also allows IPU6 access the system memory to | |
| 28 | +store and load frame pixel streams and any other metadata. | |
| 29 | + | |
| 30 | +Buttress mainly manages several system functionalities: power management, | |
| 31 | +interrupt handling, firmware authentication and global timer sync. | |
| 32 | + | |
| 33 | +ISYS and PSYS Power flow | |
| 34 | +------------------------ | |
| 35 | + | |
| 36 | +IPU6 driver initialize the ISYS and PSYS power up or down request by setting the | |
| 37 | +Buttress frequency control register for ISYS and PSYS | |
| 38 | +(``IPU6_BUTTRESS_REG_IS_FREQ_CTL`` and ``IPU6_BUTTRESS_REG_PS_FREQ_CTL``) in | |
| 39 | +function: | |
| 40 | + | |
| 41 | +.. c:function:: int ipu6_buttress_power(...) | |
| 42 | + | |
| 43 | +Buttress forwards the request to Punit, after Punit execute the power up flow, | |
| 44 | +Buttress indicates driver that ISYS or PSYS is powered up by updating the power | |
| 45 | +status registers. | |
| 46 | + | |
| 47 | +.. Note:: ISYS power up needs take place prior to PSYS power up, ISYS power down | |
| 48 | + needs take place after PSYS power down due to hardware limitation. | |
| 49 | + | |
| 50 | +Interrupt | |
| 51 | +--------- | |
| 52 | + | |
| 53 | +IPU6 interrupt can be generated as MSI or INTA, interrupt will be triggered when | |
| 54 | +ISYS, PSYS, Buttress event or error happen, driver can get the interrupt cause | |
| 55 | +by reading the interrupt status register ``BUTTRESS_REG_ISR_STATUS``, driver | |
| 56 | +clears the irq status and then calls specific ISYS or PSYS irq handler. | |
| 57 | + | |
| 58 | +.. c:function:: irqreturn_t ipu6_buttress_isr(int irq, ...) | |
| 59 | + | |
| 60 | +Security and firmware authentication | |
| 61 | +------------------------------------- | |
| 62 | + | |
| 63 | +To address the IPU6 firmware security concerns, the IPU6 firmware needs to | |
| 64 | +undergo an authentication process before it is allowed to executed on the IPU6 | |
| 65 | +internal processors. The IPU6 driver will work with Converged Security Engine | |
| 66 | +(CSE) to complete authentication process. The CSE is responsible of | |
| 67 | +authenticating the IPU6 firmware. The authenticated firmware binary is copied | |
| 68 | +into an isolated memory region. Firmware authentication process is implemented | |
| 69 | +by CSE following an IPC handshake with the IPU6 driver. There are some Buttress | |
| 70 | +registers used by the CSE and the IPU6 driver to communicate with each other via | |
| 71 | +IPC. | |
| 72 | + | |
| 73 | +.. c:function:: int ipu6_buttress_authenticate(...) | |
| 74 | + | |
| 75 | +Global timer sync | |
| 76 | +----------------- | |
| 77 | + | |
| 78 | +The IPU6 driver initiates a Hammock Harbor synchronization flow each time it | |
| 79 | +starts camera operation. The IPU6 will synchronizes an internal counter in the | |
| 80 | +Buttress with a copy of the SoC time, this counter maintains the up-to-date time | |
| 81 | +until camera operation is stopped. The IPU6 driver can use this time counter to | |
| 82 | +calibrate the timestamp based on the timestamp in response event from firmware. | |
| 83 | + | |
| 84 | +.. c:function:: int ipu6_buttress_start_tsc_sync(...) | |
| 85 | + | |
| 86 | +DMA and MMU | |
| 87 | +============ | |
| 88 | + | |
| 89 | +The IPU6 has its own scalar processor where the firmware run at and an internal | |
| 90 | +32-bit virtual address space. The IPU6 has MMU address translation hardware to | |
| 91 | +allow that scalar processors to access the internal memory and external system | |
| 92 | +memory through IPU6 virtual address. The address translation is based on two | |
| 93 | +levels of page lookup tables stored in system memory which are maintained by the | |
| 94 | +IPU6 driver. The IPU6 driver sets the level-1 page table base address to MMU | |
| 95 | +register and allows MMU to perform page table lookups. | |
| 96 | + | |
| 97 | +The IPU6 driver exports its own DMA operations. The IPU6 driver will update the | |
| 98 | +page table entries for each DMA operation and invalidate the MMU TLB after each | |
| 99 | +unmap and free. | |
| 100 | + | |
| 101 | +.. code-block:: none | |
| 102 | + | |
| 103 | + const struct dma_map_ops ipu6_dma_ops = { | |
| 104 | + .alloc = ipu6_dma_alloc, | |
| 105 | + .free = ipu6_dma_free, | |
| 106 | + .mmap = ipu6_dma_mmap, | |
| 107 | + .map_sg = ipu6_dma_map_sg, | |
| 108 | + .unmap_sg = ipu6_dma_unmap_sg, | |
| 109 | + ... | |
| 110 | + }; | |
| 111 | + | |
| 112 | +.. Note:: IPU6 MMU works behind IOMMU so for each IPU6 DMA ops, driver will call | |
| 113 | + generic PCI DMA ops to ask IOMMU to do the additional mapping if VT-d | |
| 114 | + enabled. | |
| 115 | + | |
| 116 | +Firmware file format | |
| 117 | +==================== | |
| 118 | + | |
| 119 | +The IPU6 firmware is in Code Partition Directory (CPD) file format. The CPD | |
| 120 | +firmware contains a CPD header, several CPD entries and components. The CPD | |
| 121 | +component includes 3 entries - manifest, metadata and module data. Manifest and | |
| 122 | +metadata are defined by CSE and used by CSE for authentication. Module data is | |
| 123 | +specific to IPU6 which holds the binary data of firmware called package | |
| 124 | +directory. The IPU6 driver (``ipu6-cpd.c`` in particular) parses and validates | |
| 125 | +the CPD firmware file and gets the package directory binary data of the IPU6 | |
| 126 | +firmware, copies it to specific DMA buffer and sets its base address to Buttress | |
| 127 | +``FW_SOURCE_BASE`` register. Finally the CSE will do authentication for this | |
| 128 | +firmware binary. | |
| 129 | + | |
| 130 | + | |
| 131 | +Syscom interface | |
| 132 | +================ | |
| 133 | + | |
| 134 | +The IPU6 driver communicates with firmware via the Syscom ABI. Syscom is an | |
| 135 | +inter-processor communication mechanism between the IPU scalar processors and | |
| 136 | +the CPU. There are a number of resources shared between firmware and software. | |
| 137 | +A system memory region where the message queues reside, firmware can access the | |
| 138 | +memory region via the IPU MMU. The Syscom queues are FIFO fixed depth queues | |
| 139 | +with a configurable number of tokens (messages). There are also common IPU6 MMIO | |
| 140 | +registers where the queue read and write indices reside. Software and firmware | |
| 141 | +function as producer and consumer of tokens in the queues and update the write | |
| 142 | +and read indices separately when sending or receiving each message. | |
| 143 | + | |
| 144 | +The IPU6 driver must prepare and configure the number of input and output | |
| 145 | +queues, configure the count of tokens per queue and the size of per token before | |
| 146 | +initiating and starting the communication with firmware. Firmware and software | |
| 147 | +must use same configurations. The IPU6 Buttress has a number of firmware boot | |
| 148 | +parameter registers which can be used to store the address of configuration and | |
| 149 | +initialise the Syscom state, then driver can request firmware to start and run via | |
| 150 | +setting the scalar processor control status register. | |
| 151 | + | |
| 152 | +Input System | |
| 153 | +============ | |
| 154 | + | |
| 155 | +IPU6 input system consists of MIPI D-PHY and several CSI-2 receivers. It can | |
| 156 | +capture image pixel data from camera sensors or other MIPI CSI-2 output devices. | |
| 157 | + | |
| 158 | +D-PHYs and CSI-2 ports lane mapping | |
| 159 | +----------------------------------- | |
| 160 | + | |
| 161 | +The IPU6 integrates different D-PHY IPs on different SoCs, on Tiger Lake and | |
| 162 | +Alder Lake, IPU6 integrates MCD10 D-PHY, IPU6SE on Jasper Lake integrates JSL | |
| 163 | +D-PHY and IPU6EP on Meteor Lake integrates a Synopsys DWC D-PHY. There is an | |
| 164 | +adaptional layer between D-PHY and CSI-2 receiver controller which includes port | |
| 165 | +configuration, PHY wrapper or private test interfaces for D-PHY. There are 3 | |
| 166 | +D-PHY drivers ``ipu6-isys-mcd-phy.c``, ``ipu6-isys-jsl-phy.c`` and | |
| 167 | +``ipu6-isys-dwc-phy.c`` program the above 3 D-PHYs in IPU6. | |
| 168 | + | |
| 169 | +Different IPU6 versions have different D-PHY lanes mappings, On Tiger Lake, | |
| 170 | +there are 12 data lanes and 8 clock lanes, IPU6 support maximum 8 CSI-2 ports, | |
| 171 | +see the PPI mmapping in ``ipu6-isys-mcd-phy.c`` for more information. On Jasper | |
| 172 | +Lake and Alder Lake, D-PHY has 8 data lanes and 4 clock lanes, the IPU6 supports | |
| 173 | +maximum 4 CSI-2 ports. For Meteor Lake, D-PHY has 12 data lanes and 6 clock | |
| 174 | +lanes so IPU6 support maximum 6 CSI-2 ports. | |
| 175 | + | |
| 176 | +.. Note:: Each pair of CSI-2 two ports is a single unit that can share the data | |
| 177 | + lanes. For example, for CSI-2 port 0 and 1, CSI-2 port 0 support | |
| 178 | + maximum 4 data lanes, CSI-2 port 1 support maximum 2 data lanes, CSI-2 | |
| 179 | + port 0 with 2 data lanes can work together with CSI-2 port 1 with 2 | |
| 180 | + data lanes. If trying to use CSI-2 port 0 with 4 lanes, CSI-2 port 1 | |
| 181 | + will not be available as the 4 data lanes are shared by CSI-2 port 0 | |
| 182 | + and 1. The same applies to CSI ports 2/3, 4/5 and 7/8. | |
| 183 | + | |
| 184 | +ISYS firmware ABIs | |
| 185 | +------------------ | |
| 186 | + | |
| 187 | +The IPU6 firmware implements a series of ABIs for software access. In general, | |
| 188 | +software firstly prepares the stream configuration ``struct | |
| 189 | +ipu6_fw_isys_stream_cfg_data_abi`` and sends the configuration to firmware via | |
| 190 | +sending ``STREAM_OPEN`` command. Stream configuration includes input pins and | |
| 191 | +output pins, input pin ``struct ipu6_fw_isys_input_pin_info_abi`` defines the | |
| 192 | +resolution and data type of input source, output pin ``struct | |
| 193 | +ipu6_fw_isys_output_pin_info_abi`` defines the output resolution, stride and | |
| 194 | +frame format, etc. | |
| 195 | + | |
| 196 | +Once the driver gets the interrupt from firmware that indicates stream open | |
| 197 | +successfully, the driver will send the ``STREAM_START`` and ``STREAM_CAPTURE`` | |
| 198 | +command to request firmware to start capturing image frames. ``STREAM_CAPTURE`` | |
| 199 | +command queues the buffers to firmware with ``struct | |
| 200 | +ipu6_fw_isys_frame_buff_set``, software then waits for the interrupt and | |
| 201 | +response from firmware, ``PIN_DATA_READY`` means a buffer is ready on a specific | |
| 202 | +output pin and then software can return the buffer to user. | |
| 203 | + | |
| 204 | +.. Note:: See :ref:`Examples<ipu6_isys_capture_examples>` about how to do | |
| 205 | + capture by IPU6 ISYS driver. | |
| @@ -70,5 +70,5 @@ include: · 69 unmodified lines | ||
| 70 | 70 | ``ENOMEM`` |
| 71 | 71 | Insufficient kernel memory was available. |
| 72 | 72 | |
| 73 | −``ENXIO`` | |
| 74 | − No device corresponding to this device special file exists. | |
| 73 | +``ENODEV`` | |
| 74 | + Device not found or was removed. | |
| @@ -91,7 +91,7 @@ appropriately. · 90 unmodified lines | ||
| 91 | 91 | - The caller has no permission to access the device. |
| 92 | 92 | |
| 93 | 93 | - - ``EBUSY`` |
| 94 | − - The the device driver is already in use. | |
| 94 | + - The device driver is already in use. | |
| 95 | 95 | |
| 96 | 96 | - - ``EMFILE`` |
| 97 | 97 | - The process already has the maximum number of files open. |
| @@ -25,6 +25,13 @@ Glossary · 24 unmodified lines | ||
| 25 | 25 | |
| 26 | 26 | See :ref:`cec`. |
| 27 | 27 | |
| 28 | + Data Unit | |
| 29 | + | |
| 30 | + Unit of data transported by a bus. On parallel buses, the data unit | |
| 31 | + consists of one or more related samples while on serial buses the data | |
| 32 | + unit is logical. If the data unit is image data, it may also be called a | |
| 33 | + pixel. | |
| 34 | + | |
| 28 | 35 | Device Driver |
| 29 | 36 | Part of the Linux Kernel that implements support for a hardware |
| 30 | 37 | component. |
| @@ -173,6 +180,11 @@ Glossary · 142 unmodified lines | ||
| 173 | 180 | An integrated circuit that integrates all components of a computer |
| 174 | 181 | or other electronic systems. |
| 175 | 182 | |
| 183 | + Stream | |
| 184 | + A distinct flow of data (image data or metadata) from an initial source | |
| 185 | + to a final sink. The initial source may be e.g. an image sensor and the | |
| 186 | + final sink e.g. a memory buffer. | |
| 187 | + | |
| 176 | 188 | V4L2 API |
| 177 | 189 | **V4L2 userspace API** |
| 178 | 190 | |
| @@ -47,6 +47,12 @@ member of the ``fmt`` union as needed per the desired operation. Both drivers · 46 unmodified lines | ||
| 47 | 47 | and applications must set the remainder of the :c:type:`v4l2_format` structure |
| 48 | 48 | to 0. |
| 49 | 49 | |
| 50 | +Devices that capture metadata by line have the struct v4l2_fmtdesc | |
| 51 | +``V4L2_FMT_FLAG_META_LINE_BASED`` flag set for :c:func:`VIDIOC_ENUM_FMT`. Such | |
| 52 | +devices can typically also :ref:`capture image data <capture>`. This primarily | |
| 53 | +involves devices that receive the data from a different devices such as a camera | |
| 54 | +sensor. | |
| 55 | + | |
| 50 | 56 | .. c:type:: v4l2_meta_format |
| 51 | 57 | |
| 52 | 58 | .. tabularcolumns:: |p{1.4cm}|p{2.4cm}|p{13.5cm}| |
| @@ -65,3 +71,18 @@ to 0. · 12 unmodified lines | ||
| 65 | 71 | - ``buffersize`` |
| 66 | 72 | - Maximum buffer size in bytes required for data. The value is set by the |
| 67 | 73 | driver. |
| 74 | + * - __u32 | |
| 75 | + - ``width`` | |
| 76 | + - Width of a line of metadata in Data Units. Valid when | |
| 77 | + :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is set, | |
| 78 | + otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`. | |
| 79 | + * - __u32 | |
| 80 | + - ``height`` | |
| 81 | + - Number of rows of metadata. Valid when :c:type`v4l2_fmtdesc` flag | |
| 82 | + ``V4L2_FMT_FLAG_META_LINE_BASED`` is set, otherwise zero. See | |
| 83 | + :c:func:`VIDIOC_ENUM_FMT`. | |
| 84 | + * - __u32 | |
| 85 | + - ``bytesperline`` | |
| 86 | + - Offset in bytes between the beginning of two consecutive lines. Valid | |
| 87 | + when :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is | |
| 88 | + set, otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`. | |
| @@ -506,6 +506,8 @@ source pads. · 505 unmodified lines | ||
| 506 | 506 | |
| 507 | 507 | subdev-formats |
| 508 | 508 | |
| 509 | +.. _subdev-routing: | |
| 510 | + | |
| 509 | 511 | Streams, multiplexed media pads and internal routing |
| 510 | 512 | ---------------------------------------------------- |
| 511 | 513 | |
| @@ -527,9 +529,9 @@ the its sink pad and allows to route them individually to one of its source · 15 unmodified lines | ||
| 527 | 529 | pads. |
| 528 | 530 | |
| 529 | 531 | Subdevice drivers that support multiplexed streams are compatible with |
| 530 | −non-multiplexed subdev drivers, but, of course, require a routing configuration | |
| 531 | −where the link between those two types of drivers contains only a single | |
| 532 | −stream. | |
| 532 | +non-multiplexed subdev drivers. However, if the driver at the sink end of a link | |
| 533 | +does not support streams, then only stream 0 of source end may be captured. | |
| 534 | +There may be additional limitations specific to the sink device. | |
| 533 | 535 | |
| 534 | 536 | Understanding streams |
| 535 | 537 | ^^^^^^^^^^^^^^^^^^^^^ |
| @@ -570,6 +572,29 @@ Any configurations of a stream within a pad, such as format or selections, · 34 unmodified lines | ||
| 570 | 572 | are independent of similar configurations on other streams. This is |
| 571 | 573 | subject to change in the future. |
| 572 | 574 | |
| 575 | +Device types and routing setup | |
| 576 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 577 | + | |
| 578 | +Different kinds of sub-devices have differing behaviour for route activation, | |
| 579 | +depending on the hardware. In all cases, however, only routes that have the | |
| 580 | +``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag set are active. | |
| 581 | + | |
| 582 | +Devices generating the streams may allow enabling and disabling some of the | |
| 583 | +routes or have a fixed routing configuration. If the routes can be disabled, not | |
| 584 | +declaring the routes (or declaring them without | |
| 585 | +``VIDIOC_SUBDEV_STREAM_FL_ACTIVE`` flag set) in ``VIDIOC_SUBDEV_S_ROUTING`` will | |
| 586 | +disable the routes. ``VIDIOC_SUBDEV_S_ROUTING`` will still return such routes | |
| 587 | +back to the user in the routes array, with the ``V4L2_SUBDEV_STREAM_FL_ACTIVE`` | |
| 588 | +flag unset. | |
| 589 | + | |
| 590 | +Devices transporting the streams almost always have more configurability with | |
| 591 | +respect to routing. Typically any route between the sub-device's sink and source | |
| 592 | +pads is possible, and multiple routes (usually up to certain limited number) may | |
| 593 | +be active simultaneously. For such devices, no routes are created by the driver | |
| 594 | +and user-created routes are fully replaced when ``VIDIOC_SUBDEV_S_ROUTING`` is | |
| 595 | +called on the sub-device. Such newly created routes have the device's default | |
| 596 | +configuration for format and selection rectangles. | |
| 597 | + | |
| 573 | 598 | Configuring streams |
| 574 | 599 | ^^^^^^^^^^^^^^^^^^^ |
| 575 | 600 | |
| @@ -65,8 +65,8 @@ EBUSY · 64 unmodified lines | ||
| 65 | 65 | The driver does not support multiple opens and the device is already |
| 66 | 66 | in use. |
| 67 | 67 | |
| 68 | −ENXIO | |
| 69 | − No device corresponding to this device special file exists. | |
| 68 | +ENODEV | |
| 69 | + Device not found or was removed. | |
| 70 | 70 | |
| 71 | 71 | ENOMEM |
| 72 | 72 | Not enough kernel memory was available to complete the request. |
| @@ -13,9 +13,10 @@ These formats are used for the :ref:`metadata` interface only. · 12 unmodified lines | ||
| 13 | 13 | :maxdepth: 1 |
| 14 | 14 | |
| 15 | 15 | metafmt-d4xx |
| 16 | + metafmt-generic | |
| 16 | 17 | metafmt-intel-ipu3 |
| 17 | 18 | metafmt-rkisp1 |
| 18 | 19 | metafmt-uvc |
| 20 | + metafmt-vivid | |
| 19 | 21 | metafmt-vsp1-hgo |
| 20 | 22 | metafmt-vsp1-hgt |
| 21 | − metafmt-vivid | |
| @@ -0,0 +1,340 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later | |
| 2 | + | |
| 3 | +******************************************************************************************************************************************************************************************************************************************************************************** | |
| 4 | +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') | |
| 5 | +******************************************************************************************************************************************************************************************************************************************************************************** | |
| 6 | + | |
| 7 | + | |
| 8 | +Generic line-based metadata formats | |
| 9 | + | |
| 10 | + | |
| 11 | +Description | |
| 12 | +=========== | |
| 13 | + | |
| 14 | +These generic line-based metadata formats define the memory layout of the data | |
| 15 | +without defining the format or meaning of the metadata itself. | |
| 16 | + | |
| 17 | +.. _v4l2-meta-fmt-generic-8: | |
| 18 | + | |
| 19 | +V4L2_META_FMT_GENERIC_8 | |
| 20 | +----------------------- | |
| 21 | + | |
| 22 | +The V4L2_META_FMT_GENERIC_8 format is a plain 8-bit metadata format. This format | |
| 23 | +is used on CSI-2 for 8 bits per :term:`Data Unit`. | |
| 24 | + | |
| 25 | +Additionally it is used for 16 bits per Data Unit when two bytes of metadata are | |
| 26 | +packed into one 16-bit Data Unit. Otherwise the 16 bits per pixel dataformat is | |
| 27 | +:ref:`V4L2_META_FMT_GENERIC_CSI2_16 <v4l2-meta-fmt-generic-csi2-16>`. | |
| 28 | + | |
| 29 | +**Byte Order Of V4L2_META_FMT_GENERIC_8.** | |
| 30 | +Each cell is one byte. "M" denotes a byte of metadata. | |
| 31 | + | |
| 32 | +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}| | |
| 33 | + | |
| 34 | +.. flat-table:: Sample 4x2 Metadata Frame | |
| 35 | + :header-rows: 0 | |
| 36 | + :stub-columns: 0 | |
| 37 | + :widths: 12 8 8 8 8 | |
| 38 | + | |
| 39 | + * - start + 0: | |
| 40 | + - M\ :sub:`00` | |
| 41 | + - M\ :sub:`10` | |
| 42 | + - M\ :sub:`20` | |
| 43 | + - M\ :sub:`30` | |
| 44 | + * - start + 4: | |
| 45 | + - M\ :sub:`01` | |
| 46 | + - M\ :sub:`11` | |
| 47 | + - M\ :sub:`21` | |
| 48 | + - M\ :sub:`31` | |
| 49 | + | |
| 50 | +.. _v4l2-meta-fmt-generic-csi2-10: | |
| 51 | + | |
| 52 | +V4L2_META_FMT_GENERIC_CSI2_10 | |
| 53 | +----------------------------- | |
| 54 | + | |
| 55 | +V4L2_META_FMT_GENERIC_CSI2_10 contains 8-bit generic metadata packed in 10-bit | |
| 56 | +Data Units, with one padding byte after every four bytes of metadata. This | |
| 57 | +format is typically used by CSI-2 receivers with a source that transmits | |
| 58 | +MEDIA_BUS_FMT_META_10 and the CSI-2 receiver writes the received data to memory | |
| 59 | +as-is. | |
| 60 | + | |
| 61 | +The packing of the data follows the MIPI CSI-2 specification and the padding of | |
| 62 | +the data is defined in the MIPI CCS specification. | |
| 63 | + | |
| 64 | +This format is also used in conjunction with 20 bits per :term:`Data Unit` | |
| 65 | +formats that pack two bytes of metadata into one Data Unit. Otherwise the | |
| 66 | +20 bits per pixel dataformat is :ref:`V4L2_META_FMT_GENERIC_CSI2_20 | |
| 67 | +<v4l2-meta-fmt-generic-csi2-20>`. | |
| 68 | + | |
| 69 | +This format is little endian. | |
| 70 | + | |
| 71 | +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_10.** | |
| 72 | +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. | |
| 73 | + | |
| 74 | +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}| | |
| 75 | + | |
| 76 | +.. flat-table:: Sample 4x2 Metadata Frame | |
| 77 | + :header-rows: 0 | |
| 78 | + :stub-columns: 0 | |
| 79 | + :widths: 12 8 8 8 8 8 | |
| 80 | + | |
| 81 | + * - start + 0: | |
| 82 | + - M\ :sub:`00` | |
| 83 | + - M\ :sub:`10` | |
| 84 | + - M\ :sub:`20` | |
| 85 | + - M\ :sub:`30` | |
| 86 | + - x | |
| 87 | + * - start + 5: | |
| 88 | + - M\ :sub:`01` | |
| 89 | + - M\ :sub:`11` | |
| 90 | + - M\ :sub:`21` | |
| 91 | + - M\ :sub:`31` | |
| 92 | + - x | |
| 93 | + | |
| 94 | +.. _v4l2-meta-fmt-generic-csi2-12: | |
| 95 | + | |
| 96 | +V4L2_META_FMT_GENERIC_CSI2_12 | |
| 97 | +----------------------------- | |
| 98 | + | |
| 99 | +V4L2_META_FMT_GENERIC_CSI2_12 contains 8-bit generic metadata packed in 12-bit | |
| 100 | +Data Units, with one padding byte after every two bytes of metadata. This format | |
| 101 | +is typically used by CSI-2 receivers with a source that transmits | |
| 102 | +MEDIA_BUS_FMT_META_12 and the CSI-2 receiver writes the received data to memory | |
| 103 | +as-is. | |
| 104 | + | |
| 105 | +The packing of the data follows the MIPI CSI-2 specification and the padding of | |
| 106 | +the data is defined in the MIPI CCS specification. | |
| 107 | + | |
| 108 | +This format is also used in conjunction with 24 bits per :term:`Data Unit` | |
| 109 | +formats that pack two bytes of metadata into one Data Unit. Otherwise the | |
| 110 | +24 bits per pixel dataformat is :ref:`V4L2_META_FMT_GENERIC_CSI2_24 | |
| 111 | +<v4l2-meta-fmt-generic-csi2-24>`. | |
| 112 | + | |
| 113 | +This format is little endian. | |
| 114 | + | |
| 115 | +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_12.** | |
| 116 | +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. | |
| 117 | + | |
| 118 | +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}| | |
| 119 | + | |
| 120 | +.. flat-table:: Sample 4x2 Metadata Frame | |
| 121 | + :header-rows: 0 | |
| 122 | + :stub-columns: 0 | |
| 123 | + :widths: 12 8 8 8 8 8 8 | |
| 124 | + | |
| 125 | + * - start + 0: | |
| 126 | + - M\ :sub:`00` | |
| 127 | + - M\ :sub:`10` | |
| 128 | + - x | |
| 129 | + - M\ :sub:`20` | |
| 130 | + - M\ :sub:`30` | |
| 131 | + - x | |
| 132 | + * - start + 6: | |
| 133 | + - M\ :sub:`01` | |
| 134 | + - M\ :sub:`11` | |
| 135 | + - x | |
| 136 | + - M\ :sub:`21` | |
| 137 | + - M\ :sub:`31` | |
| 138 | + - x | |
| 139 | + | |
| 140 | +.. _v4l2-meta-fmt-generic-csi2-14: | |
| 141 | + | |
| 142 | +V4L2_META_FMT_GENERIC_CSI2_14 | |
| 143 | +----------------------------- | |
| 144 | + | |
| 145 | +V4L2_META_FMT_GENERIC_CSI2_14 contains 8-bit generic metadata packed in 14-bit | |
| 146 | +Data Units, with three padding bytes after every four bytes of metadata. This | |
| 147 | +format is typically used by CSI-2 receivers with a source that transmits | |
| 148 | +MEDIA_BUS_FMT_META_14 and the CSI-2 receiver writes the received data to memory | |
| 149 | +as-is. | |
| 150 | + | |
| 151 | +The packing of the data follows the MIPI CSI-2 specification and the padding of | |
| 152 | +the data is defined in the MIPI CCS specification. | |
| 153 | + | |
| 154 | +This format is little endian. | |
| 155 | + | |
| 156 | +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_14.** | |
| 157 | +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. | |
| 158 | + | |
| 159 | +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{.8cm}| | |
| 160 | + | |
| 161 | +.. flat-table:: Sample 4x2 Metadata Frame | |
| 162 | + :header-rows: 0 | |
| 163 | + :stub-columns: 0 | |
| 164 | + :widths: 12 8 8 8 8 8 8 8 | |
| 165 | + | |
| 166 | + * - start + 0: | |
| 167 | + - M\ :sub:`00` | |
| 168 | + - M\ :sub:`10` | |
| 169 | + - M\ :sub:`20` | |
| 170 | + - M\ :sub:`30` | |
| 171 | + - x | |
| 172 | + - x | |
| 173 | + - x | |
| 174 | + * - start + 7: | |
| 175 | + - M\ :sub:`01` | |
| 176 | + - M\ :sub:`11` | |
| 177 | + - M\ :sub:`21` | |
| 178 | + - M\ :sub:`31` | |
| 179 | + - x | |
| 180 | + - x | |
| 181 | + - x | |
| 182 | + | |
| 183 | +.. _v4l2-meta-fmt-generic-csi2-16: | |
| 184 | + | |
| 185 | +V4L2_META_FMT_GENERIC_CSI2_16 | |
| 186 | +----------------------------- | |
| 187 | + | |
| 188 | +V4L2_META_FMT_GENERIC_CSI2_16 contains 8-bit generic metadata packed in 16-bit | |
| 189 | +Data Units, with one padding byte after every byte of metadata. This format is | |
| 190 | +typically used by CSI-2 receivers with a source that transmits | |
| 191 | +MEDIA_BUS_FMT_META_16 and the CSI-2 receiver writes the received data to memory | |
| 192 | +as-is. | |
| 193 | + | |
| 194 | +The packing of the data follows the MIPI CSI-2 specification and the padding of | |
| 195 | +the data is defined in the MIPI CCS specification. | |
| 196 | + | |
| 197 | +Some devices support more efficient packing of metadata in conjunction with | |
| 198 | +16-bit image data. In that case the dataformat is | |
| 199 | +:ref:`V4L2_META_FMT_GENERIC_8 <v4l2-meta-fmt-generic-8>`. | |
| 200 | + | |
| 201 | +This format is little endian. | |
| 202 | + | |
| 203 | +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_16.** | |
| 204 | +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. | |
| 205 | + | |
| 206 | +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}| | |
| 207 | + | |
| 208 | +.. flat-table:: Sample 4x2 Metadata Frame | |
| 209 | + :header-rows: 0 | |
| 210 | + :stub-columns: 0 | |
| 211 | + :widths: 12 8 8 8 8 8 8 8 8 | |
| 212 | + | |
| 213 | + * - start + 0: | |
| 214 | + - M\ :sub:`00` | |
| 215 | + - x | |
| 216 | + - M\ :sub:`10` | |
| 217 | + - x | |
| 218 | + - M\ :sub:`20` | |
| 219 | + - x | |
| 220 | + - M\ :sub:`30` | |
| 221 | + - x | |
| 222 | + * - start + 8: | |
| 223 | + - M\ :sub:`01` | |
| 224 | + - x | |
| 225 | + - M\ :sub:`11` | |
| 226 | + - x | |
| 227 | + - M\ :sub:`21` | |
| 228 | + - x | |
| 229 | + - M\ :sub:`31` | |
| 230 | + - x | |
| 231 | + | |
| 232 | +.. _v4l2-meta-fmt-generic-csi2-20: | |
| 233 | + | |
| 234 | +V4L2_META_FMT_GENERIC_CSI2_20 | |
| 235 | +----------------------------- | |
| 236 | + | |
| 237 | +V4L2_META_FMT_GENERIC_CSI2_20 contains 8-bit generic metadata packed in 20-bit | |
| 238 | +Data Units, with alternating one or two padding bytes after every byte of | |
| 239 | +metadata. This format is typically used by CSI-2 receivers with a source that | |
| 240 | +transmits MEDIA_BUS_FMT_META_20 and the CSI-2 receiver writes the received data | |
| 241 | +to memory as-is. | |
| 242 | + | |
| 243 | +The packing of the data follows the MIPI CSI-2 specification and the padding of | |
| 244 | +the data is defined in the MIPI CCS specification. | |
| 245 | + | |
| 246 | +Some devices support more efficient packing of metadata in conjunction with | |
| 247 | +16-bit image data. In that case the dataformat is | |
| 248 | +:ref:`V4L2_META_FMT_GENERIC_CSI2_10 <v4l2-meta-fmt-generic-csi2-10>`. | |
| 249 | + | |
| 250 | +This format is little endian. | |
| 251 | + | |
| 252 | +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_20.** | |
| 253 | +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. | |
| 254 | + | |
| 255 | +.. 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}| | |
| 256 | + | |
| 257 | +.. flat-table:: Sample 4x2 Metadata Frame | |
| 258 | + :header-rows: 0 | |
| 259 | + :stub-columns: 0 | |
| 260 | + :widths: 12 8 8 8 8 8 8 8 8 8 8 | |
| 261 | + | |
| 262 | + * - start + 0: | |
| 263 | + - M\ :sub:`00` | |
| 264 | + - x | |
| 265 | + - M\ :sub:`10` | |
| 266 | + - x | |
| 267 | + - x | |
| 268 | + - M\ :sub:`20` | |
| 269 | + - x | |
| 270 | + - M\ :sub:`30` | |
| 271 | + - x | |
| 272 | + - x | |
| 273 | + * - start + 10: | |
| 274 | + - M\ :sub:`01` | |
| 275 | + - x | |
| 276 | + - M\ :sub:`11` | |
| 277 | + - x | |
| 278 | + - x | |
| 279 | + - M\ :sub:`21` | |
| 280 | + - x | |
| 281 | + - M\ :sub:`31` | |
| 282 | + - x | |
| 283 | + - x | |
| 284 | + | |
| 285 | +.. _v4l2-meta-fmt-generic-csi2-24: | |
| 286 | + | |
| 287 | +V4L2_META_FMT_GENERIC_CSI2_24 | |
| 288 | +----------------------------- | |
| 289 | + | |
| 290 | +V4L2_META_FMT_GENERIC_CSI2_24 contains 8-bit generic metadata packed in 24-bit | |
| 291 | +Data Units, with two padding bytes after every byte of metadata. This format is | |
| 292 | +typically used by CSI-2 receivers with a source that transmits | |
| 293 | +MEDIA_BUS_FMT_META_24 and the CSI-2 receiver writes the received data to memory | |
| 294 | +as-is. | |
| 295 | + | |
| 296 | +The packing of the data follows the MIPI CSI-2 specification and the padding of | |
| 297 | +the data is defined in the MIPI CCS specification. | |
| 298 | + | |
| 299 | +Some devices support more efficient packing of metadata in conjunction with | |
| 300 | +16-bit image data. In that case the dataformat is | |
| 301 | +:ref:`V4L2_META_FMT_GENERIC_CSI2_12 <v4l2-meta-fmt-generic-csi2-12>`. | |
| 302 | + | |
| 303 | +This format is little endian. | |
| 304 | + | |
| 305 | +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_24.** | |
| 306 | +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. | |
| 307 | + | |
| 308 | +.. 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}| | |
| 309 | + | |
| 310 | +.. flat-table:: Sample 4x2 Metadata Frame | |
| 311 | + :header-rows: 0 | |
| 312 | + :stub-columns: 0 | |
| 313 | + :widths: 12 8 8 8 8 8 8 8 8 8 8 8 8 | |
| 314 | + | |
| 315 | + * - start + 0: | |
| 316 | + - M\ :sub:`00` | |
| 317 | + - x | |
| 318 | + - x | |
| 319 | + - M\ :sub:`10` | |
| 320 | + - x | |
| 321 | + - x | |
| 322 | + - M\ :sub:`20` | |
| 323 | + - x | |
| 324 | + - x | |
| 325 | + - M\ :sub:`30` | |
| 326 | + - x | |
| 327 | + - x | |
| 328 | + * - start + 12: | |
| 329 | + - M\ :sub:`01` | |
| 330 | + - x | |
| 331 | + - x | |
| 332 | + - M\ :sub:`11` | |
| 333 | + - x | |
| 334 | + - x | |
| 335 | + - M\ :sub:`21` | |
| 336 | + - x | |
| 337 | + - x | |
| 338 | + - M\ :sub:`31` | |
| 339 | + - x | |
| 340 | + - x | |
| @@ -188,7 +188,7 @@ Example: Mapping buffers in the multi-planar API · 187 unmodified lines | ||
| 188 | 188 | buffers[i].start[j] = mmap(NULL, buffer.m.planes[j].length, |
| 189 | 189 | PROT_READ | PROT_WRITE, /* recommended */ |
| 190 | 190 | MAP_SHARED, /* recommended */ |
| 191 | − fd, buffer.m.planes[j].m.offset); | |
| 191 | + fd, buffer.m.planes[j].m.mem_offset); | |
| 192 | 192 | |
| 193 | 193 | if (MAP_FAILED == buffers[i].start[j]) { |
| 194 | 194 | /* If you do not exit here you should unmap() and free() |
| @@ -36,6 +36,8 @@ are often referred to as greyscale formats. · 35 unmodified lines | ||
| 36 | 36 | - Byte 2 |
| 37 | 37 | - Byte 3 |
| 38 | 38 | - Byte 4 |
| 39 | + - Byte 5 | |
| 40 | + - Byte 6 | |
| 39 | 41 | |
| 40 | 42 | * .. _V4L2-PIX-FMT-GREY: |
| 41 | 43 | |
| @@ -47,6 +49,8 @@ are often referred to as greyscale formats. · 5 unmodified lines | ||
| 47 | 49 | - ... |
| 48 | 50 | - ... |
| 49 | 51 | - ... |
| 52 | + - ... | |
| 53 | + - ... | |
| 50 | 54 | |
| 51 | 55 | * .. _V4L2-PIX-FMT-IPU3-Y10: |
| 52 | 56 | |
| @@ -58,6 +62,8 @@ are often referred to as greyscale formats. · 5 unmodified lines | ||
| 58 | 62 | - Y'\ :sub:`2`\ [3:0] Y'\ :sub:`1`\ [9:6] |
| 59 | 63 | - Y'\ :sub:`3`\ [1:0] Y'\ :sub:`2`\ [9:4] |
| 60 | 64 | - Y'\ :sub:`3`\ [9:2] |
| 65 | + - ... | |
| 66 | + - ... | |
| 61 | 67 | |
| 62 | 68 | * .. _V4L2-PIX-FMT-Y10: |
| 63 | 69 | |
| @@ -69,6 +75,8 @@ are often referred to as greyscale formats. · 5 unmodified lines | ||
| 69 | 75 | - ... |
| 70 | 76 | - ... |
| 71 | 77 | - ... |
| 78 | + - ... | |
| 79 | + - ... | |
| 72 | 80 | |
| 73 | 81 | * .. _V4L2-PIX-FMT-Y10BPACK: |
| 74 | 82 | |
| @@ -80,6 +88,8 @@ are often referred to as greyscale formats. · 5 unmodified lines | ||
| 80 | 88 | - Y'\ :sub:`1`\ [3:0] Y'\ :sub:`2`\ [9:6] |
| 81 | 89 | - Y'\ :sub:`2`\ [5:0] Y'\ :sub:`3`\ [9:8] |
| 82 | 90 | - Y'\ :sub:`3`\ [7:0] |
| 91 | + - ... | |
| 92 | + - ... | |
| 83 | 93 | |
| 84 | 94 | * .. _V4L2-PIX-FMT-Y10P: |
| 85 | 95 | |
| @@ -91,6 +101,8 @@ are often referred to as greyscale formats. · 5 unmodified lines | ||
| 91 | 101 | - Y'\ :sub:`2`\ [9:2] |
| 92 | 102 | - Y'\ :sub:`3`\ [9:2] |
| 93 | 103 | - Y'\ :sub:`3`\ [1:0] Y'\ :sub:`2`\ [1:0] Y'\ :sub:`1`\ [1:0] Y'\ :sub:`0`\ [1:0] |
| 104 | + - ... | |
| 105 | + - ... | |
| 94 | 106 | |
| 95 | 107 | * .. _V4L2-PIX-FMT-Y12: |
| 96 | 108 | |
| @@ -102,6 +114,8 @@ are often referred to as greyscale formats. · 5 unmodified lines | ||
| 102 | 114 | - ... |
| 103 | 115 | - ... |
| 104 | 116 | - ... |
| 117 | + - ... | |
| 118 | + - ... | |
| 105 | 119 | |
| 106 | 120 | * .. _V4L2-PIX-FMT-Y012: |
| 107 | 121 | |
| @@ -113,6 +127,21 @@ are often referred to as greyscale formats. · 5 unmodified lines | ||
| 113 | 127 | - ... |
| 114 | 128 | - ... |
| 115 | 129 | - ... |
| 130 | + - ... | |
| 131 | + - ... | |
| 132 | + | |
| 133 | + * .. _V4L2-PIX-FMT-Y12P: | |
| 134 | + | |
| 135 | + - ``V4L2_PIX_FMT_Y12P`` | |
| 136 | + - 'Y12P' | |
| 137 | + | |
| 138 | + - Y'\ :sub:`0`\ [11:4] | |
| 139 | + - Y'\ :sub:`1`\ [11:4] | |
| 140 | + - Y'\ :sub:`1`\ [3:0] Y'\ :sub:`0`\ [3:0] | |
| 141 | + - ... | |
| 142 | + - ... | |
| 143 | + - ... | |
| 144 | + - ... | |
| 116 | 145 | |
| 117 | 146 | * .. _V4L2-PIX-FMT-Y14: |
| 118 | 147 | |
| @@ -124,6 +153,21 @@ are often referred to as greyscale formats. · 5 unmodified lines | ||
| 124 | 153 | - ... |
| 125 | 154 | - ... |
| 126 | 155 | - ... |
| 156 | + - ... | |
| 157 | + - ... | |
| 158 | + | |
| 159 | + * .. _V4L2-PIX-FMT-Y14P: | |
| 160 | + | |
| 161 | + - ``V4L2_PIX_FMT_Y14P`` | |
| 162 | + - 'Y14P' | |
| 163 | + | |
| 164 | + - Y'\ :sub:`0`\ [13:6] | |
| 165 | + - Y'\ :sub:`1`\ [13:6] | |
| 166 | + - Y'\ :sub:`2`\ [13:6] | |
| 167 | + - Y'\ :sub:`3`\ [13:6] | |
| 168 | + - Y'\ :sub:`1`\ [1:0] Y'\ :sub:`0`\ [5:0] | |
| 169 | + - Y'\ :sub:`2`\ [3:0] Y'\ :sub:`1`\ [5:2] | |
| 170 | + - Y'\ :sub:`3`\ [5:0] Y'\ :sub:`2`\ [5:4] | |
| 127 | 171 | |
| 128 | 172 | * .. _V4L2-PIX-FMT-Y16: |
| 129 | 173 | |
| @@ -135,6 +179,8 @@ are often referred to as greyscale formats. · 5 unmodified lines | ||
| 135 | 179 | - ... |
| 136 | 180 | - ... |
| 137 | 181 | - ... |
| 182 | + - ... | |
| 183 | + - ... | |
| 138 | 184 | |
| 139 | 185 | * .. _V4L2-PIX-FMT-Y16-BE: |
| 140 | 186 | |
| @@ -146,6 +192,8 @@ are often referred to as greyscale formats. · 5 unmodified lines | ||
| 146 | 192 | - ... |
| 147 | 193 | - ... |
| 148 | 194 | - ... |
| 195 | + - ... | |
| 196 | + - ... | |
| 149 | 197 | |
| 150 | 198 | .. raw:: latex |
| 151 | 199 | |
| @@ -33,7 +33,7 @@ Media Bus Formats · 32 unmodified lines | ||
| 33 | 33 | * - __u32 |
| 34 | 34 | - ``field`` |
| 35 | 35 | - Field order, from enum :c:type:`v4l2_field`. See |
| 36 | − :ref:`field-order` for details. | |
| 36 | + :ref:`field-order` for details. Zero for metadata mbus codes. | |
| 37 | 37 | * - __u32 |
| 38 | 38 | - ``colorspace`` |
| 39 | 39 | - Image colorspace, from enum :c:type:`v4l2_colorspace`. |
| @@ -45,7 +45,7 @@ Media Bus Formats · 5 unmodified lines | ||
| 45 | 45 | conversion is supported by setting the flag |
| 46 | 46 | V4L2_SUBDEV_MBUS_CODE_CSC_COLORSPACE in the corresponding struct |
| 47 | 47 | :c:type:`v4l2_subdev_mbus_code_enum` during enumeration. |
| 48 | − See :ref:`v4l2-subdev-mbus-code-flags`. | |
| 48 | + See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes. | |
| 49 | 49 | * - union { |
| 50 | 50 | - (anonymous) |
| 51 | 51 | * - __u16 |
| @@ -61,7 +61,7 @@ Media Bus Formats · 9 unmodified lines | ||
| 61 | 61 | that ycbcr_enc conversion is supported by setting the flag |
| 62 | 62 | V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC in the corresponding struct |
| 63 | 63 | :c:type:`v4l2_subdev_mbus_code_enum` during enumeration. |
| 64 | − See :ref:`v4l2-subdev-mbus-code-flags`. | |
| 64 | + See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes. | |
| 65 | 65 | * - __u16 |
| 66 | 66 | - ``hsv_enc`` |
| 67 | 67 | - HSV encoding, from enum :c:type:`v4l2_hsv_encoding`. |
| @@ -75,7 +75,7 @@ Media Bus Formats · 7 unmodified lines | ||
| 75 | 75 | that hsv_enc conversion is supported by setting the flag |
| 76 | 76 | V4L2_SUBDEV_MBUS_CODE_CSC_HSV_ENC in the corresponding struct |
| 77 | 77 | :c:type:`v4l2_subdev_mbus_code_enum` during enumeration. |
| 78 | − See :ref:`v4l2-subdev-mbus-code-flags` | |
| 78 | + See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes. | |
| 79 | 79 | * - } |
| 80 | 80 | - |
| 81 | 81 | * - __u16 |
| @@ -90,8 +90,8 @@ Media Bus Formats · 8 unmodified lines | ||
| 90 | 90 | The driver indicates that quantization conversion is supported by |
| 91 | 91 | setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION in the |
| 92 | 92 | corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` |
| 93 | − during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. | |
| 94 | − | |
| 93 | + during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for | |
| 94 | + metadata mbus codes. | |
| 95 | 95 | * - __u16 |
| 96 | 96 | - ``xfer_func`` |
| 97 | 97 | - Transfer function, from enum :c:type:`v4l2_xfer_func`. |
| @@ -104,7 +104,8 @@ Media Bus Formats · 6 unmodified lines | ||
| 104 | 104 | The driver indicates that the transfer function conversion is supported by |
| 105 | 105 | setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_XFER_FUNC in the |
| 106 | 106 | corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` |
| 107 | − during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. | |
| 107 | + during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for | |
| 108 | + metadata mbus codes. | |
| 108 | 109 | * - __u16 |
| 109 | 110 | - ``flags`` |
| 110 | 111 | - flags See: :ref:v4l2-mbus-framefmt-flags |
| @@ -8306,3 +8307,257 @@ The following table lists the existing metadata formats. · 8195 unmodified lines | ||
| 8306 | 8307 | both sides of the link and the bus format is a fixed |
| 8307 | 8308 | metadata format that is not configurable from userspace. |
| 8308 | 8309 | Width and height will be set to 0 for this format. |
| 8310 | + | |
| 8311 | +Generic Serial Metadata Formats | |
| 8312 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 8313 | + | |
| 8314 | +Generic serial metadata formats are used on serial buses where the actual data | |
| 8315 | +content is more or less device specific but the data is transmitted and received | |
| 8316 | +by multiple devices that do not process the data in any way, simply writing | |
| 8317 | +it to system memory for processing in software at the end of the pipeline. | |
| 8318 | + | |
| 8319 | +"b" in an array cell signifies a byte of data, followed by the number of the bit | |
| 8320 | +and finally the bit number in subscript. "x" indicates a padding bit. | |
| 8321 | + | |
| 8322 | +.. _media-bus-format-generic-meta: | |
| 8323 | + | |
| 8324 | +.. cssclass: longtable | |
| 8325 | + | |
| 8326 | +.. flat-table:: Generic Serial Metadata Formats | |
| 8327 | + :header-rows: 2 | |
| 8328 | + :stub-columns: 0 | |
| 8329 | + | |
| 8330 | + * - Identifier | |
| 8331 | + - Code | |
| 8332 | + - | |
| 8333 | + - :cspan:`23` Data organization within bus :term:`Data Unit` | |
| 8334 | + * - | |
| 8335 | + - | |
| 8336 | + - Bit | |
| 8337 | + - 23 | |
| 8338 | + - 22 | |
| 8339 | + - 21 | |
| 8340 | + - 20 | |
| 8341 | + - 19 | |
| 8342 | + - 18 | |
| 8343 | + - 17 | |
| 8344 | + - 16 | |
| 8345 | + - 15 | |
| 8346 | + - 14 | |
| 8347 | + - 13 | |
| 8348 | + - 12 | |
| 8349 | + - 11 | |
| 8350 | + - 10 | |
| 8351 | + - 9 | |
| 8352 | + - 8 | |
| 8353 | + - 7 | |
| 8354 | + - 6 | |
| 8355 | + - 5 | |
| 8356 | + - 4 | |
| 8357 | + - 3 | |
| 8358 | + - 2 | |
| 8359 | + - 1 | |
| 8360 | + - 0 | |
| 8361 | + * .. _MEDIA-BUS-FMT-META-8: | |
| 8362 | + | |
| 8363 | + - MEDIA_BUS_FMT_META_8 | |
| 8364 | + - 0x8001 | |
| 8365 | + - | |
| 8366 | + - | |
| 8367 | + - | |
| 8368 | + - | |
| 8369 | + - | |
| 8370 | + - | |
| 8371 | + - | |
| 8372 | + - | |
| 8373 | + - | |
| 8374 | + - | |
| 8375 | + - | |
| 8376 | + - | |
| 8377 | + - | |
| 8378 | + - | |
| 8379 | + - | |
| 8380 | + - | |
| 8381 | + - | |
| 8382 | + - b0\ :sub:`7` | |
| 8383 | + - b0\ :sub:`6` | |
| 8384 | + - b0\ :sub:`5` | |
| 8385 | + - b0\ :sub:`4` | |
| 8386 | + - b0\ :sub:`3` | |
| 8387 | + - b0\ :sub:`2` | |
| 8388 | + - b0\ :sub:`1` | |
| 8389 | + - b0\ :sub:`0` | |
| 8390 | + * .. _MEDIA-BUS-FMT-META-10: | |
| 8391 | + | |
| 8392 | + - MEDIA_BUS_FMT_META_10 | |
| 8393 | + - 0x8002 | |
| 8394 | + - | |
| 8395 | + - | |
| 8396 | + - | |
| 8397 | + - | |
| 8398 | + - | |
| 8399 | + - | |
| 8400 | + - | |
| 8401 | + - | |
| 8402 | + - | |
| 8403 | + - | |
| 8404 | + - | |
| 8405 | + - | |
| 8406 | + - | |
| 8407 | + - | |
| 8408 | + - | |
| 8409 | + - b0\ :sub:`7` | |
| 8410 | + - b0\ :sub:`6` | |
| 8411 | + - b0\ :sub:`5` | |
| 8412 | + - b0\ :sub:`4` | |
| 8413 | + - b0\ :sub:`3` | |
| 8414 | + - b0\ :sub:`2` | |
| 8415 | + - b0\ :sub:`1` | |
| 8416 | + - b0\ :sub:`0` | |
| 8417 | + - x | |
| 8418 | + - x | |
| 8419 | + * .. _MEDIA-BUS-FMT-META-12: | |
| 8420 | + | |
| 8421 | + - MEDIA_BUS_FMT_META_12 | |
| 8422 | + - 0x8003 | |
| 8423 | + - | |
| 8424 | + - | |
| 8425 | + - | |
| 8426 | + - | |
| 8427 | + - | |
| 8428 | + - | |
| 8429 | + - | |
| 8430 | + - | |
| 8431 | + - | |
| 8432 | + - | |
| 8433 | + - | |
| 8434 | + - | |
| 8435 | + - | |
| 8436 | + - b0\ :sub:`7` | |
| 8437 | + - b0\ :sub:`6` | |
| 8438 | + - b0\ :sub:`5` | |
| 8439 | + - b0\ :sub:`4` | |
| 8440 | + - b0\ :sub:`3` | |
| 8441 | + - b0\ :sub:`2` | |
| 8442 | + - b0\ :sub:`1` | |
| 8443 | + - b0\ :sub:`0` | |
| 8444 | + - x | |
| 8445 | + - x | |
| 8446 | + - x | |
| 8447 | + - x | |
| 8448 | + * .. _MEDIA-BUS-FMT-META-14: | |
| 8449 | + | |
| 8450 | + - MEDIA_BUS_FMT_META_14 | |
| 8451 | + - 0x8004 | |
| 8452 | + - | |
| 8453 | + - | |
| 8454 | + - | |
| 8455 | + - | |
| 8456 | + - | |
| 8457 | + - | |
| 8458 | + - | |
| 8459 | + - | |
| 8460 | + - | |
| 8461 | + - | |
| 8462 | + - | |
| 8463 | + - b0\ :sub:`7` | |
| 8464 | + - b0\ :sub:`6` | |
| 8465 | + - b0\ :sub:`5` | |
| 8466 | + - b0\ :sub:`4` | |
| 8467 | + - b0\ :sub:`3` | |
| 8468 | + - b0\ :sub:`2` | |
| 8469 | + - b0\ :sub:`1` | |
| 8470 | + - b0\ :sub:`0` | |
| 8471 | + - x | |
| 8472 | + - x | |
| 8473 | + - x | |
| 8474 | + - x | |
| 8475 | + - x | |
| 8476 | + - x | |
| 8477 | + * .. _MEDIA-BUS-FMT-META-16: | |
| 8478 | + | |
| 8479 | + - MEDIA_BUS_FMT_META_16 | |
| 8480 | + - 0x8005 | |
| 8481 | + - | |
| 8482 | + - | |
| 8483 | + - | |
| 8484 | + - | |
| 8485 | + - | |
| 8486 | + - | |
| 8487 | + - | |
| 8488 | + - | |
| 8489 | + - | |
| 8490 | + - b0\ :sub:`7` | |
| 8491 | + - b0\ :sub:`6` | |
| 8492 | + - b0\ :sub:`5` | |
| 8493 | + - b0\ :sub:`4` | |
| 8494 | + - b0\ :sub:`3` | |
| 8495 | + - b0\ :sub:`2` | |
| 8496 | + - b0\ :sub:`1` | |
| 8497 | + - b0\ :sub:`0` | |
| 8498 | + - x | |
| 8499 | + - x | |
| 8500 | + - x | |
| 8501 | + - x | |
| 8502 | + - x | |
| 8503 | + - x | |
| 8504 | + - x | |
| 8505 | + - x | |
| 8506 | + * .. _MEDIA-BUS-FMT-META-20: | |
| 8507 | + | |
| 8508 | + - MEDIA_BUS_FMT_META_20 | |
| 8509 | + - 0x8006 | |
| 8510 | + - | |
| 8511 | + - | |
| 8512 | + - | |
| 8513 | + - | |
| 8514 | + - | |
| 8515 | + - b0\ :sub:`7` | |
| 8516 | + - b0\ :sub:`6` | |
| 8517 | + - b0\ :sub:`5` | |
| 8518 | + - b0\ :sub:`4` | |
| 8519 | + - b0\ :sub:`3` | |
| 8520 | + - b0\ :sub:`2` | |
| 8521 | + - b0\ :sub:`1` | |
| 8522 | + - b0\ :sub:`0` | |
| 8523 | + - x | |
| 8524 | + - x | |
| 8525 | + - x | |
| 8526 | + - x | |
| 8527 | + - x | |
| 8528 | + - x | |
| 8529 | + - x | |
| 8530 | + - x | |
| 8531 | + - x | |
| 8532 | + - x | |
| 8533 | + - x | |
| 8534 | + - x | |
| 8535 | + * .. _MEDIA-BUS-FMT-META-24: | |
| 8536 | + | |
| 8537 | + - MEDIA_BUS_FMT_META_24 | |
| 8538 | + - 0x8007 | |
| 8539 | + - | |
| 8540 | + - b0\ :sub:`7` | |
| 8541 | + - b0\ :sub:`6` | |
| 8542 | + - b0\ :sub:`5` | |
| 8543 | + - b0\ :sub:`4` | |
| 8544 | + - b0\ :sub:`3` | |
| 8545 | + - b0\ :sub:`2` | |
| 8546 | + - b0\ :sub:`1` | |
| 8547 | + - b0\ :sub:`0` | |
| 8548 | + - x | |
| 8549 | + - x | |
| 8550 | + - x | |
| 8551 | + - x | |
| 8552 | + - x | |
| 8553 | + - x | |
| 8554 | + - x | |
| 8555 | + - x | |
| 8556 | + - x | |
| 8557 | + - x | |
| 8558 | + - x | |
| 8559 | + - x | |
| 8560 | + - x | |
| 8561 | + - x | |
| 8562 | + - x | |
| 8563 | + - x | |
| @@ -62,6 +62,7 @@ Function Reference · 61 unmodified lines | ||
| 62 | 62 | vidioc-query-dv-timings |
| 63 | 63 | vidioc-querystd |
| 64 | 64 | vidioc-reqbufs |
| 65 | + vidioc-remove-bufs | |
| 65 | 66 | vidioc-s-hw-freq-seek |
| 66 | 67 | vidioc-streamon |
| 67 | 68 | vidioc-subdev-enum-frame-interval |
| @@ -227,6 +227,13 @@ the ``mbus_code`` field is handled differently: · 226 unmodified lines | ||
| 227 | 227 | The application can ask to configure the quantization of the capture |
| 228 | 228 | device when calling the :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl with |
| 229 | 229 | :ref:`V4L2_PIX_FMT_FLAG_SET_CSC <v4l2-pix-fmt-flag-set-csc>` set. |
| 230 | + * - ``V4L2_FMT_FLAG_META_LINE_BASED`` | |
| 231 | + - 0x0200 | |
| 232 | + - The metadata format is line-based. In this case the ``width``, | |
| 233 | + ``height`` and ``bytesperline`` fields of :c:type:`v4l2_meta_format` are | |
| 234 | + valid. The buffer consists of ``height`` lines, each having ``width`` | |
| 235 | + Data Units of data and the offset (in bytes) between the beginning of | |
| 236 | + each two consecutive lines is ``bytesperline``. | |
| 230 | 237 | |
| 231 | 238 | Return Value |
| 232 | 239 | ============ |
| @@ -0,0 +1,86 @@ | ||
| 1 | +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later | |
| 2 | +.. c:namespace:: V4L | |
| 3 | + | |
| 4 | +.. _VIDIOC_REMOVE_BUFS: | |
| 5 | + | |
| 6 | +************************ | |
| 7 | +ioctl VIDIOC_REMOVE_BUFS | |
| 8 | +************************ | |
| 9 | + | |
| 10 | +Name | |
| 11 | +==== | |
| 12 | + | |
| 13 | +VIDIOC_REMOVE_BUFS - Removes buffers from a queue | |
| 14 | + | |
| 15 | +Synopsis | |
| 16 | +======== | |
| 17 | + | |
| 18 | +.. c:macro:: VIDIOC_REMOVE_BUFS | |
| 19 | + | |
| 20 | +``int ioctl(int fd, VIDIOC_REMOVE_BUFS, struct v4l2_remove_buffers *argp)`` | |
| 21 | + | |
| 22 | +Arguments | |
| 23 | +========= | |
| 24 | + | |
| 25 | +``fd`` | |
| 26 | + File descriptor returned by :c:func:`open()`. | |
| 27 | + | |
| 28 | +``argp`` | |
| 29 | + Pointer to struct :c:type:`v4l2_remove_buffers`. | |
| 30 | + | |
| 31 | +Description | |
| 32 | +=========== | |
| 33 | + | |
| 34 | +Applications can optionally call the :ref:`VIDIOC_REMOVE_BUFS` ioctl to | |
| 35 | +remove buffers from a queue. | |
| 36 | +:ref:`VIDIOC_CREATE_BUFS` ioctl support is mandatory to enable :ref:`VIDIOC_REMOVE_BUFS`. | |
| 37 | +This ioctl is available if the ``V4L2_BUF_CAP_SUPPORTS_REMOVE_BUFS`` capability | |
| 38 | +is set on the queue when :c:func:`VIDIOC_REQBUFS` or :c:func:`VIDIOC_CREATE_BUFS` | |
| 39 | +are invoked. | |
| 40 | + | |
| 41 | +.. c:type:: v4l2_remove_buffers | |
| 42 | + | |
| 43 | +.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.5cm}| | |
| 44 | + | |
| 45 | +.. flat-table:: struct v4l2_remove_buffers | |
| 46 | + :header-rows: 0 | |
| 47 | + :stub-columns: 0 | |
| 48 | + :widths: 1 1 2 | |
| 49 | + | |
| 50 | + * - __u32 | |
| 51 | + - ``index`` | |
| 52 | + - The starting buffer index to remove. This field is ignored if count == 0. | |
| 53 | + * - __u32 | |
| 54 | + - ``count`` | |
| 55 | + - The number of buffers to be removed with indices 'index' until 'index + count - 1'. | |
| 56 | + All buffers in this range must be valid and in DEQUEUED state. | |
| 57 | + :ref:`VIDIOC_REMOVE_BUFS` will always check the validity of ``type`, if it is | |
| 58 | + invalid it returns ``EINVAL`` error code. | |
| 59 | + If count is set to 0 :ref:`VIDIOC_REMOVE_BUFS` will do nothing and return 0. | |
| 60 | + * - __u32 | |
| 61 | + - ``type`` | |
| 62 | + - Type of the stream or buffers, this is the same as the struct | |
| 63 | + :c:type:`v4l2_format` ``type`` field. See | |
| 64 | + :c:type:`v4l2_buf_type` for valid values. | |
| 65 | + * - __u32 | |
| 66 | + - ``reserved``\ [13] | |
| 67 | + - A place holder for future extensions. Drivers and applications | |
| 68 | + must set the array to zero. | |
| 69 | + | |
| 70 | +Return Value | |
| 71 | +============ | |
| 72 | + | |
| 73 | +On success 0 is returned, on error -1 and the ``errno`` variable is set | |
| 74 | +appropriately. The generic error codes are described at the | |
| 75 | +:ref:`Generic Error Codes <gen-errors>` chapter. If an error occurs, no | |
| 76 | +buffers will be freed and one of the error codes below will be returned: | |
| 77 | + | |
| 78 | +EBUSY | |
| 79 | + File I/O is in progress. | |
| 80 | + One or more of the buffers in the range ``index`` to ``index + count - 1`` are not | |
| 81 | + in DEQUEUED state. | |
| 82 | + | |
| 83 | +EINVAL | |
| 84 | + One or more of the buffers in the range ``index`` to ``index + count - 1`` do not | |
| 85 | + exist in the queue. | |
| 86 | + The buffer type (``type`` field) is not valid. | |
| @@ -121,6 +121,7 @@ aborting or finishing any DMA in progress, an implicit · 120 unmodified lines | ||
| 121 | 121 | .. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF: |
| 122 | 122 | .. _V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS: |
| 123 | 123 | .. _V4L2-BUF-CAP-SUPPORTS-MAX-NUM-BUFFERS: |
| 124 | +.. _V4L2-BUF-CAP-SUPPORTS-REMOVE-BUFS: | |
| 124 | 125 | |
| 125 | 126 | .. raw:: latex |
| 126 | 127 | |
| @@ -37,9 +37,9 @@ Description · 36 unmodified lines | ||
| 37 | 37 | |
| 38 | 38 | .. note:: |
| 39 | 39 | |
| 40 | − This is an :ref:`obsolete` interface and may be removed | |
| 41 | − in the future. It is superseded by | |
| 42 | − :ref:`the selection API <VIDIOC_SUBDEV_G_SELECTION>`. | |
| 40 | + This is an :ref:`obsolete` interface and may be removed in the future. It is | |
| 41 | + superseded by :ref:`the selection API <VIDIOC_SUBDEV_G_SELECTION>`. No new | |
| 42 | + extensions to the :c:type:`v4l2_subdev_crop` structure will be accepted. | |
| 43 | 43 | |
| 44 | 44 | To retrieve the current crop rectangle applications set the ``pad`` |
| 45 | 45 | 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. · 42 unmodified lines | ||
| 43 | 43 | Drivers report their current routing tables using the |
| 44 | 44 | ``VIDIOC_SUBDEV_G_ROUTING`` ioctl and application may enable or disable routes |
| 45 | 45 | with the ``VIDIOC_SUBDEV_S_ROUTING`` ioctl, by adding or removing routes and |
| 46 | −setting or clearing flags of the ``flags`` field of a | |
| 47 | −struct :c:type:`v4l2_subdev_route`. | |
| 46 | +setting or clearing flags of the ``flags`` field of a struct | |
| 47 | +:c:type:`v4l2_subdev_route`. Similarly to ``VIDIOC_SUBDEV_G_ROUTING``, also | |
| 48 | +``VIDIOC_SUBDEV_S_ROUTING`` returns the routes back to the user. | |
| 48 | 49 | |
| 49 | −All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called. This | |
| 50 | −means that the userspace must reconfigure all streams after calling the ioctl | |
| 51 | −with e.g. ``VIDIOC_SUBDEV_S_FMT``. | |
| 50 | +All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called. | |
| 51 | +This means that the userspace must reconfigure all stream formats and selections | |
| 52 | +after calling the ioctl with e.g. ``VIDIOC_SUBDEV_S_FMT``. | |
| 52 | 53 | |
| 53 | 54 | Only subdevices which have both sink and source pads can support routing. |
| 54 | 55 | |
| 55 | −When inspecting routes through ``VIDIOC_SUBDEV_G_ROUTING`` and the application | |
| 56 | −provided ``num_routes`` is not big enough to contain all the available routes | |
| 57 | −the subdevice exposes, drivers return the ENOSPC error code and adjust the | |
| 58 | −value of the ``num_routes`` field. Application should then reserve enough memory | |
| 59 | −for all the route entries and call ``VIDIOC_SUBDEV_G_ROUTING`` again. | |
| 60 | − | |
| 61 | −On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the | |
| 62 | −``num_routes`` field to reflect the actual number of routes returned. | |
| 56 | +The ``len_routes`` field indicates the number of routes that can fit in the | |
| 57 | +``routes`` array allocated by userspace. It is set by applications for both | |
| 58 | +ioctls to indicate how many routes the kernel can return, and is never modified | |
| 59 | +by the kernel. | |
| 60 | + | |
| 61 | +The ``num_routes`` field indicates the number of routes in the routing | |
| 62 | +table. For ``VIDIOC_SUBDEV_S_ROUTING``, it is set by userspace to the number of | |
| 63 | +routes that the application stored in the ``routes`` array. For both ioctls, it | |
| 64 | +is returned by the kernel and indicates how many routes are stored in the | |
| 65 | +subdevice routing table. This may be smaller or larger than the value of | |
| 66 | +``num_routes`` set by the application for ``VIDIOC_SUBDEV_S_ROUTING``, as | |
| 67 | +drivers may adjust the requested routing table. | |
| 68 | + | |
| 69 | +The kernel can return a ``num_routes`` value larger than ``len_routes`` from | |
| 70 | +both ioctls. This indicates thare are more routes in the routing table than fits | |
| 71 | +the ``routes`` array. In this case, the ``routes`` array is filled by the kernel | |
| 72 | +with the first ``len_routes`` entries of the subdevice routing table. This is | |
| 73 | +not considered to be an error, and the ioctl call succeeds. If the applications | |
| 74 | +wants to retrieve the missing routes, it can issue a new | |
| 75 | +``VIDIOC_SUBDEV_G_ROUTING`` call with a large enough ``routes`` array. | |
| 76 | + | |
| 77 | +``VIDIOC_SUBDEV_S_ROUTING`` may return more routes than the user provided in | |
| 78 | +``num_routes`` field due to e.g. hardware properties. | |
| 63 | 79 | |
| 64 | 80 | .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| |
| 65 | 81 | |
| @@ -74,6 +90,9 @@ On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the · 8 unmodified lines | ||
| 74 | 90 | - ``which`` |
| 75 | 91 | - Routing table to be accessed, from enum |
| 76 | 92 | :ref:`v4l2_subdev_format_whence <v4l2-subdev-format-whence>`. |
| 93 | + * - __u32 | |
| 94 | + - ``len_routes`` | |
| 95 | + - The length of the array (as in memory reserved for the array) | |
| 77 | 96 | * - struct :c:type:`v4l2_subdev_route` |
| 78 | 97 | - ``routes[]`` |
| 79 | 98 | - 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 · 1 unmodified line | ||
| 81 | 100 | - ``num_routes`` |
| 82 | 101 | - Number of entries of the routes array |
| 83 | 102 | * - __u32 |
| 84 | − - ``reserved``\ [5] | |
| 103 | + - ``reserved``\ [11] | |
| 85 | 104 | - Reserved for future extensions. Applications and drivers must set |
| 86 | 105 | the array to zero. |
| 87 | 106 | |
| @@ -135,10 +154,6 @@ On success 0 is returned, on error -1 and the ``errno`` variable is set · 47 unmodified lines | ||
| 135 | 154 | appropriately. The generic error codes are described at the |
| 136 | 155 | :ref:`Generic Error Codes <gen-errors>` chapter. |
| 137 | 156 | |
| 138 | −ENOSPC | |
| 139 | − The application provided ``num_routes`` is not big enough to contain | |
| 140 | − all the available routes the subdevice exposes. | |
| 141 | − | |
| 142 | 157 | EINVAL |
| 143 | 158 | The sink or source pad identifiers reference a non-existing pad or reference |
| 144 | 159 | 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 · 214 unmodified lines | ||
| 215 | 215 | replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags |
| 216 | 216 | replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags |
| 217 | 217 | replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags |
| 218 | +replace define V4L2_FMT_FLAG_META_LINE_BASED fmtdesc-flags | |
| 218 | 219 | |
| 219 | 220 | # V4L2 timecode types |
| 220 | 221 | replace define V4L2_TC_TYPE_24FPS timecode-type |
| @@ -4110,6 +4110,13 @@ N: bcm113* · 4109 unmodified lines | ||
| 4110 | 4110 | N: bcm216* |
| 4111 | 4111 | N: kona |
| 4112 | 4112 | |
| 4113 | +BROADCOM BCM2835 CAMERA DRIVERS | |
| 4114 | +M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com> | |
| 4115 | +L: linux-media@vger.kernel.org | |
| 4116 | +S: Maintained | |
| 4117 | +F: Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml | |
| 4118 | +F: drivers/media/platform/broadcom/bcm2835-unicam* | |
| 4119 | + | |
| 4113 | 4120 | BROADCOM BCM47XX MIPS ARCHITECTURE |
| 4114 | 4121 | M: Hauke Mehrtens <hauke@hauke-m.de> |
| 4115 | 4122 | M: Rafał Miłecki <zajec5@gmail.com> |
| @@ -11091,6 +11098,16 @@ F: Documentation/admin-guide/media/ipu3_rcb.svg · 6975 unmodified lines | ||
| 11091 | 11098 | F: Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst |
| 11092 | 11099 | F: drivers/staging/media/ipu3/ |
| 11093 | 11100 | |
| 11101 | +INTEL IPU6 INPUT SYSTEM DRIVER | |
| 11102 | +M: Sakari Ailus <sakari.ailus@linux.intel.com> | |
| 11103 | +M: Bingbu Cao <bingbu.cao@intel.com> | |
| 11104 | +R: Tianshu Qiu <tian.shu.qiu@intel.com> | |
| 11105 | +L: linux-media@vger.kernel.org | |
| 11106 | +S: Maintained | |
| 11107 | +T: git git://linuxtv.org/media_tree.git | |
| 11108 | +F: Documentation/admin-guide/media/ipu6-isys.rst | |
| 11109 | +F: drivers/media/pci/intel/ipu6/ | |
| 11110 | + | |
| 11094 | 11111 | INTEL ISHTP ECLITE DRIVER |
| 11095 | 11112 | M: Sumesh K Naduvalath <sumesh.k.naduvalath@intel.com> |
| 11096 | 11113 | L: platform-driver-x86@vger.kernel.org |
| @@ -16539,7 +16556,7 @@ M: Sakari Ailus <sakari.ailus@linux.intel.com> · 5442 unmodified lines | ||
| 16539 | 16556 | L: linux-media@vger.kernel.org |
| 16540 | 16557 | S: Maintained |
| 16541 | 16558 | T: git git://linuxtv.org/media_tree.git |
| 16542 | −F: Documentation/devicetree/bindings/media/i2c/ov8856.yaml | |
| 16559 | +F: Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml | |
| 16543 | 16560 | F: drivers/media/i2c/ov8856.c |
| 16544 | 16561 | |
| 16545 | 16562 | OMNIVISION OV8858 SENSOR DRIVER |
| @@ -490,6 +490,15 @@ int cec_thread_func(void *_adap) · 489 unmodified lines | ||
| 490 | 490 | goto unlock; |
| 491 | 491 | } |
| 492 | 492 | |
| 493 | + if (adap->transmit_in_progress && | |
| 494 | + adap->transmit_in_progress_aborted) { | |
| 495 | + if (adap->transmitting) | |
| 496 | + cec_data_cancel(adap->transmitting, | |
| 497 | + CEC_TX_STATUS_ABORTED, 0); | |
| 498 | + adap->transmit_in_progress = false; | |
| 499 | + adap->transmit_in_progress_aborted = false; | |
| 500 | + goto unlock; | |
| 501 | + } | |
| 493 | 502 | if (adap->transmit_in_progress && timeout) { |
| 494 | 503 | /* |
| 495 | 504 | * 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, · 275 unmodified lines | ||
| 771 | 780 | { |
| 772 | 781 | struct cec_data *data; |
| 773 | 782 | bool is_raw = msg_is_raw(msg); |
| 783 | + int err; | |
| 774 | 784 | |
| 775 | 785 | if (adap->devnode.unregistered) |
| 776 | 786 | return -ENODEV; |
| @@ -935,11 +945,13 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, · 158 unmodified lines | ||
| 935 | 945 | * Release the lock and wait, retake the lock afterwards. |
| 936 | 946 | */ |
| 937 | 947 | mutex_unlock(&adap->lock); |
| 938 | − wait_for_completion_killable(&data->c); | |
| 939 | − if (!data->completed) | |
| 940 | − cancel_delayed_work_sync(&data->work); | |
| 948 | + err = wait_for_completion_killable(&data->c); | |
| 949 | + cancel_delayed_work_sync(&data->work); | |
| 941 | 950 | mutex_lock(&adap->lock); |
| 942 | 951 | |
| 952 | + if (err) | |
| 953 | + adap->transmit_in_progress_aborted = true; | |
| 954 | + | |
| 943 | 955 | /* Cancel the transmit if it was interrupted */ |
| 944 | 956 | if (!data->completed) { |
| 945 | 957 | if (data->msg.tx_status & CEC_TX_STATUS_OK) |
| @@ -1575,9 +1587,12 @@ unconfigure: · 629 unmodified lines | ||
| 1575 | 1587 | */ |
| 1576 | 1588 | static void cec_claim_log_addrs(struct cec_adapter *adap, bool block) |
| 1577 | 1589 | { |
| 1578 | − if (WARN_ON(adap->is_configuring || adap->is_configured)) | |
| 1590 | + if (WARN_ON(adap->is_claiming_log_addrs || | |
| 1591 | + adap->is_configuring || adap->is_configured)) | |
| 1579 | 1592 | return; |
| 1580 | 1593 | |
| 1594 | + adap->is_claiming_log_addrs = true; | |
| 1595 | + | |
| 1581 | 1596 | init_completion(&adap->config_completion); |
| 1582 | 1597 | |
| 1583 | 1598 | /* Ready to kick off the thread */ |
| @@ -1592,6 +1607,7 @@ static void cec_claim_log_addrs(struct cec_adapter *adap, bool block) · 8 unmodified lines | ||
| 1592 | 1607 | wait_for_completion(&adap->config_completion); |
| 1593 | 1608 | mutex_lock(&adap->lock); |
| 1594 | 1609 | } |
| 1610 | + adap->is_claiming_log_addrs = false; | |
| 1595 | 1611 | } |
| 1596 | 1612 | |
| 1597 | 1613 | /* |
| @@ -178,7 +178,7 @@ static long cec_adap_s_log_addrs(struct cec_adapter *adap, struct cec_fh *fh, · 177 unmodified lines | ||
| 178 | 178 | CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU | |
| 179 | 179 | CEC_LOG_ADDRS_FL_CDC_ONLY; |
| 180 | 180 | mutex_lock(&adap->lock); |
| 181 | − if (!adap->is_configuring && | |
| 181 | + if (!adap->is_claiming_log_addrs && !adap->is_configuring && | |
| 182 | 182 | (!log_addrs.num_log_addrs || !adap->is_configured) && |
| 183 | 183 | !cec_is_busy(adap, fh)) { |
| 184 | 184 | err = __cec_s_log_addrs(adap, &log_addrs, block); |
| @@ -664,6 +664,8 @@ static int cec_release(struct inode *inode, struct file *filp) · 479 unmodified lines | ||
| 664 | 664 | list_del_init(&data->xfer_list); |
| 665 | 665 | } |
| 666 | 666 | mutex_unlock(&adap->lock); |
| 667 | + | |
| 668 | + mutex_lock(&fh->lock); | |
| 667 | 669 | while (!list_empty(&fh->msgs)) { |
| 668 | 670 | struct cec_msg_entry *entry = |
| 669 | 671 | list_first_entry(&fh->msgs, struct cec_msg_entry, list); |
| @@ -681,6 +683,7 @@ static int cec_release(struct inode *inode, struct file *filp) · 11 unmodified lines | ||
| 681 | 683 | kfree(entry); |
| 682 | 684 | } |
| 683 | 685 | } |
| 686 | + mutex_unlock(&fh->lock); | |
| 684 | 687 | kfree(fh); |
| 685 | 688 | |
| 686 | 689 | cec_put_device(devnode); |
| @@ -62,12 +62,12 @@ int cec_get_device(struct cec_devnode *devnode) · 61 unmodified lines | ||
| 62 | 62 | */ |
| 63 | 63 | mutex_lock(&devnode->lock); |
| 64 | 64 | /* |
| 65 | − * return ENXIO if the cec device has been removed | |
| 65 | + * return ENODEV if the cec device has been removed | |
| 66 | 66 | * already or if it is not registered anymore. |
| 67 | 67 | */ |
| 68 | 68 | if (!devnode->registered) { |
| 69 | 69 | mutex_unlock(&devnode->lock); |
| 70 | − return -ENXIO; | |
| 70 | + return -ENODEV; | |
| 71 | 71 | } |
| 72 | 72 | /* and increase the device refcount */ |
| 73 | 73 | get_device(&devnode->dev); |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | #include <linux/kernel.h> |
| 10 | 10 | #include <linux/module.h> |
| 11 | +#include <linux/mod_devicetable.h> | |
| 11 | 12 | #include <linux/platform_device.h> |
| 12 | 13 | #include <linux/dmi.h> |
| 13 | 14 | #include <linux/pci.h> |
| @@ -573,6 +574,12 @@ static void cros_ec_cec_remove(struct platform_device *pdev) · 559 unmodified lines | ||
| 573 | 574 | } |
| 574 | 575 | } |
| 575 | 576 | |
| 577 | +static const struct platform_device_id cros_ec_cec_id[] = { | |
| 578 | + { DRV_NAME, 0 }, | |
| 579 | + {} | |
| 580 | +}; | |
| 581 | +MODULE_DEVICE_TABLE(platform, cros_ec_cec_id); | |
| 582 | + | |
| 576 | 583 | static struct platform_driver cros_ec_cec_driver = { |
| 577 | 584 | .probe = cros_ec_cec_probe, |
| 578 | 585 | .remove_new = cros_ec_cec_remove, |
| @@ -580,6 +587,7 @@ static struct platform_driver cros_ec_cec_driver = { · 1 unmodified line | ||
| 580 | 587 | .name = DRV_NAME, |
| 581 | 588 | .pm = &cros_ec_cec_pm_ops, |
| 582 | 589 | }, |
| 590 | + .id_table = cros_ec_cec_id, | |
| 583 | 591 | }; |
| 584 | 592 | |
| 585 | 593 | module_platform_driver(cros_ec_cec_driver); |
| @@ -587,4 +595,3 @@ module_platform_driver(cros_ec_cec_driver); · 1 unmodified line | ||
| 587 | 595 | MODULE_DESCRIPTION("CEC driver for ChromeOS ECs"); |
| 588 | 596 | MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); |
| 589 | 597 | MODULE_LICENSE("GPL"); |
| 590 | −MODULE_ALIAS("platform:" DRV_NAME); | |
| @@ -122,7 +122,7 @@ static int calculate_h_scale_registers(struct saa7146_dev *dev, · 121 unmodified lines | ||
| 122 | 122 | xacm = 0; |
| 123 | 123 | |
| 124 | 124 | /* set horizontal filter parameters (CXY = CXUV) */ |
| 125 | − cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff; | |
| 125 | + cxy = hps_h_coeff_tab[min(xpsc - 1, 63)].hps_coeff; | |
| 126 | 126 | cxuv = cxy; |
| 127 | 127 | |
| 128 | 128 | /* calculate and set horizontal fine scale (xsci) */ |
| @@ -151,7 +151,7 @@ static int calculate_h_scale_registers(struct saa7146_dev *dev, · 22 unmodified lines | ||
| 151 | 151 | xacm = 0; |
| 152 | 152 | /* get best match in the table of attenuations |
| 153 | 153 | for horizontal scaling */ |
| 154 | − h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum; | |
| 154 | + h_atten = hps_h_coeff_tab[min(xpsc - 1, 63)].weight_sum; | |
| 155 | 155 | |
| 156 | 156 | for (i = 0; h_attenuation[i] != 0; i++) { |
| 157 | 157 | if (h_attenuation[i] >= h_atten) |
| @@ -283,10 +283,10 @@ static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field · 125 unmodified lines | ||
| 283 | 283 | } |
| 284 | 284 | |
| 285 | 285 | /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */ |
| 286 | − cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff; | |
| 286 | + cya_cyb = hps_v_coeff_tab[min(yacl, 63)].hps_coeff; | |
| 287 | 287 | |
| 288 | 288 | /* get best match in the table of attenuations for vertical scaling */ |
| 289 | − v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum; | |
| 289 | + v_atten = hps_v_coeff_tab[min(yacl, 63)].weight_sum; | |
| 290 | 290 | |
| 291 | 291 | for (i = 0; v_attenuation[i] != 0; i++) { |
| 292 | 292 | if (v_attenuation[i] >= v_atten) |
| @@ -421,11 +421,12 @@ static void init_buffer_cache_hints(struct vb2_queue *q, struct vb2_buffer *vb) · 420 unmodified lines | ||
| 421 | 421 | */ |
| 422 | 422 | static void vb2_queue_add_buffer(struct vb2_queue *q, struct vb2_buffer *vb, unsigned int index) |
| 423 | 423 | { |
| 424 | − WARN_ON(index >= q->max_num_buffers || q->bufs[index] || vb->vb2_queue); | |
| 424 | + WARN_ON(index >= q->max_num_buffers || test_bit(index, q->bufs_bitmap) || vb->vb2_queue); | |
| 425 | 425 | |
| 426 | 426 | q->bufs[index] = vb; |
| 427 | 427 | vb->index = index; |
| 428 | 428 | vb->vb2_queue = q; |
| 429 | + set_bit(index, q->bufs_bitmap); | |
| 429 | 430 | } |
| 430 | 431 | |
| 431 | 432 | /** |
| @@ -434,6 +435,7 @@ static void vb2_queue_add_buffer(struct vb2_queue *q, struct vb2_buffer *vb, uns · 2 unmodified lines | ||
| 434 | 435 | */ |
| 435 | 436 | static void vb2_queue_remove_buffer(struct vb2_buffer *vb) |
| 436 | 437 | { |
| 438 | + clear_bit(vb->index, vb->vb2_queue->bufs_bitmap); | |
| 437 | 439 | vb->vb2_queue->bufs[vb->index] = NULL; |
| 438 | 440 | vb->vb2_queue = NULL; |
| 439 | 441 | } |
| @@ -442,16 +444,19 @@ static void vb2_queue_remove_buffer(struct vb2_buffer *vb) · 2 unmodified lines | ||
| 442 | 444 | * __vb2_queue_alloc() - allocate vb2 buffer structures and (for MMAP type) |
| 443 | 445 | * video buffer memory for all buffers/planes on the queue and initializes the |
| 444 | 446 | * queue |
| 447 | + * @first_index: index of the first created buffer, all newly allocated buffers | |
| 448 | + * have indices in the range [first_index..first_index+count-1] | |
| 445 | 449 | * |
| 446 | 450 | * Returns the number of buffers successfully allocated. |
| 447 | 451 | */ |
| 448 | 452 | static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, |
| 449 | 453 | unsigned int num_buffers, unsigned int num_planes, |
| 450 | − const unsigned plane_sizes[VB2_MAX_PLANES]) | |
| 454 | + const unsigned int plane_sizes[VB2_MAX_PLANES], | |
| 455 | + unsigned int *first_index) | |
| 451 | 456 | { |
| 452 | − unsigned int q_num_buffers = vb2_get_num_buffers(q); | |
| 453 | 457 | unsigned int buffer, plane; |
| 454 | 458 | struct vb2_buffer *vb; |
| 459 | + unsigned long index = q->max_num_buffers; | |
| 455 | 460 | int ret; |
| 456 | 461 | |
| 457 | 462 | /* |
| @@ -459,7 +464,25 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, · 1 unmodified line | ||
| 459 | 464 | * in the queue is below q->max_num_buffers |
| 460 | 465 | */ |
| 461 | 466 | num_buffers = min_t(unsigned int, num_buffers, |
| 462 | − q->max_num_buffers - q_num_buffers); | |
| 467 | + q->max_num_buffers - vb2_get_num_buffers(q)); | |
| 468 | + | |
| 469 | + while (num_buffers) { | |
| 470 | + index = bitmap_find_next_zero_area(q->bufs_bitmap, q->max_num_buffers, | |
| 471 | + 0, num_buffers, 0); | |
| 472 | + | |
| 473 | + if (index < q->max_num_buffers) | |
| 474 | + break; | |
| 475 | + /* Try to find free space for less buffers */ | |
| 476 | + num_buffers--; | |
| 477 | + } | |
| 478 | + | |
| 479 | + /* If there is no space left to allocate buffers return 0 to indicate the error */ | |
| 480 | + if (!num_buffers) { | |
| 481 | + *first_index = 0; | |
| 482 | + return 0; | |
| 483 | + } | |
| 484 | + | |
| 485 | + *first_index = index; | |
| 463 | 486 | |
| 464 | 487 | for (buffer = 0; buffer < num_buffers; ++buffer) { |
| 465 | 488 | /* Allocate vb2 buffer structures */ |
| @@ -479,7 +502,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, · 13 unmodified lines | ||
| 479 | 502 | vb->planes[plane].min_length = plane_sizes[plane]; |
| 480 | 503 | } |
| 481 | 504 | |
| 482 | − vb2_queue_add_buffer(q, vb, q_num_buffers + buffer); | |
| 505 | + vb2_queue_add_buffer(q, vb, index++); | |
| 483 | 506 | call_void_bufop(q, init_buffer, vb); |
| 484 | 507 | |
| 485 | 508 | /* 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, · 31 unmodified lines | ||
| 517 | 540 | } |
| 518 | 541 | |
| 519 | 542 | /* |
| 520 | − * __vb2_free_mem() - release all video buffer memory for a given queue | |
| 543 | + * __vb2_free_mem() - release video buffer memory for a given range of | |
| 544 | + * buffers in a given queue | |
| 521 | 545 | */ |
| 522 | −static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers) | |
| 546 | +static void __vb2_free_mem(struct vb2_queue *q, unsigned int start, unsigned int count) | |
| 523 | 547 | { |
| 524 | − unsigned int buffer; | |
| 548 | + unsigned int i; | |
| 525 | 549 | struct vb2_buffer *vb; |
| 526 | − unsigned int q_num_buffers = vb2_get_num_buffers(q); | |
| 527 | 550 | |
| 528 | − for (buffer = q_num_buffers - buffers; buffer < q_num_buffers; | |
| 529 | − ++buffer) { | |
| 530 | − vb = vb2_get_buffer(q, buffer); | |
| 551 | + for (i = start; i < start + count; i++) { | |
| 552 | + vb = vb2_get_buffer(q, i); | |
| 531 | 553 | if (!vb) |
| 532 | 554 | continue; |
| 533 | 555 | |
| @@ -542,35 +564,33 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers) · 8 unmodified lines | ||
| 542 | 564 | } |
| 543 | 565 | |
| 544 | 566 | /* |
| 545 | − * __vb2_queue_free() - free buffers at the end of the queue - video memory and | |
| 567 | + * __vb2_queue_free() - free @count buffers from @start index of the queue - video memory and | |
| 546 | 568 | * related information, if no buffers are left return the queue to an |
| 547 | 569 | * uninitialized state. Might be called even if the queue has already been freed. |
| 548 | 570 | */ |
| 549 | −static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) | |
| 571 | +static void __vb2_queue_free(struct vb2_queue *q, unsigned int start, unsigned int count) | |
| 550 | 572 | { |
| 551 | − unsigned int buffer; | |
| 552 | − unsigned int q_num_buffers = vb2_get_num_buffers(q); | |
| 573 | + unsigned int i; | |
| 553 | 574 | |
| 554 | 575 | lockdep_assert_held(&q->mmap_lock); |
| 555 | 576 | |
| 556 | 577 | /* Call driver-provided cleanup function for each buffer, if provided */ |
| 557 | − for (buffer = q_num_buffers - buffers; buffer < q_num_buffers; | |
| 558 | − ++buffer) { | |
| 559 | − struct vb2_buffer *vb = vb2_get_buffer(q, buffer); | |
| 578 | + for (i = start; i < start + count; i++) { | |
| 579 | + struct vb2_buffer *vb = vb2_get_buffer(q, i); | |
| 560 | 580 | |
| 561 | 581 | if (vb && vb->planes[0].mem_priv) |
| 562 | 582 | call_void_vb_qop(vb, buf_cleanup, vb); |
| 563 | 583 | } |
| 564 | 584 | |
| 565 | 585 | /* Release video buffer memory */ |
| 566 | − __vb2_free_mem(q, buffers); | |
| 586 | + __vb2_free_mem(q, start, count); | |
| 567 | 587 | |
| 568 | 588 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
| 569 | 589 | /* |
| 570 | 590 | * Check that all the calls were balanced during the life-time of this |
| 571 | 591 | * queue. If not then dump the counters to the kernel log. |
| 572 | 592 | */ |
| 573 | − if (q_num_buffers) { | |
| 593 | + if (vb2_get_num_buffers(q)) { | |
| 574 | 594 | bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming || |
| 575 | 595 | q->cnt_prepare_streaming != q->cnt_unprepare_streaming || |
| 576 | 596 | q->cnt_wait_prepare != q->cnt_wait_finish; |
| @@ -596,8 +616,8 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) · 19 unmodified lines | ||
| 596 | 616 | q->cnt_stop_streaming = 0; |
| 597 | 617 | q->cnt_unprepare_streaming = 0; |
| 598 | 618 | } |
| 599 | − for (buffer = 0; buffer < vb2_get_num_buffers(q); buffer++) { | |
| 600 | − struct vb2_buffer *vb = vb2_get_buffer(q, buffer); | |
| 619 | + for (i = start; i < start + count; i++) { | |
| 620 | + struct vb2_buffer *vb = vb2_get_buffer(q, i); | |
| 601 | 621 | bool unbalanced; |
| 602 | 622 | |
| 603 | 623 | if (!vb) |
| @@ -614,7 +634,7 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) · 10 unmodified lines | ||
| 614 | 634 | |
| 615 | 635 | if (unbalanced) { |
| 616 | 636 | pr_info("unbalanced counters for queue %p, buffer %d:\n", |
| 617 | − q, buffer); | |
| 637 | + q, i); | |
| 618 | 638 | if (vb->cnt_buf_init != vb->cnt_buf_cleanup) |
| 619 | 639 | pr_info(" buf_init: %u buf_cleanup: %u\n", |
| 620 | 640 | vb->cnt_buf_init, vb->cnt_buf_cleanup); |
| @@ -648,9 +668,8 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) · 27 unmodified lines | ||
| 648 | 668 | #endif |
| 649 | 669 | |
| 650 | 670 | /* Free vb2 buffers */ |
| 651 | − for (buffer = q_num_buffers - buffers; buffer < q_num_buffers; | |
| 652 | − ++buffer) { | |
| 653 | − struct vb2_buffer *vb = vb2_get_buffer(q, buffer); | |
| 671 | + for (i = start; i < start + count; i++) { | |
| 672 | + struct vb2_buffer *vb = vb2_get_buffer(q, i); | |
| 654 | 673 | |
| 655 | 674 | if (!vb) |
| 656 | 675 | continue; |
| @@ -659,7 +678,6 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) · 2 unmodified lines | ||
| 659 | 678 | kfree(vb); |
| 660 | 679 | } |
| 661 | 680 | |
| 662 | − q->num_buffers -= buffers; | |
| 663 | 681 | if (!vb2_get_num_buffers(q)) { |
| 664 | 682 | q->memory = VB2_MEMORY_UNKNOWN; |
| 665 | 683 | INIT_LIST_HEAD(&q->queued_list); |
| @@ -691,7 +709,7 @@ EXPORT_SYMBOL(vb2_buffer_in_use); · 25 unmodified lines | ||
| 691 | 709 | static bool __buffers_in_use(struct vb2_queue *q) |
| 692 | 710 | { |
| 693 | 711 | unsigned int buffer; |
| 694 | − for (buffer = 0; buffer < vb2_get_num_buffers(q); ++buffer) { | |
| 712 | + for (buffer = 0; buffer < q->max_num_buffers; ++buffer) { | |
| 695 | 713 | struct vb2_buffer *vb = vb2_get_buffer(q, buffer); |
| 696 | 714 | |
| 697 | 715 | if (!vb) |
| @@ -813,6 +831,32 @@ static bool verify_coherency_flags(struct vb2_queue *q, bool non_coherent_mem) · 115 unmodified lines | ||
| 813 | 831 | return true; |
| 814 | 832 | } |
| 815 | 833 | |
| 834 | +static int vb2_core_allocated_buffers_storage(struct vb2_queue *q) | |
| 835 | +{ | |
| 836 | + if (!q->bufs) | |
| 837 | + q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL); | |
| 838 | + if (!q->bufs) | |
| 839 | + return -ENOMEM; | |
| 840 | + | |
| 841 | + if (!q->bufs_bitmap) | |
| 842 | + q->bufs_bitmap = bitmap_zalloc(q->max_num_buffers, GFP_KERNEL); | |
| 843 | + if (!q->bufs_bitmap) { | |
| 844 | + kfree(q->bufs); | |
| 845 | + q->bufs = NULL; | |
| 846 | + return -ENOMEM; | |
| 847 | + } | |
| 848 | + | |
| 849 | + return 0; | |
| 850 | +} | |
| 851 | + | |
| 852 | +static void vb2_core_free_buffers_storage(struct vb2_queue *q) | |
| 853 | +{ | |
| 854 | + kfree(q->bufs); | |
| 855 | + q->bufs = NULL; | |
| 856 | + bitmap_free(q->bufs_bitmap); | |
| 857 | + q->bufs_bitmap = NULL; | |
| 858 | +} | |
| 859 | + | |
| 816 | 860 | int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, |
| 817 | 861 | unsigned int flags, unsigned int *count) |
| 818 | 862 | { |
| @@ -820,7 +864,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 1 unmodified line | ||
| 820 | 864 | unsigned int q_num_bufs = vb2_get_num_buffers(q); |
| 821 | 865 | unsigned plane_sizes[VB2_MAX_PLANES] = { }; |
| 822 | 866 | bool non_coherent_mem = flags & V4L2_MEMORY_FLAG_NON_COHERENT; |
| 823 | − unsigned int i; | |
| 867 | + unsigned int i, first_index; | |
| 824 | 868 | int ret = 0; |
| 825 | 869 | |
| 826 | 870 | if (q->streaming) { |
| @@ -851,9 +895,10 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 24 unmodified lines | ||
| 851 | 895 | * queued without ever calling STREAMON. |
| 852 | 896 | */ |
| 853 | 897 | __vb2_queue_cancel(q); |
| 854 | − __vb2_queue_free(q, q_num_bufs); | |
| 898 | + __vb2_queue_free(q, 0, q->max_num_buffers); | |
| 855 | 899 | mutex_unlock(&q->mmap_lock); |
| 856 | 900 | |
| 901 | + q->is_busy = 0; | |
| 857 | 902 | /* |
| 858 | 903 | * In case of REQBUFS(0) return immediately without calling |
| 859 | 904 | * driver's queue_setup() callback and allocating resources. |
| @@ -865,7 +910,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 5 unmodified lines | ||
| 865 | 910 | /* |
| 866 | 911 | * Make sure the requested values and current defaults are sane. |
| 867 | 912 | */ |
| 868 | − num_buffers = max_t(unsigned int, *count, q->min_queued_buffers); | |
| 913 | + num_buffers = max_t(unsigned int, *count, q->min_reqbufs_allocation); | |
| 869 | 914 | num_buffers = min_t(unsigned int, num_buffers, q->max_num_buffers); |
| 870 | 915 | memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); |
| 871 | 916 | /* |
| @@ -873,10 +918,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 1 unmodified line | ||
| 873 | 918 | * in the queue_setup op. |
| 874 | 919 | */ |
| 875 | 920 | mutex_lock(&q->mmap_lock); |
| 876 | − if (!q->bufs) | |
| 877 | − q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL); | |
| 878 | − if (!q->bufs) | |
| 879 | − ret = -ENOMEM; | |
| 921 | + ret = vb2_core_allocated_buffers_storage(q); | |
| 880 | 922 | q->memory = memory; |
| 881 | 923 | mutex_unlock(&q->mmap_lock); |
| 882 | 924 | if (ret) |
| @@ -906,8 +948,10 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 23 unmodified lines | ||
| 906 | 948 | |
| 907 | 949 | /* Finally, allocate buffers and video memory */ |
| 908 | 950 | allocated_buffers = |
| 909 | − __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes); | |
| 951 | + __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes, &first_index); | |
| 910 | 952 | if (allocated_buffers == 0) { |
| 953 | + /* There shouldn't be any buffers allocated, so first_index == 0 */ | |
| 954 | + WARN_ON(first_index); | |
| 911 | 955 | dprintk(q, 1, "memory allocation failed\n"); |
| 912 | 956 | ret = -ENOMEM; |
| 913 | 957 | goto error; |
| @@ -917,7 +961,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 3 unmodified lines | ||
| 917 | 961 | * There is no point in continuing if we can't allocate the minimum |
| 918 | 962 | * number of buffers needed by this vb2_queue. |
| 919 | 963 | */ |
| 920 | − if (allocated_buffers < q->min_queued_buffers) | |
| 964 | + if (allocated_buffers < q->min_reqbufs_allocation) | |
| 921 | 965 | ret = -ENOMEM; |
| 922 | 966 | |
| 923 | 967 | /* |
| @@ -946,7 +990,6 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 22 unmodified lines | ||
| 946 | 990 | } |
| 947 | 991 | |
| 948 | 992 | mutex_lock(&q->mmap_lock); |
| 949 | − q->num_buffers = allocated_buffers; | |
| 950 | 993 | |
| 951 | 994 | if (ret < 0) { |
| 952 | 995 | /* |
| @@ -954,7 +997,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 1 unmodified line | ||
| 954 | 997 | * from already queued buffers and it will reset q->memory to |
| 955 | 998 | * VB2_MEMORY_UNKNOWN. |
| 956 | 999 | */ |
| 957 | − __vb2_queue_free(q, allocated_buffers); | |
| 1000 | + __vb2_queue_free(q, first_index, allocated_buffers); | |
| 958 | 1001 | mutex_unlock(&q->mmap_lock); |
| 959 | 1002 | return ret; |
| 960 | 1003 | } |
| @@ -966,6 +1009,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 5 unmodified lines | ||
| 966 | 1009 | */ |
| 967 | 1010 | *count = allocated_buffers; |
| 968 | 1011 | q->waiting_for_buffers = !q->is_output; |
| 1012 | + q->is_busy = 1; | |
| 969 | 1013 | |
| 970 | 1014 | return 0; |
| 971 | 1015 | |
| @@ -973,6 +1017,7 @@ error: · 1 unmodified line | ||
| 973 | 1017 | mutex_lock(&q->mmap_lock); |
| 974 | 1018 | q->memory = VB2_MEMORY_UNKNOWN; |
| 975 | 1019 | mutex_unlock(&q->mmap_lock); |
| 1020 | + vb2_core_free_buffers_storage(q); | |
| 976 | 1021 | return ret; |
| 977 | 1022 | } |
| 978 | 1023 | EXPORT_SYMBOL_GPL(vb2_core_reqbufs); |
| @@ -980,7 +1025,8 @@ EXPORT_SYMBOL_GPL(vb2_core_reqbufs); · 1 unmodified line | ||
| 980 | 1025 | int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, |
| 981 | 1026 | unsigned int flags, unsigned int *count, |
| 982 | 1027 | unsigned int requested_planes, |
| 983 | − const unsigned int requested_sizes[]) | |
| 1028 | + const unsigned int requested_sizes[], | |
| 1029 | + unsigned int *first_index) | |
| 984 | 1030 | { |
| 985 | 1031 | unsigned int num_planes = 0, num_buffers, allocated_buffers; |
| 986 | 1032 | unsigned plane_sizes[VB2_MAX_PLANES] = { }; |
| @@ -1005,11 +1051,8 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, · 18 unmodified lines | ||
| 1005 | 1051 | * value in the queue_setup op. |
| 1006 | 1052 | */ |
| 1007 | 1053 | mutex_lock(&q->mmap_lock); |
| 1054 | + ret = vb2_core_allocated_buffers_storage(q); | |
| 1008 | 1055 | q->memory = memory; |
| 1009 | − if (!q->bufs) | |
| 1010 | − q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL); | |
| 1011 | − if (!q->bufs) | |
| 1012 | − ret = -ENOMEM; | |
| 1013 | 1056 | mutex_unlock(&q->mmap_lock); |
| 1014 | 1057 | if (ret) |
| 1015 | 1058 | return ret; |
| @@ -1042,7 +1085,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, · 26 unmodified lines | ||
| 1042 | 1085 | |
| 1043 | 1086 | /* Finally, allocate buffers and video memory */ |
| 1044 | 1087 | allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers, |
| 1045 | − num_planes, plane_sizes); | |
| 1088 | + num_planes, plane_sizes, first_index); | |
| 1046 | 1089 | if (allocated_buffers == 0) { |
| 1047 | 1090 | dprintk(q, 1, "memory allocation failed\n"); |
| 1048 | 1091 | ret = -ENOMEM; |
| @@ -1072,7 +1115,6 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, · 23 unmodified lines | ||
| 1072 | 1115 | } |
| 1073 | 1116 | |
| 1074 | 1117 | mutex_lock(&q->mmap_lock); |
| 1075 | − q->num_buffers += allocated_buffers; | |
| 1076 | 1118 | |
| 1077 | 1119 | if (ret < 0) { |
| 1078 | 1120 | /* |
| @@ -1080,7 +1122,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, · 1 unmodified line | ||
| 1080 | 1122 | * from already queued buffers and it will reset q->memory to |
| 1081 | 1123 | * VB2_MEMORY_UNKNOWN. |
| 1082 | 1124 | */ |
| 1083 | − __vb2_queue_free(q, allocated_buffers); | |
| 1125 | + __vb2_queue_free(q, *first_index, allocated_buffers); | |
| 1084 | 1126 | mutex_unlock(&q->mmap_lock); |
| 1085 | 1127 | return -ENOMEM; |
| 1086 | 1128 | } |
| @@ -1091,6 +1133,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, · 4 unmodified lines | ||
| 1091 | 1133 | * to the userspace. |
| 1092 | 1134 | */ |
| 1093 | 1135 | *count = allocated_buffers; |
| 1136 | + q->is_busy = 1; | |
| 1094 | 1137 | |
| 1095 | 1138 | return 0; |
| 1096 | 1139 | |
| @@ -1648,6 +1691,44 @@ int vb2_core_prepare_buf(struct vb2_queue *q, struct vb2_buffer *vb, void *pb) · 551 unmodified lines | ||
| 1648 | 1691 | } |
| 1649 | 1692 | EXPORT_SYMBOL_GPL(vb2_core_prepare_buf); |
| 1650 | 1693 | |
| 1694 | +int vb2_core_remove_bufs(struct vb2_queue *q, unsigned int start, unsigned int count) | |
| 1695 | +{ | |
| 1696 | + unsigned int i, ret = 0; | |
| 1697 | + unsigned int q_num_bufs = vb2_get_num_buffers(q); | |
| 1698 | + | |
| 1699 | + if (count == 0) | |
| 1700 | + return 0; | |
| 1701 | + | |
| 1702 | + if (count > q_num_bufs) | |
| 1703 | + return -EINVAL; | |
| 1704 | + | |
| 1705 | + if (start > q->max_num_buffers - count) | |
| 1706 | + return -EINVAL; | |
| 1707 | + | |
| 1708 | + mutex_lock(&q->mmap_lock); | |
| 1709 | + | |
| 1710 | + /* Check that all buffers in the range exist */ | |
| 1711 | + for (i = start; i < start + count; i++) { | |
| 1712 | + struct vb2_buffer *vb = vb2_get_buffer(q, i); | |
| 1713 | + | |
| 1714 | + if (!vb) { | |
| 1715 | + ret = -EINVAL; | |
| 1716 | + goto unlock; | |
| 1717 | + } | |
| 1718 | + if (vb->state != VB2_BUF_STATE_DEQUEUED) { | |
| 1719 | + ret = -EBUSY; | |
| 1720 | + goto unlock; | |
| 1721 | + } | |
| 1722 | + } | |
| 1723 | + __vb2_queue_free(q, start, count); | |
| 1724 | + dprintk(q, 2, "%u buffers removed\n", count); | |
| 1725 | + | |
| 1726 | +unlock: | |
| 1727 | + mutex_unlock(&q->mmap_lock); | |
| 1728 | + return ret; | |
| 1729 | +} | |
| 1730 | +EXPORT_SYMBOL_GPL(vb2_core_remove_bufs); | |
| 1731 | + | |
| 1651 | 1732 | /* |
| 1652 | 1733 | * vb2_start_streaming() - Attempt to start streaming. |
| 1653 | 1734 | * @q: videobuf2 queue |
| @@ -1694,7 +1775,7 @@ static int vb2_start_streaming(struct vb2_queue *q) · 40 unmodified lines | ||
| 1694 | 1775 | * Forcefully reclaim buffers if the driver did not |
| 1695 | 1776 | * correctly return them to vb2. |
| 1696 | 1777 | */ |
| 1697 | − for (i = 0; i < vb2_get_num_buffers(q); ++i) { | |
| 1778 | + for (i = 0; i < q->max_num_buffers; ++i) { | |
| 1698 | 1779 | vb = vb2_get_buffer(q, i); |
| 1699 | 1780 | |
| 1700 | 1781 | if (!vb) |
| @@ -2100,7 +2181,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) · 399 unmodified lines | ||
| 2100 | 2181 | * to vb2 in stop_streaming(). |
| 2101 | 2182 | */ |
| 2102 | 2183 | if (WARN_ON(atomic_read(&q->owned_by_drv_count))) { |
| 2103 | − for (i = 0; i < vb2_get_num_buffers(q); i++) { | |
| 2184 | + for (i = 0; i < q->max_num_buffers; i++) { | |
| 2104 | 2185 | struct vb2_buffer *vb = vb2_get_buffer(q, i); |
| 2105 | 2186 | |
| 2106 | 2187 | if (!vb) |
| @@ -2144,7 +2225,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) · 37 unmodified lines | ||
| 2144 | 2225 | * call to __fill_user_buffer() after buf_finish(). That order can't |
| 2145 | 2226 | * be changed, so we can't move the buf_finish() to __vb2_dqbuf(). |
| 2146 | 2227 | */ |
| 2147 | − for (i = 0; i < vb2_get_num_buffers(q); i++) { | |
| 2228 | + for (i = 0; i < q->max_num_buffers; i++) { | |
| 2148 | 2229 | struct vb2_buffer *vb; |
| 2149 | 2230 | struct media_request *req; |
| 2150 | 2231 | |
| @@ -2503,7 +2584,7 @@ int vb2_core_queue_init(struct vb2_queue *q) · 352 unmodified lines | ||
| 2503 | 2584 | WARN_ON(!q->ops->buf_queue)) |
| 2504 | 2585 | return -EINVAL; |
| 2505 | 2586 | |
| 2506 | − if (WARN_ON(q->max_num_buffers > MAX_BUFFER_INDEX) || | |
| 2587 | + if (WARN_ON(q->max_num_buffers < VB2_MAX_FRAME) || | |
| 2507 | 2588 | WARN_ON(q->min_queued_buffers > q->max_num_buffers)) |
| 2508 | 2589 | return -EINVAL; |
| 2509 | 2590 | |
| @@ -2521,6 +2602,25 @@ int vb2_core_queue_init(struct vb2_queue *q) · 11 unmodified lines | ||
| 2521 | 2602 | if (WARN_ON(q->supports_requests && q->min_queued_buffers)) |
| 2522 | 2603 | return -EINVAL; |
| 2523 | 2604 | |
| 2605 | + /* | |
| 2606 | + * The minimum requirement is 2: one buffer is used | |
| 2607 | + * by the hardware while the other is being processed by userspace. | |
| 2608 | + */ | |
| 2609 | + if (q->min_reqbufs_allocation < 2) | |
| 2610 | + q->min_reqbufs_allocation = 2; | |
| 2611 | + | |
| 2612 | + /* | |
| 2613 | + * If the driver needs 'min_queued_buffers' in the queue before | |
| 2614 | + * calling start_streaming() then the minimum requirement is | |
| 2615 | + * 'min_queued_buffers + 1' to keep at least one buffer available | |
| 2616 | + * for userspace. | |
| 2617 | + */ | |
| 2618 | + if (q->min_reqbufs_allocation < q->min_queued_buffers + 1) | |
| 2619 | + q->min_reqbufs_allocation = q->min_queued_buffers + 1; | |
| 2620 | + | |
| 2621 | + if (WARN_ON(q->min_reqbufs_allocation > q->max_num_buffers)) | |
| 2622 | + return -EINVAL; | |
| 2623 | + | |
| 2524 | 2624 | INIT_LIST_HEAD(&q->queued_list); |
| 2525 | 2625 | INIT_LIST_HEAD(&q->done_list); |
| 2526 | 2626 | spin_lock_init(&q->done_lock); |
| @@ -2552,9 +2652,9 @@ void vb2_core_queue_release(struct vb2_queue *q) · 25 unmodified lines | ||
| 2552 | 2652 | __vb2_cleanup_fileio(q); |
| 2553 | 2653 | __vb2_queue_cancel(q); |
| 2554 | 2654 | mutex_lock(&q->mmap_lock); |
| 2555 | − __vb2_queue_free(q, vb2_get_num_buffers(q)); | |
| 2556 | − kfree(q->bufs); | |
| 2557 | − q->bufs = NULL; | |
| 2655 | + __vb2_queue_free(q, 0, q->max_num_buffers); | |
| 2656 | + vb2_core_free_buffers_storage(q); | |
| 2657 | + q->is_busy = 0; | |
| 2558 | 2658 | mutex_unlock(&q->mmap_lock); |
| 2559 | 2659 | } |
| 2560 | 2660 | EXPORT_SYMBOL_GPL(vb2_core_queue_release); |
| @@ -2713,7 +2813,6 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) · 152 unmodified lines | ||
| 2713 | 2813 | struct vb2_fileio_data *fileio; |
| 2714 | 2814 | struct vb2_buffer *vb; |
| 2715 | 2815 | int i, ret; |
| 2716 | − unsigned int count = 0; | |
| 2717 | 2816 | |
| 2718 | 2817 | /* |
| 2719 | 2818 | * Sanity check |
| @@ -2734,18 +2833,8 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) · 14 unmodified lines | ||
| 2734 | 2833 | if (q->streaming || vb2_get_num_buffers(q) > 0) |
| 2735 | 2834 | return -EBUSY; |
| 2736 | 2835 | |
| 2737 | − /* | |
| 2738 | − * Start with q->min_queued_buffers + 1, driver can increase it in | |
| 2739 | − * queue_setup() | |
| 2740 | − * | |
| 2741 | − * 'min_queued_buffers' buffers need to be queued up before you | |
| 2742 | − * can start streaming, plus 1 for userspace (or in this case, | |
| 2743 | − * kernelspace) processing. | |
| 2744 | − */ | |
| 2745 | − count = max(2, q->min_queued_buffers + 1); | |
| 2746 | − | |
| 2747 | 2836 | dprintk(q, 3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", |
| 2748 | − (read) ? "read" : "write", count, q->fileio_read_once, | |
| 2837 | + (read) ? "read" : "write", q->min_reqbufs_allocation, q->fileio_read_once, | |
| 2749 | 2838 | q->fileio_write_immediately); |
| 2750 | 2839 | |
| 2751 | 2840 | fileio = kzalloc(sizeof(*fileio), GFP_KERNEL); |
| @@ -2759,13 +2848,19 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) · 7 unmodified lines | ||
| 2759 | 2848 | * Request buffers and use MMAP type to force driver |
| 2760 | 2849 | * to allocate buffers by itself. |
| 2761 | 2850 | */ |
| 2762 | − fileio->count = count; | |
| 2851 | + fileio->count = q->min_reqbufs_allocation; | |
| 2763 | 2852 | fileio->memory = VB2_MEMORY_MMAP; |
| 2764 | 2853 | fileio->type = q->type; |
| 2765 | 2854 | q->fileio = fileio; |
| 2766 | 2855 | ret = vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); |
| 2767 | 2856 | if (ret) |
| 2768 | 2857 | goto err_kfree; |
| 2858 | + /* vb2_fileio_data supports max VB2_MAX_FRAME buffers */ | |
| 2859 | + if (fileio->count > VB2_MAX_FRAME) { | |
| 2860 | + dprintk(q, 1, "fileio: more than VB2_MAX_FRAME buffers requested\n"); | |
| 2861 | + ret = -ENOSPC; | |
| 2862 | + goto err_reqbufs; | |
| 2863 | + } | |
| 2769 | 2864 | |
| 2770 | 2865 | /* |
| 2771 | 2866 | * 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, · 684 unmodified lines | ||
| 685 | 685 | *flags &= V4L2_MEMORY_FLAG_NON_COHERENT; |
| 686 | 686 | } |
| 687 | 687 | |
| 688 | − *caps = V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS; | |
| 688 | + *caps |= V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS; | |
| 689 | 689 | if (q->io_modes & VB2_MMAP) |
| 690 | 690 | *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP; |
| 691 | 691 | if (q->io_modes & VB2_USERPTR) |
| @@ -795,11 +795,15 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) · 103 unmodified lines | ||
| 795 | 795 | for (i = 0; i < requested_planes; i++) |
| 796 | 796 | if (requested_sizes[i] == 0) |
| 797 | 797 | return -EINVAL; |
| 798 | − return ret ? ret : vb2_core_create_bufs(q, create->memory, | |
| 799 | − create->flags, | |
| 800 | − &create->count, | |
| 801 | − requested_planes, | |
| 802 | − requested_sizes); | |
| 798 | + if (ret) | |
| 799 | + return ret; | |
| 800 | + | |
| 801 | + return vb2_core_create_bufs(q, create->memory, | |
| 802 | + create->flags, | |
| 803 | + &create->count, | |
| 804 | + requested_planes, | |
| 805 | + requested_sizes, | |
| 806 | + &create->index); | |
| 803 | 807 | } |
| 804 | 808 | EXPORT_SYMBOL_GPL(vb2_create_bufs); |
| 805 | 809 | |
| @@ -997,6 +1001,24 @@ EXPORT_SYMBOL_GPL(vb2_poll); · 191 unmodified lines | ||
| 997 | 1001 | |
| 998 | 1002 | /* vb2 ioctl helpers */ |
| 999 | 1003 | |
| 1004 | +int vb2_ioctl_remove_bufs(struct file *file, void *priv, | |
| 1005 | + struct v4l2_remove_buffers *d) | |
| 1006 | +{ | |
| 1007 | + struct video_device *vdev = video_devdata(file); | |
| 1008 | + | |
| 1009 | + if (vdev->queue->type != d->type) | |
| 1010 | + return -EINVAL; | |
| 1011 | + | |
| 1012 | + if (d->count == 0) | |
| 1013 | + return 0; | |
| 1014 | + | |
| 1015 | + if (vb2_queue_is_busy(vdev->queue, file)) | |
| 1016 | + return -EBUSY; | |
| 1017 | + | |
| 1018 | + return vb2_core_remove_bufs(vdev->queue, d->index, d->count); | |
| 1019 | +} | |
| 1020 | +EXPORT_SYMBOL_GPL(vb2_ioctl_remove_bufs); | |
| 1021 | + | |
| 1000 | 1022 | int vb2_ioctl_reqbufs(struct file *file, void *priv, |
| 1001 | 1023 | struct v4l2_requestbuffers *p) |
| 1002 | 1024 | { |
| @@ -956,7 +956,7 @@ int dvb_usercopy(struct file *file, · 955 unmodified lines | ||
| 956 | 956 | int (*func)(struct file *file, |
| 957 | 957 | unsigned int cmd, void *arg)) |
| 958 | 958 | { |
| 959 | − char sbuf[128]; | |
| 959 | + char sbuf[128] = {}; | |
| 960 | 960 | void *mbuf = NULL; |
| 961 | 961 | void *parg = NULL; |
| 962 | 962 | int err = -EINVAL; |
| @@ -174,6 +174,6 @@ struct as10x_register_addr { · 173 unmodified lines | ||
| 174 | 174 | uint32_t addr; |
| 175 | 175 | /* register mode access */ |
| 176 | 176 | uint8_t mode; |
| 177 | −}; | |
| 177 | +} __packed; | |
| 178 | 178 | |
| 179 | 179 | #endif |
| @@ -5,4 +5,4 @@ config DVB_CXD2880 · 4 unmodified lines | ||
| 5 | 5 | depends on DVB_CORE && SPI |
| 6 | 6 | default m if !MEDIA_SUBDRV_AUTOSELECT |
| 7 | 7 | help |
| 8 | − Say Y when you want to support this frontend. | |
| 8 | + Say Y when you want to support this frontend. | |
| @@ -33,7 +33,6 @@ · 32 unmodified lines | ||
| 33 | 33 | |
| 34 | 34 | #include <linux/kernel.h> |
| 35 | 35 | #include <linux/errno.h> |
| 36 | −#include <linux/firmware.h> | |
| 37 | 36 | #include <linux/i2c.h> |
| 38 | 37 | |
| 39 | 38 | /* |
| @@ -1910,7 +1909,6 @@ struct drx_demod_instance { · 1870 unmodified lines | ||
| 1910 | 1909 | /* generic demodulator data */ |
| 1911 | 1910 | |
| 1912 | 1911 | struct i2c_adapter *i2c; |
| 1913 | − const struct firmware *firmware; | |
| 1914 | 1912 | }; |
| 1915 | 1913 | |
| 1916 | 1914 | /*------------------------------------------------------------------------- |
| @@ -56,6 +56,7 @@ INCLUDE FILES · 55 unmodified lines | ||
| 56 | 56 | #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ |
| 57 | 57 | |
| 58 | 58 | #include <linux/module.h> |
| 59 | +#include <linux/firmware.h> | |
| 59 | 60 | #include <linux/init.h> |
| 60 | 61 | #include <linux/string.h> |
| 61 | 62 | #include <linux/slab.h> |
| @@ -1444,8 +1445,7 @@ static int drxdap_fasi_read_block(struct i2c_device_addr *dev_addr, · 1382 unmodified lines | ||
| 1444 | 1445 | |
| 1445 | 1446 | /* Read block from I2C **************************************************** */ |
| 1446 | 1447 | do { |
| 1447 | − u16 todo = (datasize < DRXDAP_MAX_RCHUNKSIZE ? | |
| 1448 | − datasize : DRXDAP_MAX_RCHUNKSIZE); | |
| 1448 | + u16 todo = min(datasize, DRXDAP_MAX_RCHUNKSIZE); | |
| 1449 | 1449 | |
| 1450 | 1450 | bufx = 0; |
| 1451 | 1451 | |
| @@ -1659,7 +1659,7 @@ static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr, · 207 unmodified lines | ||
| 1659 | 1659 | Address must be rewritten because HI is reset after data transport and |
| 1660 | 1660 | expects an address. |
| 1661 | 1661 | */ |
| 1662 | − todo = (block_size < datasize ? block_size : datasize); | |
| 1662 | + todo = min(block_size, datasize); | |
| 1663 | 1663 | if (todo == 0) { |
| 1664 | 1664 | u16 overhead_size_i2c_addr = 0; |
| 1665 | 1665 | u16 data_block_size = 0; |
| @@ -1681,9 +1681,7 @@ static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr, · 15 unmodified lines | ||
| 1681 | 1681 | first_err = st; |
| 1682 | 1682 | } |
| 1683 | 1683 | bufx = 0; |
| 1684 | − todo = | |
| 1685 | − (data_block_size < | |
| 1686 | − datasize ? data_block_size : datasize); | |
| 1684 | + todo = min(data_block_size, datasize); | |
| 1687 | 1685 | } |
| 1688 | 1686 | memcpy(&buf[bufx], data, todo); |
| 1689 | 1687 | /* write (address if can do and) data */ |
| @@ -11750,6 +11748,7 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, · 10060 unmodified lines | ||
| 11750 | 11748 | u8 *mc_data = NULL; |
| 11751 | 11749 | unsigned size; |
| 11752 | 11750 | char *mc_file; |
| 11751 | + const struct firmware *fw; | |
| 11753 | 11752 | |
| 11754 | 11753 | /* Check arguments */ |
| 11755 | 11754 | if (!mc_info || !mc_info->mc_file) |
| @@ -11757,28 +11756,22 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, · 1 unmodified line | ||
| 11757 | 11756 | |
| 11758 | 11757 | mc_file = mc_info->mc_file; |
| 11759 | 11758 | |
| 11760 | − if (!demod->firmware) { | |
| 11761 | − const struct firmware *fw = NULL; | |
| 11762 | − | |
| 11763 | − rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent); | |
| 11764 | − if (rc < 0) { | |
| 11765 | − pr_err("Couldn't read firmware %s\n", mc_file); | |
| 11766 | − return rc; | |
| 11767 | − } | |
| 11768 | − demod->firmware = fw; | |
| 11769 | − | |
| 11770 | − if (demod->firmware->size < 2 * sizeof(u16)) { | |
| 11771 | − rc = -EINVAL; | |
| 11772 | − pr_err("Firmware is too short!\n"); | |
| 11773 | − goto release; | |
| 11774 | − } | |
| 11759 | + rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent); | |
| 11760 | + if (rc < 0) { | |
| 11761 | + pr_err("Couldn't read firmware %s\n", mc_file); | |
| 11762 | + return rc; | |
| 11763 | + } | |
| 11775 | 11764 | |
| 11776 | − pr_info("Firmware %s, size %zu\n", | |
| 11777 | − mc_file, demod->firmware->size); | |
| 11765 | + if (fw->size < 2 * sizeof(u16)) { | |
| 11766 | + rc = -EINVAL; | |
| 11767 | + pr_err("Firmware is too short!\n"); | |
| 11768 | + goto release; | |
| 11778 | 11769 | } |
| 11779 | 11770 | |
| 11780 | − mc_data_init = demod->firmware->data; | |
| 11781 | − size = demod->firmware->size; | |
| 11771 | + pr_info("Firmware %s, size %zu\n", mc_file, fw->size); | |
| 11772 | + | |
| 11773 | + mc_data_init = fw->data; | |
| 11774 | + size = fw->size; | |
| 11782 | 11775 | |
| 11783 | 11776 | mc_data = (void *)mc_data_init; |
| 11784 | 11777 | /* Check data */ |
| @@ -11874,7 +11867,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, · 89 unmodified lines | ||
| 11874 | 11867 | 0x0000)) { |
| 11875 | 11868 | pr_err("error reading firmware at pos %zd\n", |
| 11876 | 11869 | mc_data - mc_data_init); |
| 11877 | − return -EIO; | |
| 11870 | + rc = -EIO; | |
| 11871 | + goto release; | |
| 11878 | 11872 | } |
| 11879 | 11873 | |
| 11880 | 11874 | result = memcmp(curr_ptr, mc_data_buffer, |
| @@ -11883,7 +11877,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, · 2 unmodified lines | ||
| 11883 | 11877 | if (result) { |
| 11884 | 11878 | pr_err("error verifying firmware at pos %zd\n", |
| 11885 | 11879 | mc_data - mc_data_init); |
| 11886 | − return -EIO; | |
| 11880 | + rc = -EIO; | |
| 11881 | + goto release; | |
| 11887 | 11882 | } |
| 11888 | 11883 | |
| 11889 | 11884 | curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2)); |
| @@ -11893,17 +11888,17 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, · 3 unmodified lines | ||
| 11893 | 11888 | break; |
| 11894 | 11889 | } |
| 11895 | 11890 | default: |
| 11896 | − return -EINVAL; | |
| 11891 | + rc = -EINVAL; | |
| 11892 | + goto release; | |
| 11897 | 11893 | |
| 11898 | 11894 | } |
| 11899 | 11895 | mc_data += mc_block_nr_bytes; |
| 11900 | 11896 | } |
| 11901 | 11897 | |
| 11902 | − return 0; | |
| 11898 | + rc = 0; | |
| 11903 | 11899 | |
| 11904 | 11900 | release: |
| 11905 | − release_firmware(demod->firmware); | |
| 11906 | − demod->firmware = NULL; | |
| 11901 | + release_firmware(fw); | |
| 11907 | 11902 | |
| 11908 | 11903 | return rc; |
| 11909 | 11904 | } |
| @@ -12271,7 +12266,6 @@ static void drx39xxj_release(struct dvb_frontend *fe) · 361 unmodified lines | ||
| 12271 | 12266 | kfree(demod->my_ext_attr); |
| 12272 | 12267 | kfree(demod->my_common_attr); |
| 12273 | 12268 | kfree(demod->my_i2c_dev_addr); |
| 12274 | − release_firmware(demod->firmware); | |
| 12275 | 12269 | kfree(demod); |
| 12276 | 12270 | kfree(state); |
| 12277 | 12271 | } |
| @@ -2176,6 +2176,11 @@ static int lgdt3306a_probe(struct i2c_client *client) · 2175 unmodified lines | ||
| 2176 | 2176 | struct dvb_frontend *fe; |
| 2177 | 2177 | int ret; |
| 2178 | 2178 | |
| 2179 | + if (!client->dev.platform_data) { | |
| 2180 | + dev_err(&client->dev, "platform data is mandatory\n"); | |
| 2181 | + return -EINVAL; | |
| 2182 | + } | |
| 2183 | + | |
| 2179 | 2184 | config = kmemdup(client->dev.platform_data, |
| 2180 | 2185 | sizeof(struct lgdt3306a_config), GFP_KERNEL); |
| 2181 | 2186 | if (config == NULL) { |
| @@ -1000,6 +1000,13 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) · 999 unmodified lines | ||
| 1000 | 1000 | if (ret) |
| 1001 | 1001 | goto err; |
| 1002 | 1002 | |
| 1003 | + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { | |
| 1004 | + /* to light up the LOCK led */ | |
| 1005 | + ret = m88ds3103_update_bits(dev, 0x11, 0x80, 0x00); | |
| 1006 | + if (ret) | |
| 1007 | + goto err; | |
| 1008 | + } | |
| 1009 | + | |
| 1003 | 1010 | dev->delivery_system = c->delivery_system; |
| 1004 | 1011 | |
| 1005 | 1012 | return 0; |
| @@ -1381,57 +1381,57 @@ static int config_ts(struct mxl *state, enum MXL_HYDRA_DEMOD_ID_E demod_id, · 1380 unmodified lines | ||
| 1381 | 1381 | u32 nco_count_min = 0; |
| 1382 | 1382 | u32 clk_type = 0; |
| 1383 | 1383 | |
| 1384 | − struct MXL_REG_FIELD_T xpt_sync_polarity[MXL_HYDRA_DEMOD_MAX] = { | |
| 1384 | + static const struct MXL_REG_FIELD_T xpt_sync_polarity[MXL_HYDRA_DEMOD_MAX] = { | |
| 1385 | 1385 | {0x90700010, 8, 1}, {0x90700010, 9, 1}, |
| 1386 | 1386 | {0x90700010, 10, 1}, {0x90700010, 11, 1}, |
| 1387 | 1387 | {0x90700010, 12, 1}, {0x90700010, 13, 1}, |
| 1388 | 1388 | {0x90700010, 14, 1}, {0x90700010, 15, 1} }; |
| 1389 | − struct MXL_REG_FIELD_T xpt_clock_polarity[MXL_HYDRA_DEMOD_MAX] = { | |
| 1389 | + static const struct MXL_REG_FIELD_T xpt_clock_polarity[MXL_HYDRA_DEMOD_MAX] = { | |
| 1390 | 1390 | {0x90700010, 16, 1}, {0x90700010, 17, 1}, |
| 1391 | 1391 | {0x90700010, 18, 1}, {0x90700010, 19, 1}, |
| 1392 | 1392 | {0x90700010, 20, 1}, {0x90700010, 21, 1}, |
| 1393 | 1393 | {0x90700010, 22, 1}, {0x90700010, 23, 1} }; |
| 1394 | − struct MXL_REG_FIELD_T xpt_valid_polarity[MXL_HYDRA_DEMOD_MAX] = { | |
| 1394 | + static const struct MXL_REG_FIELD_T xpt_valid_polarity[MXL_HYDRA_DEMOD_MAX] = { | |
| 1395 | 1395 | {0x90700014, 0, 1}, {0x90700014, 1, 1}, |
| 1396 | 1396 | {0x90700014, 2, 1}, {0x90700014, 3, 1}, |
| 1397 | 1397 | {0x90700014, 4, 1}, {0x90700014, 5, 1}, |
| 1398 | 1398 | {0x90700014, 6, 1}, {0x90700014, 7, 1} }; |
| 1399 | − struct MXL_REG_FIELD_T xpt_ts_clock_phase[MXL_HYDRA_DEMOD_MAX] = { | |
| 1399 | + static const struct MXL_REG_FIELD_T xpt_ts_clock_phase[MXL_HYDRA_DEMOD_MAX] = { | |
| 1400 | 1400 | {0x90700018, 0, 3}, {0x90700018, 4, 3}, |
| 1401 | 1401 | {0x90700018, 8, 3}, {0x90700018, 12, 3}, |
| 1402 | 1402 | {0x90700018, 16, 3}, {0x90700018, 20, 3}, |
| 1403 | 1403 | {0x90700018, 24, 3}, {0x90700018, 28, 3} }; |
| 1404 | − struct MXL_REG_FIELD_T xpt_lsb_first[MXL_HYDRA_DEMOD_MAX] = { | |
| 1404 | + static const struct MXL_REG_FIELD_T xpt_lsb_first[MXL_HYDRA_DEMOD_MAX] = { | |
| 1405 | 1405 | {0x9070000C, 16, 1}, {0x9070000C, 17, 1}, |
| 1406 | 1406 | {0x9070000C, 18, 1}, {0x9070000C, 19, 1}, |
| 1407 | 1407 | {0x9070000C, 20, 1}, {0x9070000C, 21, 1}, |
| 1408 | 1408 | {0x9070000C, 22, 1}, {0x9070000C, 23, 1} }; |
| 1409 | − struct MXL_REG_FIELD_T xpt_sync_byte[MXL_HYDRA_DEMOD_MAX] = { | |
| 1409 | + static const struct MXL_REG_FIELD_T xpt_sync_byte[MXL_HYDRA_DEMOD_MAX] = { | |
| 1410 | 1410 | {0x90700010, 0, 1}, {0x90700010, 1, 1}, |
| 1411 | 1411 | {0x90700010, 2, 1}, {0x90700010, 3, 1}, |
| 1412 | 1412 | {0x90700010, 4, 1}, {0x90700010, 5, 1}, |
| 1413 | 1413 | {0x90700010, 6, 1}, {0x90700010, 7, 1} }; |
| 1414 | − struct MXL_REG_FIELD_T xpt_enable_output[MXL_HYDRA_DEMOD_MAX] = { | |
| 1414 | + static const struct MXL_REG_FIELD_T xpt_enable_output[MXL_HYDRA_DEMOD_MAX] = { | |
| 1415 | 1415 | {0x9070000C, 0, 1}, {0x9070000C, 1, 1}, |
| 1416 | 1416 | {0x9070000C, 2, 1}, {0x9070000C, 3, 1}, |
| 1417 | 1417 | {0x9070000C, 4, 1}, {0x9070000C, 5, 1}, |
| 1418 | 1418 | {0x9070000C, 6, 1}, {0x9070000C, 7, 1} }; |
| 1419 | − struct MXL_REG_FIELD_T xpt_err_replace_sync[MXL_HYDRA_DEMOD_MAX] = { | |
| 1419 | + static const struct MXL_REG_FIELD_T xpt_err_replace_sync[MXL_HYDRA_DEMOD_MAX] = { | |
| 1420 | 1420 | {0x9070000C, 24, 1}, {0x9070000C, 25, 1}, |
| 1421 | 1421 | {0x9070000C, 26, 1}, {0x9070000C, 27, 1}, |
| 1422 | 1422 | {0x9070000C, 28, 1}, {0x9070000C, 29, 1}, |
| 1423 | 1423 | {0x9070000C, 30, 1}, {0x9070000C, 31, 1} }; |
| 1424 | − struct MXL_REG_FIELD_T xpt_err_replace_valid[MXL_HYDRA_DEMOD_MAX] = { | |
| 1424 | + static const struct MXL_REG_FIELD_T xpt_err_replace_valid[MXL_HYDRA_DEMOD_MAX] = { | |
| 1425 | 1425 | {0x90700014, 8, 1}, {0x90700014, 9, 1}, |
| 1426 | 1426 | {0x90700014, 10, 1}, {0x90700014, 11, 1}, |
| 1427 | 1427 | {0x90700014, 12, 1}, {0x90700014, 13, 1}, |
| 1428 | 1428 | {0x90700014, 14, 1}, {0x90700014, 15, 1} }; |
| 1429 | − struct MXL_REG_FIELD_T xpt_continuous_clock[MXL_HYDRA_DEMOD_MAX] = { | |
| 1429 | + static const struct MXL_REG_FIELD_T xpt_continuous_clock[MXL_HYDRA_DEMOD_MAX] = { | |
| 1430 | 1430 | {0x907001D4, 0, 1}, {0x907001D4, 1, 1}, |
| 1431 | 1431 | {0x907001D4, 2, 1}, {0x907001D4, 3, 1}, |
| 1432 | 1432 | {0x907001D4, 4, 1}, {0x907001D4, 5, 1}, |
| 1433 | 1433 | {0x907001D4, 6, 1}, {0x907001D4, 7, 1} }; |
| 1434 | − struct MXL_REG_FIELD_T xpt_nco_clock_rate[MXL_HYDRA_DEMOD_MAX] = { | |
| 1434 | + static const struct MXL_REG_FIELD_T xpt_nco_clock_rate[MXL_HYDRA_DEMOD_MAX] = { | |
| 1435 | 1435 | {0x90700044, 16, 80}, {0x90700044, 16, 81}, |
| 1436 | 1436 | {0x90700044, 16, 82}, {0x90700044, 16, 83}, |
| 1437 | 1437 | {0x90700044, 16, 84}, {0x90700044, 16, 85}, |
| @@ -513,10 +513,8 @@ static int si2165_upload_firmware(struct si2165_state *state) · 512 unmodified lines | ||
| 513 | 513 | ret = 0; |
| 514 | 514 | state->firmware_loaded = true; |
| 515 | 515 | error: |
| 516 | − if (fw) { | |
| 517 | − release_firmware(fw); | |
| 518 | − fw = NULL; | |
| 519 | − } | |
| 516 | + release_firmware(fw); | |
| 517 | + fw = NULL; | |
| 520 | 518 | |
| 521 | 519 | return ret; |
| 522 | 520 | } |
| @@ -1277,7 +1277,7 @@ static int stb0899_get_dev_id(struct stb0899_state *state) · 1276 unmodified lines | ||
| 1277 | 1277 | dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver); |
| 1278 | 1278 | CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str); |
| 1279 | 1279 | fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG); |
| 1280 | − if (! (chip_id > 0)) { | |
| 1280 | + if (!chip_id) { | |
| 1281 | 1281 | dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899"); |
| 1282 | 1282 | |
| 1283 | 1283 | return -ENODEV; |
| @@ -410,6 +410,7 @@ static int tda10048_set_if(struct dvb_frontend *fe, u32 bw) · 409 unmodified lines | ||
| 410 | 410 | struct tda10048_config *config = &state->config; |
| 411 | 411 | int i; |
| 412 | 412 | u32 if_freq_khz; |
| 413 | + u64 sample_freq; | |
| 413 | 414 | |
| 414 | 415 | dprintk(1, "%s(bw = %d)\n", __func__, bw); |
| 415 | 416 | |
| @@ -451,9 +452,11 @@ static int tda10048_set_if(struct dvb_frontend *fe, u32 bw) · 35 unmodified lines | ||
| 451 | 452 | dprintk(1, "- pll_pfactor = %d\n", state->pll_pfactor); |
| 452 | 453 | |
| 453 | 454 | /* Calculate the sample frequency */ |
| 454 | − state->sample_freq = state->xtal_hz * (state->pll_mfactor + 45); | |
| 455 | − state->sample_freq /= (state->pll_nfactor + 1); | |
| 456 | − state->sample_freq /= (state->pll_pfactor + 4); | |
| 455 | + sample_freq = state->xtal_hz; | |
| 456 | + sample_freq *= state->pll_mfactor + 45; | |
| 457 | + do_div(sample_freq, state->pll_nfactor + 1); | |
| 458 | + do_div(sample_freq, state->pll_pfactor + 4); | |
| 459 | + state->sample_freq = sample_freq; | |
| 457 | 460 | dprintk(1, "- sample_freq = %d\n", state->sample_freq); |
| 458 | 461 | |
| 459 | 462 | /* Update the I/F */ |
| @@ -328,7 +328,7 @@ static int CalcMainPLL(struct tda_state *state, u32 freq) · 327 unmodified lines | ||
| 328 | 328 | |
| 329 | 329 | OscFreq = (u64) freq * (u64) Div; |
| 330 | 330 | OscFreq *= (u64) 16384; |
| 331 | − do_div(OscFreq, (u64)16000000); | |
| 331 | + do_div(OscFreq, 16000000); | |
| 332 | 332 | MainDiv = OscFreq; |
| 333 | 333 | |
| 334 | 334 | state->m_Regs[MPD] = PostDiv & 0x77; |
| @@ -352,7 +352,7 @@ static int CalcCalPLL(struct tda_state *state, u32 freq) · 17 unmodified lines | ||
| 352 | 352 | OscFreq = (u64)freq * (u64)Div; |
| 353 | 353 | /* CalDiv = u32( OscFreq * 16384 / 16000000 ); */ |
| 354 | 354 | OscFreq *= (u64)16384; |
| 355 | − do_div(OscFreq, (u64)16000000); | |
| 355 | + do_div(OscFreq, 16000000); | |
| 356 | 356 | CalDiv = OscFreq; |
| 357 | 357 | |
| 358 | 358 | state->m_Regs[CPD] = PostDiv; |
| @@ -195,6 +195,7 @@ config VIDEO_IMX334 · 194 unmodified lines | ||
| 195 | 195 | config VIDEO_IMX335 |
| 196 | 196 | tristate "Sony IMX335 sensor support" |
| 197 | 197 | depends on OF_GPIO |
| 198 | + select V4L2_CCI_I2C | |
| 198 | 199 | help |
| 199 | 200 | This is a Video4Linux2 sensor driver for the Sony |
| 200 | 201 | IMX335 camera. |
| @@ -405,6 +406,7 @@ config VIDEO_OV2740 · 204 unmodified lines | ||
| 405 | 406 | config VIDEO_OV4689 |
| 406 | 407 | tristate "OmniVision OV4689 sensor support" |
| 407 | 408 | depends on GPIOLIB |
| 409 | + select V4L2_CCI_I2C | |
| 408 | 410 | help |
| 409 | 411 | This is a Video4Linux2 sensor-level driver for the OmniVision |
| 410 | 412 | OV4689 camera. |
| @@ -1486,7 +1486,7 @@ static int adv7180_probe(struct i2c_client *client) · 1485 unmodified lines | ||
| 1486 | 1486 | if (ret) |
| 1487 | 1487 | goto err_media_entity_cleanup; |
| 1488 | 1488 | |
| 1489 | − if (state->irq) { | |
| 1489 | + if (state->irq > 0) { | |
| 1490 | 1490 | ret = request_threaded_irq(client->irq, NULL, adv7180_irq, |
| 1491 | 1491 | IRQF_ONESHOT | IRQF_TRIGGER_FALLING, |
| 1492 | 1492 | KBUILD_MODNAME, state); |
| @@ -214,7 +214,7 @@ static int adv748x_hdmi_set_video_timings(struct adv748x_state *state, · 213 unmodified lines | ||
| 214 | 214 | * v4l2_subdev_video_ops |
| 215 | 215 | */ |
| 216 | 216 | |
| 217 | −static int adv748x_hdmi_s_dv_timings(struct v4l2_subdev *sd, | |
| 217 | +static int adv748x_hdmi_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 218 | 218 | struct v4l2_dv_timings *timings) |
| 219 | 219 | { |
| 220 | 220 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); |
| @@ -254,7 +254,7 @@ error: · 33 unmodified lines | ||
| 254 | 254 | return ret; |
| 255 | 255 | } |
| 256 | 256 | |
| 257 | −static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd, | |
| 257 | +static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 258 | 258 | struct v4l2_dv_timings *timings) |
| 259 | 259 | { |
| 260 | 260 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); |
| @@ -269,7 +269,7 @@ static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd, · 8 unmodified lines | ||
| 269 | 269 | return 0; |
| 270 | 270 | } |
| 271 | 271 | |
| 272 | −static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd, | |
| 272 | +static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 273 | 273 | struct v4l2_dv_timings *timings) |
| 274 | 274 | { |
| 275 | 275 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); |
| @@ -392,9 +392,6 @@ static int adv748x_hdmi_g_pixelaspect(struct v4l2_subdev *sd, · 116 unmodified lines | ||
| 392 | 392 | } |
| 393 | 393 | |
| 394 | 394 | static const struct v4l2_subdev_video_ops adv748x_video_ops_hdmi = { |
| 395 | − .s_dv_timings = adv748x_hdmi_s_dv_timings, | |
| 396 | − .g_dv_timings = adv748x_hdmi_g_dv_timings, | |
| 397 | − .query_dv_timings = adv748x_hdmi_query_dv_timings, | |
| 398 | 395 | .g_input_status = adv748x_hdmi_g_input_status, |
| 399 | 396 | .s_stream = adv748x_hdmi_s_stream, |
| 400 | 397 | .g_pixelaspect = adv748x_hdmi_g_pixelaspect, |
| @@ -413,7 +410,7 @@ static int adv748x_hdmi_propagate_pixelrate(struct adv748x_hdmi *hdmi) · 12 unmodified lines | ||
| 413 | 410 | if (!tx) |
| 414 | 411 | return -ENOLINK; |
| 415 | 412 | |
| 416 | − adv748x_hdmi_query_dv_timings(&hdmi->sd, &timings); | |
| 413 | + adv748x_hdmi_query_dv_timings(&hdmi->sd, 0, &timings); | |
| 417 | 414 | |
| 418 | 415 | return adv748x_csi2_set_pixelrate(tx, timings.bt.pixelclock); |
| 419 | 416 | } |
| @@ -610,6 +607,9 @@ static const struct v4l2_subdev_pad_ops adv748x_pad_ops_hdmi = { · 190 unmodified lines | ||
| 610 | 607 | .get_fmt = adv748x_hdmi_get_format, |
| 611 | 608 | .get_edid = adv748x_hdmi_get_edid, |
| 612 | 609 | .set_edid = adv748x_hdmi_set_edid, |
| 610 | + .s_dv_timings = adv748x_hdmi_s_dv_timings, | |
| 611 | + .g_dv_timings = adv748x_hdmi_g_dv_timings, | |
| 612 | + .query_dv_timings = adv748x_hdmi_query_dv_timings, | |
| 613 | 613 | .dv_timings_cap = adv748x_hdmi_dv_timings_cap, |
| 614 | 614 | .enum_dv_timings = adv748x_hdmi_enum_dv_timings, |
| 615 | 615 | }; |
| @@ -734,7 +734,7 @@ int adv748x_hdmi_init(struct adv748x_hdmi *hdmi) · 118 unmodified lines | ||
| 734 | 734 | struct v4l2_dv_timings cea1280x720 = V4L2_DV_BT_CEA_1280X720P30; |
| 735 | 735 | int ret; |
| 736 | 736 | |
| 737 | − adv748x_hdmi_s_dv_timings(&hdmi->sd, &cea1280x720); | |
| 737 | + adv748x_hdmi_s_dv_timings(&hdmi->sd, 0, &cea1280x720); | |
| 738 | 738 | |
| 739 | 739 | /* Initialise a default 16:9 aspect ratio */ |
| 740 | 740 | hdmi->aspect_ratio.numerator = 16; |
| @@ -995,8 +995,8 @@ static int adv7511_s_stream(struct v4l2_subdev *sd, int enable) · 994 unmodified lines | ||
| 995 | 995 | return 0; |
| 996 | 996 | } |
| 997 | 997 | |
| 998 | −static int adv7511_s_dv_timings(struct v4l2_subdev *sd, | |
| 999 | − struct v4l2_dv_timings *timings) | |
| 998 | +static int adv7511_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 999 | + struct v4l2_dv_timings *timings) | |
| 1000 | 1000 | { |
| 1001 | 1001 | struct adv7511_state *state = get_adv7511_state(sd); |
| 1002 | 1002 | struct v4l2_bt_timings *bt = &timings->bt; |
| @@ -1004,6 +1004,9 @@ static int adv7511_s_dv_timings(struct v4l2_subdev *sd, · 1 unmodified line | ||
| 1004 | 1004 | |
| 1005 | 1005 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); |
| 1006 | 1006 | |
| 1007 | + if (pad != 0) | |
| 1008 | + return -EINVAL; | |
| 1009 | + | |
| 1007 | 1010 | /* quick sanity check */ |
| 1008 | 1011 | if (!v4l2_valid_dv_timings(timings, &adv7511_timings_cap, NULL, NULL)) |
| 1009 | 1012 | return -EINVAL; |
| @@ -1042,13 +1045,16 @@ static int adv7511_s_dv_timings(struct v4l2_subdev *sd, · 32 unmodified lines | ||
| 1042 | 1045 | return 0; |
| 1043 | 1046 | } |
| 1044 | 1047 | |
| 1045 | −static int adv7511_g_dv_timings(struct v4l2_subdev *sd, | |
| 1048 | +static int adv7511_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1046 | 1049 | struct v4l2_dv_timings *timings) |
| 1047 | 1050 | { |
| 1048 | 1051 | struct adv7511_state *state = get_adv7511_state(sd); |
| 1049 | 1052 | |
| 1050 | 1053 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); |
| 1051 | 1054 | |
| 1055 | + if (pad != 0) | |
| 1056 | + return -EINVAL; | |
| 1057 | + | |
| 1052 | 1058 | if (!timings) |
| 1053 | 1059 | return -EINVAL; |
| 1054 | 1060 | |
| @@ -1078,8 +1084,6 @@ static int adv7511_dv_timings_cap(struct v4l2_subdev *sd, · 23 unmodified lines | ||
| 1078 | 1084 | |
| 1079 | 1085 | static const struct v4l2_subdev_video_ops adv7511_video_ops = { |
| 1080 | 1086 | .s_stream = adv7511_s_stream, |
| 1081 | − .s_dv_timings = adv7511_s_dv_timings, | |
| 1082 | − .g_dv_timings = adv7511_g_dv_timings, | |
| 1083 | 1087 | }; |
| 1084 | 1088 | |
| 1085 | 1089 | /* ------------------------------ AUDIO OPS ------------------------------ */ |
| @@ -1403,6 +1407,8 @@ static const struct v4l2_subdev_pad_ops adv7511_pad_ops = { · 317 unmodified lines | ||
| 1403 | 1407 | .enum_mbus_code = adv7511_enum_mbus_code, |
| 1404 | 1408 | .get_fmt = adv7511_get_fmt, |
| 1405 | 1409 | .set_fmt = adv7511_set_fmt, |
| 1410 | + .s_dv_timings = adv7511_s_dv_timings, | |
| 1411 | + .g_dv_timings = adv7511_g_dv_timings, | |
| 1406 | 1412 | .enum_dv_timings = adv7511_enum_dv_timings, |
| 1407 | 1413 | .dv_timings_cap = adv7511_dv_timings_cap, |
| 1408 | 1414 | }; |
| @@ -1557,8 +1557,8 @@ static unsigned int adv76xx_read_hdmi_pixelclock(struct v4l2_subdev *sd) · 1556 unmodified lines | ||
| 1557 | 1557 | return freq; |
| 1558 | 1558 | } |
| 1559 | 1559 | |
| 1560 | −static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, | |
| 1561 | − struct v4l2_dv_timings *timings) | |
| 1560 | +static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1561 | + struct v4l2_dv_timings *timings) | |
| 1562 | 1562 | { |
| 1563 | 1563 | struct adv76xx_state *state = to_state(sd); |
| 1564 | 1564 | const struct adv76xx_chip_info *info = state->info; |
| @@ -1687,8 +1687,8 @@ found: · 122 unmodified lines | ||
| 1687 | 1687 | return 0; |
| 1688 | 1688 | } |
| 1689 | 1689 | |
| 1690 | −static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, | |
| 1691 | − struct v4l2_dv_timings *timings) | |
| 1690 | +static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1691 | + struct v4l2_dv_timings *timings) | |
| 1692 | 1692 | { |
| 1693 | 1693 | struct adv76xx_state *state = to_state(sd); |
| 1694 | 1694 | struct v4l2_bt_timings *bt; |
| @@ -1730,8 +1730,8 @@ static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, · 35 unmodified lines | ||
| 1730 | 1730 | return 0; |
| 1731 | 1731 | } |
| 1732 | 1732 | |
| 1733 | −static int adv76xx_g_dv_timings(struct v4l2_subdev *sd, | |
| 1734 | − struct v4l2_dv_timings *timings) | |
| 1733 | +static int adv76xx_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1734 | + struct v4l2_dv_timings *timings) | |
| 1735 | 1735 | { |
| 1736 | 1736 | struct adv76xx_state *state = to_state(sd); |
| 1737 | 1737 | |
| @@ -2607,7 +2607,7 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) · 869 unmodified lines | ||
| 2607 | 2607 | stdi.lcf, stdi.bl, stdi.lcvs, |
| 2608 | 2608 | stdi.interlaced ? "interlaced" : "progressive", |
| 2609 | 2609 | stdi.hs_pol, stdi.vs_pol); |
| 2610 | − if (adv76xx_query_dv_timings(sd, &timings)) | |
| 2610 | + if (adv76xx_query_dv_timings(sd, 0, &timings)) | |
| 2611 | 2611 | v4l2_info(sd, "No video detected\n"); |
| 2612 | 2612 | else |
| 2613 | 2613 | v4l2_print_dv_timings(sd->name, "Detected format: ", |
| @@ -2726,9 +2726,6 @@ static const struct v4l2_subdev_core_ops adv76xx_core_ops = { · 112 unmodified lines | ||
| 2726 | 2726 | static const struct v4l2_subdev_video_ops adv76xx_video_ops = { |
| 2727 | 2727 | .s_routing = adv76xx_s_routing, |
| 2728 | 2728 | .g_input_status = adv76xx_g_input_status, |
| 2729 | − .s_dv_timings = adv76xx_s_dv_timings, | |
| 2730 | − .g_dv_timings = adv76xx_g_dv_timings, | |
| 2731 | − .query_dv_timings = adv76xx_query_dv_timings, | |
| 2732 | 2729 | }; |
| 2733 | 2730 | |
| 2734 | 2731 | static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = { |
| @@ -2738,6 +2735,9 @@ static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = { · 3 unmodified lines | ||
| 2738 | 2735 | .set_fmt = adv76xx_set_format, |
| 2739 | 2736 | .get_edid = adv76xx_get_edid, |
| 2740 | 2737 | .set_edid = adv76xx_set_edid, |
| 2738 | + .s_dv_timings = adv76xx_s_dv_timings, | |
| 2739 | + .g_dv_timings = adv76xx_g_dv_timings, | |
| 2740 | + .query_dv_timings = adv76xx_query_dv_timings, | |
| 2741 | 2741 | .dv_timings_cap = adv76xx_dv_timings_cap, |
| 2742 | 2742 | .enum_dv_timings = adv76xx_enum_dv_timings, |
| 2743 | 2743 | }; |
| @@ -1518,7 +1518,7 @@ static void adv7842_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, · 1517 unmodified lines | ||
| 1518 | 1518 | timings->bt.flags |= V4L2_DV_FL_CAN_DETECT_REDUCED_FPS; |
| 1519 | 1519 | } |
| 1520 | 1520 | |
| 1521 | −static int adv7842_query_dv_timings(struct v4l2_subdev *sd, | |
| 1521 | +static int adv7842_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1522 | 1522 | struct v4l2_dv_timings *timings) |
| 1523 | 1523 | { |
| 1524 | 1524 | struct adv7842_state *state = to_state(sd); |
| @@ -1527,6 +1527,9 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, · 2 unmodified lines | ||
| 1527 | 1527 | |
| 1528 | 1528 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); |
| 1529 | 1529 | |
| 1530 | + if (pad != 0) | |
| 1531 | + return -EINVAL; | |
| 1532 | + | |
| 1530 | 1533 | memset(timings, 0, sizeof(struct v4l2_dv_timings)); |
| 1531 | 1534 | |
| 1532 | 1535 | /* SDP block */ |
| @@ -1643,7 +1646,7 @@ found: · 110 unmodified lines | ||
| 1643 | 1646 | return 0; |
| 1644 | 1647 | } |
| 1645 | 1648 | |
| 1646 | −static int adv7842_s_dv_timings(struct v4l2_subdev *sd, | |
| 1649 | +static int adv7842_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1647 | 1650 | struct v4l2_dv_timings *timings) |
| 1648 | 1651 | { |
| 1649 | 1652 | struct adv7842_state *state = to_state(sd); |
| @@ -1652,6 +1655,9 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd, · 2 unmodified lines | ||
| 1652 | 1655 | |
| 1653 | 1656 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); |
| 1654 | 1657 | |
| 1658 | + if (pad != 0) | |
| 1659 | + return -EINVAL; | |
| 1660 | + | |
| 1655 | 1661 | if (state->mode == ADV7842_MODE_SDP) |
| 1656 | 1662 | return -ENODATA; |
| 1657 | 1663 | |
| @@ -1689,11 +1695,14 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd, · 31 unmodified lines | ||
| 1689 | 1695 | return 0; |
| 1690 | 1696 | } |
| 1691 | 1697 | |
| 1692 | −static int adv7842_g_dv_timings(struct v4l2_subdev *sd, | |
| 1698 | +static int adv7842_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1693 | 1699 | struct v4l2_dv_timings *timings) |
| 1694 | 1700 | { |
| 1695 | 1701 | struct adv7842_state *state = to_state(sd); |
| 1696 | 1702 | |
| 1703 | + if (pad != 0) | |
| 1704 | + return -EINVAL; | |
| 1705 | + | |
| 1697 | 1706 | if (state->mode == ADV7842_MODE_SDP) |
| 1698 | 1707 | return -ENODATA; |
| 1699 | 1708 | *timings = state->timings; |
| @@ -2780,7 +2789,7 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) · 1080 unmodified lines | ||
| 2780 | 2789 | "interlaced" : "progressive", |
| 2781 | 2790 | hs_pol, vs_pol); |
| 2782 | 2791 | } |
| 2783 | − if (adv7842_query_dv_timings(sd, &timings)) | |
| 2792 | + if (adv7842_query_dv_timings(sd, 0, &timings)) | |
| 2784 | 2793 | v4l2_info(sd, "No video detected\n"); |
| 2785 | 2794 | else |
| 2786 | 2795 | v4l2_print_dv_timings(sd->name, "Detected format: ", |
| @@ -3226,7 +3235,7 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd) · 439 unmodified lines | ||
| 3226 | 3235 | |
| 3227 | 3236 | memset(&state->timings, 0, sizeof(struct v4l2_dv_timings)); |
| 3228 | 3237 | |
| 3229 | − adv7842_s_dv_timings(sd, &timings); | |
| 3238 | + adv7842_s_dv_timings(sd, 0, &timings); | |
| 3230 | 3239 | |
| 3231 | 3240 | return ret; |
| 3232 | 3241 | } |
| @@ -3298,9 +3307,6 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = { · 65 unmodified lines | ||
| 3298 | 3307 | .s_routing = adv7842_s_routing, |
| 3299 | 3308 | .querystd = adv7842_querystd, |
| 3300 | 3309 | .g_input_status = adv7842_g_input_status, |
| 3301 | − .s_dv_timings = adv7842_s_dv_timings, | |
| 3302 | − .g_dv_timings = adv7842_g_dv_timings, | |
| 3303 | − .query_dv_timings = adv7842_query_dv_timings, | |
| 3304 | 3310 | }; |
| 3305 | 3311 | |
| 3306 | 3312 | static const struct v4l2_subdev_pad_ops adv7842_pad_ops = { |
| @@ -3309,6 +3315,9 @@ static const struct v4l2_subdev_pad_ops adv7842_pad_ops = { · 2 unmodified lines | ||
| 3309 | 3315 | .set_fmt = adv7842_set_format, |
| 3310 | 3316 | .get_edid = adv7842_get_edid, |
| 3311 | 3317 | .set_edid = adv7842_set_edid, |
| 3318 | + .s_dv_timings = adv7842_s_dv_timings, | |
| 3319 | + .g_dv_timings = adv7842_g_dv_timings, | |
| 3320 | + .query_dv_timings = adv7842_query_dv_timings, | |
| 3312 | 3321 | .enum_dv_timings = adv7842_enum_dv_timings, |
| 3313 | 3322 | .dv_timings_cap = adv7842_dv_timings_cap, |
| 3314 | 3323 | }; |
| @@ -310,8 +310,8 @@ module_i2c_driver(dw9714_i2c_driver); · 309 unmodified lines | ||
| 310 | 310 | |
| 311 | 311 | MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); |
| 312 | 312 | MODULE_AUTHOR("Jian Xu Zheng"); |
| 313 | −MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>"); | |
| 314 | −MODULE_AUTHOR("Jouni Ukkonen <jouni.ukkonen@intel.com>"); | |
| 315 | −MODULE_AUTHOR("Tommi Franttila <tommi.franttila@intel.com>"); | |
| 313 | +MODULE_AUTHOR("Yuning Pu"); | |
| 314 | +MODULE_AUTHOR("Jouni Ukkonen"); | |
| 315 | +MODULE_AUTHOR("Tommi Franttila"); | |
| 316 | 316 | MODULE_DESCRIPTION("DW9714 VCM driver"); |
| 317 | 317 | MODULE_LICENSE("GPL v2"); |
| @@ -1475,7 +1475,7 @@ err_mutex: · 1474 unmodified lines | ||
| 1475 | 1475 | return ret; |
| 1476 | 1476 | } |
| 1477 | 1477 | |
| 1478 | −static void __exit et8ek8_remove(struct i2c_client *client) | |
| 1478 | +static void et8ek8_remove(struct i2c_client *client) | |
| 1479 | 1479 | { |
| 1480 | 1480 | struct v4l2_subdev *subdev = i2c_get_clientdata(client); |
| 1481 | 1481 | struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev); |
| @@ -1517,7 +1517,7 @@ static struct i2c_driver et8ek8_i2c_driver = { · 35 unmodified lines | ||
| 1517 | 1517 | .of_match_table = et8ek8_of_table, |
| 1518 | 1518 | }, |
| 1519 | 1519 | .probe = et8ek8_probe, |
| 1520 | − .remove = __exit_p(et8ek8_remove), | |
| 1520 | + .remove = et8ek8_remove, | |
| 1521 | 1521 | .id_table = et8ek8_id_table, |
| 1522 | 1522 | }; |
| 1523 | 1523 | |
| @@ -3,10 +3,13 @@ · 2 unmodified lines | ||
| 3 | 3 | |
| 4 | 4 | #include <asm/unaligned.h> |
| 5 | 5 | #include <linux/acpi.h> |
| 6 | +#include <linux/clk.h> | |
| 6 | 7 | #include <linux/delay.h> |
| 8 | +#include <linux/gpio/consumer.h> | |
| 7 | 9 | #include <linux/i2c.h> |
| 8 | 10 | #include <linux/module.h> |
| 9 | 11 | #include <linux/pm_runtime.h> |
| 12 | +#include <linux/regulator/consumer.h> | |
| 10 | 13 | #include <media/v4l2-ctrls.h> |
| 11 | 14 | #include <media/v4l2-device.h> |
| 12 | 15 | #include <media/v4l2-fwnode.h> |
| @@ -633,6 +636,11 @@ struct hi556 { · 620 unmodified lines | ||
| 633 | 636 | struct v4l2_ctrl *hblank; |
| 634 | 637 | struct v4l2_ctrl *exposure; |
| 635 | 638 | |
| 639 | + /* GPIOs, clocks, etc. */ | |
| 640 | + struct gpio_desc *reset_gpio; | |
| 641 | + struct clk *clk; | |
| 642 | + struct regulator *avdd; | |
| 643 | + | |
| 636 | 644 | /* Current mode */ |
| 637 | 645 | const struct hi556_mode *cur_mode; |
| 638 | 646 | |
| @@ -1206,8 +1214,18 @@ static int hi556_check_hwcfg(struct device *dev) · 567 unmodified lines | ||
| 1206 | 1214 | int ret = 0; |
| 1207 | 1215 | unsigned int i, j; |
| 1208 | 1216 | |
| 1209 | − if (!fwnode) | |
| 1210 | − return -ENXIO; | |
| 1217 | + /* | |
| 1218 | + * Sometimes the fwnode graph is initialized by the bridge driver, | |
| 1219 | + * wait for this. | |
| 1220 | + */ | |
| 1221 | + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); | |
| 1222 | + if (!ep) | |
| 1223 | + return -EPROBE_DEFER; | |
| 1224 | + | |
| 1225 | + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); | |
| 1226 | + fwnode_handle_put(ep); | |
| 1227 | + if (ret) | |
| 1228 | + return ret; | |
| 1211 | 1229 | |
| 1212 | 1230 | ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); |
| 1213 | 1231 | if (ret) { |
| @@ -1220,15 +1238,6 @@ static int hi556_check_hwcfg(struct device *dev) · 6 unmodified lines | ||
| 1220 | 1238 | return -EINVAL; |
| 1221 | 1239 | } |
| 1222 | 1240 | |
| 1223 | − ep = fwnode_graph_get_next_endpoint(fwnode, NULL); | |
| 1224 | − if (!ep) | |
| 1225 | − return -ENXIO; | |
| 1226 | − | |
| 1227 | − ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); | |
| 1228 | − fwnode_handle_put(ep); | |
| 1229 | − if (ret) | |
| 1230 | − return ret; | |
| 1231 | − | |
| 1232 | 1241 | if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { |
| 1233 | 1242 | dev_err(dev, "number of CSI2 data lanes %d is not supported", |
| 1234 | 1243 | bus_cfg.bus.mipi_csi2.num_data_lanes); |
| @@ -1275,6 +1284,47 @@ static void hi556_remove(struct i2c_client *client) · 40 unmodified lines | ||
| 1275 | 1284 | mutex_destroy(&hi556->mutex); |
| 1276 | 1285 | } |
| 1277 | 1286 | |
| 1287 | +static int hi556_suspend(struct device *dev) | |
| 1288 | +{ | |
| 1289 | + struct v4l2_subdev *sd = dev_get_drvdata(dev); | |
| 1290 | + struct hi556 *hi556 = to_hi556(sd); | |
| 1291 | + int ret; | |
| 1292 | + | |
| 1293 | + gpiod_set_value_cansleep(hi556->reset_gpio, 1); | |
| 1294 | + | |
| 1295 | + ret = regulator_disable(hi556->avdd); | |
| 1296 | + if (ret) { | |
| 1297 | + dev_err(dev, "failed to disable avdd: %d\n", ret); | |
| 1298 | + gpiod_set_value_cansleep(hi556->reset_gpio, 0); | |
| 1299 | + return ret; | |
| 1300 | + } | |
| 1301 | + | |
| 1302 | + clk_disable_unprepare(hi556->clk); | |
| 1303 | + return 0; | |
| 1304 | +} | |
| 1305 | + | |
| 1306 | +static int hi556_resume(struct device *dev) | |
| 1307 | +{ | |
| 1308 | + struct v4l2_subdev *sd = dev_get_drvdata(dev); | |
| 1309 | + struct hi556 *hi556 = to_hi556(sd); | |
| 1310 | + int ret; | |
| 1311 | + | |
| 1312 | + ret = clk_prepare_enable(hi556->clk); | |
| 1313 | + if (ret) | |
| 1314 | + return ret; | |
| 1315 | + | |
| 1316 | + ret = regulator_enable(hi556->avdd); | |
| 1317 | + if (ret) { | |
| 1318 | + dev_err(dev, "failed to enable avdd: %d\n", ret); | |
| 1319 | + clk_disable_unprepare(hi556->clk); | |
| 1320 | + return ret; | |
| 1321 | + } | |
| 1322 | + | |
| 1323 | + gpiod_set_value_cansleep(hi556->reset_gpio, 0); | |
| 1324 | + usleep_range(5000, 5500); | |
| 1325 | + return 0; | |
| 1326 | +} | |
| 1327 | + | |
| 1278 | 1328 | static int hi556_probe(struct i2c_client *client) |
| 1279 | 1329 | { |
| 1280 | 1330 | struct hi556 *hi556; |
| @@ -1294,12 +1344,35 @@ static int hi556_probe(struct i2c_client *client) · 13 unmodified lines | ||
| 1294 | 1344 | |
| 1295 | 1345 | v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops); |
| 1296 | 1346 | |
| 1347 | + hi556->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", | |
| 1348 | + GPIOD_OUT_HIGH); | |
| 1349 | + if (IS_ERR(hi556->reset_gpio)) | |
| 1350 | + return dev_err_probe(&client->dev, PTR_ERR(hi556->reset_gpio), | |
| 1351 | + "failed to get reset GPIO\n"); | |
| 1352 | + | |
| 1353 | + hi556->clk = devm_clk_get_optional(&client->dev, "clk"); | |
| 1354 | + if (IS_ERR(hi556->clk)) | |
| 1355 | + return dev_err_probe(&client->dev, PTR_ERR(hi556->clk), | |
| 1356 | + "failed to get clock\n"); | |
| 1357 | + | |
| 1358 | + /* The regulator core will provide a "dummy" regulator if necessary */ | |
| 1359 | + hi556->avdd = devm_regulator_get(&client->dev, "avdd"); | |
| 1360 | + if (IS_ERR(hi556->avdd)) | |
| 1361 | + return dev_err_probe(&client->dev, PTR_ERR(hi556->avdd), | |
| 1362 | + "failed to get avdd regulator\n"); | |
| 1363 | + | |
| 1297 | 1364 | full_power = acpi_dev_state_d0(&client->dev); |
| 1298 | 1365 | if (full_power) { |
| 1366 | + /* Ensure non ACPI managed resources are enabled */ | |
| 1367 | + ret = hi556_resume(&client->dev); | |
| 1368 | + if (ret) | |
| 1369 | + return dev_err_probe(&client->dev, ret, | |
| 1370 | + "failed to power on sensor\n"); | |
| 1371 | + | |
| 1299 | 1372 | ret = hi556_identify_module(hi556); |
| 1300 | 1373 | if (ret) { |
| 1301 | 1374 | dev_err(&client->dev, "failed to find sensor: %d", ret); |
| 1302 | − return ret; | |
| 1375 | + goto probe_error_power_off; | |
| 1303 | 1376 | } |
| 1304 | 1377 | } |
| 1305 | 1378 | |
| @@ -1344,9 +1417,16 @@ probe_error_v4l2_ctrl_handler_free: · 38 unmodified lines | ||
| 1344 | 1417 | v4l2_ctrl_handler_free(hi556->sd.ctrl_handler); |
| 1345 | 1418 | mutex_destroy(&hi556->mutex); |
| 1346 | 1419 | |
| 1420 | +probe_error_power_off: | |
| 1421 | + if (full_power) | |
| 1422 | + hi556_suspend(&client->dev); | |
| 1423 | + | |
| 1347 | 1424 | return ret; |
| 1348 | 1425 | } |
| 1349 | 1426 | |
| 1427 | +static DEFINE_RUNTIME_DEV_PM_OPS(hi556_pm_ops, hi556_suspend, hi556_resume, | |
| 1428 | + NULL); | |
| 1429 | + | |
| 1350 | 1430 | #ifdef CONFIG_ACPI |
| 1351 | 1431 | static const struct acpi_device_id hi556_acpi_ids[] = { |
| 1352 | 1432 | {"INT3537"}, |
| @@ -1360,6 +1440,7 @@ static struct i2c_driver hi556_i2c_driver = { · 7 unmodified lines | ||
| 1360 | 1440 | .driver = { |
| 1361 | 1441 | .name = "hi556", |
| 1362 | 1442 | .acpi_match_table = ACPI_PTR(hi556_acpi_ids), |
| 1443 | + .pm = pm_sleep_ptr(&hi556_pm_ops), | |
| 1363 | 1444 | }, |
| 1364 | 1445 | .probe = hi556_probe, |
| 1365 | 1446 | .remove = hi556_remove, |
| @@ -1114,6 +1114,7 @@ free_ctrl: · 1113 unmodified lines | ||
| 1114 | 1114 | v4l2_ctrl_handler_free(&imx214->ctrls); |
| 1115 | 1115 | error_power_off: |
| 1116 | 1116 | pm_runtime_disable(imx214->dev); |
| 1117 | + regulator_bulk_disable(IMX214_NUM_SUPPLIES, imx214->supplies); | |
| 1117 | 1118 | |
| 1118 | 1119 | return ret; |
| 1119 | 1120 | } |
| @@ -551,8 +551,7 @@ static int imx219_init_controls(struct imx219 *imx219) · 550 unmodified lines | ||
| 551 | 551 | |
| 552 | 552 | if (ctrl_hdlr->error) { |
| 553 | 553 | ret = ctrl_hdlr->error; |
| 554 | − dev_err(&client->dev, "%s control init failed (%d)\n", | |
| 555 | − __func__, ret); | |
| 554 | + dev_err_probe(&client->dev, ret, "Control init failed\n"); | |
| 556 | 555 | goto error; |
| 557 | 556 | } |
| 558 | 557 | |
| @@ -1024,17 +1023,15 @@ static int imx219_identify_module(struct imx219 *imx219) · 465 unmodified lines | ||
| 1024 | 1023 | u64 val; |
| 1025 | 1024 | |
| 1026 | 1025 | ret = cci_read(imx219->regmap, IMX219_REG_CHIP_ID, &val, NULL); |
| 1027 | − if (ret) { | |
| 1028 | − dev_err(&client->dev, "failed to read chip id %x\n", | |
| 1029 | − IMX219_CHIP_ID); | |
| 1030 | − return ret; | |
| 1031 | − } | |
| 1026 | + if (ret) | |
| 1027 | + return dev_err_probe(&client->dev, ret, | |
| 1028 | + "failed to read chip id %x\n", | |
| 1029 | + IMX219_CHIP_ID); | |
| 1032 | 1030 | |
| 1033 | − if (val != IMX219_CHIP_ID) { | |
| 1034 | − dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", | |
| 1035 | − IMX219_CHIP_ID, val); | |
| 1036 | − return -EIO; | |
| 1037 | − } | |
| 1031 | + if (val != IMX219_CHIP_ID) | |
| 1032 | + return dev_err_probe(&client->dev, -EIO, | |
| 1033 | + "chip id mismatch: %x!=%llx\n", | |
| 1034 | + IMX219_CHIP_ID, val); | |
| 1038 | 1035 | |
| 1039 | 1036 | return 0; |
| 1040 | 1037 | } |
| @@ -1048,35 +1045,36 @@ static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) · 7 unmodified lines | ||
| 1048 | 1045 | int ret = -EINVAL; |
| 1049 | 1046 | |
| 1050 | 1047 | endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); |
| 1051 | − if (!endpoint) { | |
| 1052 | − dev_err(dev, "endpoint node not found\n"); | |
| 1053 | − return -EINVAL; | |
| 1054 | − } | |
| 1048 | + if (!endpoint) | |
| 1049 | + return dev_err_probe(dev, -EINVAL, "endpoint node not found\n"); | |
| 1055 | 1050 | |
| 1056 | 1051 | if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { |
| 1057 | − dev_err(dev, "could not parse endpoint\n"); | |
| 1052 | + dev_err_probe(dev, -EINVAL, "could not parse endpoint\n"); | |
| 1058 | 1053 | goto error_out; |
| 1059 | 1054 | } |
| 1060 | 1055 | |
| 1061 | 1056 | /* Check the number of MIPI CSI2 data lanes */ |
| 1062 | 1057 | if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2 && |
| 1063 | 1058 | ep_cfg.bus.mipi_csi2.num_data_lanes != 4) { |
| 1064 | − dev_err(dev, "only 2 or 4 data lanes are currently supported\n"); | |
| 1059 | + dev_err_probe(dev, -EINVAL, | |
| 1060 | + "only 2 or 4 data lanes are currently supported\n"); | |
| 1065 | 1061 | goto error_out; |
| 1066 | 1062 | } |
| 1067 | 1063 | imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes; |
| 1068 | 1064 | |
| 1069 | 1065 | /* Check the link frequency set in device tree */ |
| 1070 | 1066 | if (!ep_cfg.nr_of_link_frequencies) { |
| 1071 | − dev_err(dev, "link-frequency property not found in DT\n"); | |
| 1067 | + dev_err_probe(dev, -EINVAL, | |
| 1068 | + "link-frequency property not found in DT\n"); | |
| 1072 | 1069 | goto error_out; |
| 1073 | 1070 | } |
| 1074 | 1071 | |
| 1075 | 1072 | if (ep_cfg.nr_of_link_frequencies != 1 || |
| 1076 | 1073 | (ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ? |
| 1077 | 1074 | IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) { |
| 1078 | − dev_err(dev, "Link frequency not supported: %lld\n", | |
| 1079 | − ep_cfg.link_frequencies[0]); | |
| 1075 | + dev_err_probe(dev, -EINVAL, | |
| 1076 | + "Link frequency not supported: %lld\n", | |
| 1077 | + ep_cfg.link_frequencies[0]); | |
| 1080 | 1078 | goto error_out; |
| 1081 | 1079 | } |
| 1082 | 1080 | |
| @@ -1107,31 +1105,25 @@ static int imx219_probe(struct i2c_client *client) · 24 unmodified lines | ||
| 1107 | 1105 | return -EINVAL; |
| 1108 | 1106 | |
| 1109 | 1107 | imx219->regmap = devm_cci_regmap_init_i2c(client, 16); |
| 1110 | − if (IS_ERR(imx219->regmap)) { | |
| 1111 | − ret = PTR_ERR(imx219->regmap); | |
| 1112 | − dev_err(dev, "failed to initialize CCI: %d\n", ret); | |
| 1113 | − return ret; | |
| 1114 | − } | |
| 1108 | + if (IS_ERR(imx219->regmap)) | |
| 1109 | + return dev_err_probe(dev, PTR_ERR(imx219->regmap), | |
| 1110 | + "failed to initialize CCI\n"); | |
| 1115 | 1111 | |
| 1116 | 1112 | /* Get system clock (xclk) */ |
| 1117 | 1113 | imx219->xclk = devm_clk_get(dev, NULL); |
| 1118 | − if (IS_ERR(imx219->xclk)) { | |
| 1119 | − dev_err(dev, "failed to get xclk\n"); | |
| 1120 | − return PTR_ERR(imx219->xclk); | |
| 1121 | − } | |
| 1114 | + if (IS_ERR(imx219->xclk)) | |
| 1115 | + return dev_err_probe(dev, PTR_ERR(imx219->xclk), | |
| 1116 | + "failed to get xclk\n"); | |
| 1122 | 1117 | |
| 1123 | 1118 | imx219->xclk_freq = clk_get_rate(imx219->xclk); |
| 1124 | − if (imx219->xclk_freq != IMX219_XCLK_FREQ) { | |
| 1125 | − dev_err(dev, "xclk frequency not supported: %d Hz\n", | |
| 1126 | − imx219->xclk_freq); | |
| 1127 | − return -EINVAL; | |
| 1128 | − } | |
| 1119 | + if (imx219->xclk_freq != IMX219_XCLK_FREQ) | |
| 1120 | + return dev_err_probe(dev, -EINVAL, | |
| 1121 | + "xclk frequency not supported: %d Hz\n", | |
| 1122 | + imx219->xclk_freq); | |
| 1129 | 1123 | |
| 1130 | 1124 | ret = imx219_get_regulators(imx219); |
| 1131 | − if (ret) { | |
| 1132 | − dev_err(dev, "failed to get regulators\n"); | |
| 1133 | − return ret; | |
| 1134 | − } | |
| 1125 | + if (ret) | |
| 1126 | + return dev_err_probe(dev, ret, "failed to get regulators\n"); | |
| 1135 | 1127 | |
| 1136 | 1128 | /* Request optional enable pin */ |
| 1137 | 1129 | imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset", |
| @@ -1183,20 +1175,21 @@ static int imx219_probe(struct i2c_client *client) · 45 unmodified lines | ||
| 1183 | 1175 | |
| 1184 | 1176 | ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad); |
| 1185 | 1177 | if (ret) { |
| 1186 | − dev_err(dev, "failed to init entity pads: %d\n", ret); | |
| 1178 | + dev_err_probe(dev, ret, "failed to init entity pads\n"); | |
| 1187 | 1179 | goto error_handler_free; |
| 1188 | 1180 | } |
| 1189 | 1181 | |
| 1190 | 1182 | imx219->sd.state_lock = imx219->ctrl_handler.lock; |
| 1191 | 1183 | ret = v4l2_subdev_init_finalize(&imx219->sd); |
| 1192 | 1184 | if (ret < 0) { |
| 1193 | − dev_err(dev, "subdev init error: %d\n", ret); | |
| 1185 | + dev_err_probe(dev, ret, "subdev init error\n"); | |
| 1194 | 1186 | goto error_media_entity; |
| 1195 | 1187 | } |
| 1196 | 1188 | |
| 1197 | 1189 | ret = v4l2_async_register_subdev_sensor(&imx219->sd); |
| 1198 | 1190 | if (ret < 0) { |
| 1199 | − dev_err(dev, "failed to register sensor sub-device: %d\n", ret); | |
| 1191 | + dev_err_probe(dev, ret, | |
| 1192 | + "failed to register sensor sub-device\n"); | |
| 1200 | 1193 | goto error_subdev_cleanup; |
| 1201 | 1194 | } |
| 1202 | 1195 | |
| @@ -11,57 +11,108 @@ · 10 unmodified lines | ||
| 11 | 11 | #include <linux/i2c.h> |
| 12 | 12 | #include <linux/module.h> |
| 13 | 13 | #include <linux/pm_runtime.h> |
| 14 | +#include <linux/regmap.h> | |
| 14 | 15 | |
| 16 | +#include <media/v4l2-cci.h> | |
| 15 | 17 | #include <media/v4l2-ctrls.h> |
| 16 | 18 | #include <media/v4l2-fwnode.h> |
| 17 | 19 | #include <media/v4l2-subdev.h> |
| 18 | 20 | |
| 19 | 21 | /* Streaming Mode */ |
| 20 | −#define IMX335_REG_MODE_SELECT 0x3000 | |
| 21 | −#define IMX335_MODE_STANDBY 0x01 | |
| 22 | −#define IMX335_MODE_STREAMING 0x00 | |
| 22 | +#define IMX335_REG_MODE_SELECT CCI_REG8(0x3000) | |
| 23 | +#define IMX335_MODE_STANDBY 0x01 | |
| 24 | +#define IMX335_MODE_STREAMING 0x00 | |
| 25 | + | |
| 26 | +/* Group hold register */ | |
| 27 | +#define IMX335_REG_HOLD CCI_REG8(0x3001) | |
| 28 | + | |
| 29 | +#define IMX335_REG_MASTER_MODE CCI_REG8(0x3002) | |
| 30 | +#define IMX335_REG_BCWAIT_TIME CCI_REG8(0x300c) | |
| 31 | +#define IMX335_REG_CPWAIT_TIME CCI_REG8(0x300d) | |
| 32 | +#define IMX335_REG_WINMODE CCI_REG8(0x3018) | |
| 33 | +#define IMX335_REG_HTRIMMING_START CCI_REG16_LE(0x302c) | |
| 34 | +#define IMX335_REG_HNUM CCI_REG8(0x302e) | |
| 23 | 35 | |
| 24 | 36 | /* Lines per frame */ |
| 25 | −#define IMX335_REG_LPFR 0x3030 | |
| 37 | +#define IMX335_REG_VMAX CCI_REG24_LE(0x3030) | |
| 26 | 38 | |
| 27 | −/* Chip ID */ | |
| 28 | −#define IMX335_REG_ID 0x3912 | |
| 29 | −#define IMX335_ID 0x00 | |
| 30 | − | |
| 31 | −/* Exposure control */ | |
| 32 | −#define IMX335_REG_SHUTTER 0x3058 | |
| 33 | −#define IMX335_EXPOSURE_MIN 1 | |
| 34 | −#define IMX335_EXPOSURE_OFFSET 9 | |
| 35 | −#define IMX335_EXPOSURE_STEP 1 | |
| 36 | −#define IMX335_EXPOSURE_DEFAULT 0x0648 | |
| 37 | − | |
| 38 | −/* Analog gain control */ | |
| 39 | −#define IMX335_REG_AGAIN 0x30e8 | |
| 40 | −#define IMX335_AGAIN_MIN 0 | |
| 41 | −#define IMX335_AGAIN_MAX 240 | |
| 42 | −#define IMX335_AGAIN_STEP 1 | |
| 43 | −#define IMX335_AGAIN_DEFAULT 0 | |
| 39 | +#define IMX335_REG_OPB_SIZE_V CCI_REG8(0x304c) | |
| 40 | +#define IMX335_REG_ADBIT CCI_REG8(0x3050) | |
| 41 | +#define IMX335_REG_Y_OUT_SIZE CCI_REG16_LE(0x3056) | |
| 44 | 42 | |
| 45 | −/* Group hold register */ | |
| 46 | −#define IMX335_REG_HOLD 0x3001 | |
| 43 | +#define IMX335_REG_SHUTTER CCI_REG24_LE(0x3058) | |
| 44 | +#define IMX335_EXPOSURE_MIN 1 | |
| 45 | +#define IMX335_EXPOSURE_OFFSET 9 | |
| 46 | +#define IMX335_EXPOSURE_STEP 1 | |
| 47 | +#define IMX335_EXPOSURE_DEFAULT 0x0648 | |
| 48 | + | |
| 49 | +#define IMX335_REG_AREA3_ST_ADR_1 CCI_REG16_LE(0x3074) | |
| 50 | +#define IMX335_REG_AREA3_WIDTH_1 CCI_REG16_LE(0x3076) | |
| 51 | + | |
| 52 | +/* Analog and Digital gain control */ | |
| 53 | +#define IMX335_REG_GAIN CCI_REG8(0x30e8) | |
| 54 | +#define IMX335_AGAIN_MIN 0 | |
| 55 | +#define IMX335_AGAIN_MAX 100 | |
| 56 | +#define IMX335_AGAIN_STEP 1 | |
| 57 | +#define IMX335_AGAIN_DEFAULT 0 | |
| 58 | + | |
| 59 | +#define IMX335_REG_TPG_TESTCLKEN CCI_REG8(0x3148) | |
| 60 | + | |
| 61 | +#define IMX335_REG_INCLKSEL1 CCI_REG16_LE(0x314c) | |
| 62 | +#define IMX335_REG_INCLKSEL2 CCI_REG8(0x315a) | |
| 63 | +#define IMX335_REG_INCLKSEL3 CCI_REG8(0x3168) | |
| 64 | +#define IMX335_REG_INCLKSEL4 CCI_REG8(0x316a) | |
| 65 | + | |
| 66 | +#define IMX335_REG_MDBIT CCI_REG8(0x319d) | |
| 67 | +#define IMX335_REG_SYSMODE CCI_REG8(0x319e) | |
| 68 | + | |
| 69 | +#define IMX335_REG_XVS_XHS_DRV CCI_REG8(0x31a1) | |
| 47 | 70 | |
| 48 | 71 | /* Test pattern generator */ |
| 49 | −#define IMX335_REG_TPG 0x329e | |
| 50 | −#define IMX335_TPG_ALL_000 0 | |
| 51 | −#define IMX335_TPG_ALL_FFF 1 | |
| 52 | −#define IMX335_TPG_ALL_555 2 | |
| 53 | −#define IMX335_TPG_ALL_AAA 3 | |
| 54 | −#define IMX335_TPG_TOG_555_AAA 4 | |
| 55 | −#define IMX335_TPG_TOG_AAA_555 5 | |
| 56 | −#define IMX335_TPG_TOG_000_555 6 | |
| 57 | −#define IMX335_TPG_TOG_555_000 7 | |
| 58 | −#define IMX335_TPG_TOG_000_FFF 8 | |
| 59 | −#define IMX335_TPG_TOG_FFF_000 9 | |
| 60 | −#define IMX335_TPG_H_COLOR_BARS 10 | |
| 61 | −#define IMX335_TPG_V_COLOR_BARS 11 | |
| 72 | +#define IMX335_REG_TPG_DIG_CLP_MODE CCI_REG8(0x3280) | |
| 73 | +#define IMX335_REG_TPG_EN_DUOUT CCI_REG8(0x329c) | |
| 74 | +#define IMX335_REG_TPG CCI_REG8(0x329e) | |
| 75 | +#define IMX335_TPG_ALL_000 0 | |
| 76 | +#define IMX335_TPG_ALL_FFF 1 | |
| 77 | +#define IMX335_TPG_ALL_555 2 | |
| 78 | +#define IMX335_TPG_ALL_AAA 3 | |
| 79 | +#define IMX335_TPG_TOG_555_AAA 4 | |
| 80 | +#define IMX335_TPG_TOG_AAA_555 5 | |
| 81 | +#define IMX335_TPG_TOG_000_555 6 | |
| 82 | +#define IMX335_TPG_TOG_555_000 7 | |
| 83 | +#define IMX335_TPG_TOG_000_FFF 8 | |
| 84 | +#define IMX335_TPG_TOG_FFF_000 9 | |
| 85 | +#define IMX335_TPG_H_COLOR_BARS 10 | |
| 86 | +#define IMX335_TPG_V_COLOR_BARS 11 | |
| 87 | +#define IMX335_REG_TPG_COLORWIDTH CCI_REG8(0x32a0) | |
| 88 | + | |
| 89 | +#define IMX335_REG_BLKLEVEL CCI_REG16_LE(0x3302) | |
| 90 | + | |
| 91 | +#define IMX335_REG_WRJ_OPEN CCI_REG8(0x336c) | |
| 92 | + | |
| 93 | +#define IMX335_REG_ADBIT1 CCI_REG16_LE(0x341c) | |
| 94 | + | |
| 95 | +/* Chip ID */ | |
| 96 | +#define IMX335_REG_ID CCI_REG8(0x3912) | |
| 97 | +#define IMX335_ID 0x00 | |
| 98 | + | |
| 99 | +/* Data Lanes */ | |
| 100 | +#define IMX335_REG_LANEMODE CCI_REG8(0x3a01) | |
| 101 | +#define IMX335_2LANE 1 | |
| 102 | +#define IMX335_4LANE 3 | |
| 103 | + | |
| 104 | +#define IMX335_REG_TCLKPOST CCI_REG16_LE(0x3a18) | |
| 105 | +#define IMX335_REG_TCLKPREPARE CCI_REG16_LE(0x3a1a) | |
| 106 | +#define IMX335_REG_TCLK_TRAIL CCI_REG16_LE(0x3a1c) | |
| 107 | +#define IMX335_REG_TCLK_ZERO CCI_REG16_LE(0x3a1e) | |
| 108 | +#define IMX335_REG_THS_PREPARE CCI_REG16_LE(0x3a20) | |
| 109 | +#define IMX335_REG_THS_ZERO CCI_REG16_LE(0x3a22) | |
| 110 | +#define IMX335_REG_THS_TRAIL CCI_REG16_LE(0x3a24) | |
| 111 | +#define IMX335_REG_THS_EXIT CCI_REG16_LE(0x3a26) | |
| 112 | +#define IMX335_REG_TPLX CCI_REG16_LE(0x3a28) | |
| 62 | 113 | |
| 63 | 114 | /* Input clock rate */ |
| 64 | −#define IMX335_INCLK_RATE 24000000 | |
| 115 | +#define IMX335_INCLK_RATE 24000000 | |
| 65 | 116 | |
| 66 | 117 | /* CSI2 HW configuration */ |
| 67 | 118 | #define IMX335_LINK_FREQ_594MHz 594000000LL |
| @@ -69,9 +120,6 @@ · 1 unmodified line | ||
| 69 | 120 | |
| 70 | 121 | #define IMX335_NUM_DATA_LANES 4 |
| 71 | 122 | |
| 72 | −#define IMX335_REG_MIN 0x00 | |
| 73 | −#define IMX335_REG_MAX 0xfffff | |
| 74 | − | |
| 75 | 123 | /* IMX335 native and active pixel array size. */ |
| 76 | 124 | #define IMX335_NATIVE_WIDTH 2616U |
| 77 | 125 | #define IMX335_NATIVE_HEIGHT 1964U |
| @@ -80,16 +128,6 @@ · 2 unmodified lines | ||
| 80 | 128 | #define IMX335_PIXEL_ARRAY_WIDTH 2592U |
| 81 | 129 | #define IMX335_PIXEL_ARRAY_HEIGHT 1944U |
| 82 | 130 | |
| 83 | −/** | |
| 84 | − * struct imx335_reg - imx335 sensor register | |
| 85 | − * @address: Register address | |
| 86 | − * @val: Register value | |
| 87 | − */ | |
| 88 | −struct imx335_reg { | |
| 89 | − u16 address; | |
| 90 | − u8 val; | |
| 91 | −}; | |
| 92 | − | |
| 93 | 131 | /** |
| 94 | 132 | * struct imx335_reg_list - imx335 sensor register list |
| 95 | 133 | * @num_of_regs: Number of registers in the list |
| @@ -97,7 +135,7 @@ struct imx335_reg { · 1 unmodified line | ||
| 97 | 135 | */ |
| 98 | 136 | struct imx335_reg_list { |
| 99 | 137 | u32 num_of_regs; |
| 100 | − const struct imx335_reg *regs; | |
| 138 | + const struct cci_reg_sequence *regs; | |
| 101 | 139 | }; |
| 102 | 140 | |
| 103 | 141 | static const char * const imx335_supply_name[] = { |
| @@ -138,6 +176,7 @@ struct imx335_mode { · 34 unmodified lines | ||
| 138 | 176 | * @pad: Media pad. Only one pad supported |
| 139 | 177 | * @reset_gpio: Sensor reset gpio |
| 140 | 178 | * @supplies: Regulator supplies to handle power control |
| 179 | + * @cci: CCI register map | |
| 141 | 180 | * @inclk: Sensor input clock |
| 142 | 181 | * @ctrl_handler: V4L2 control handler |
| 143 | 182 | * @link_freq_ctrl: Pointer to link frequency control |
| @@ -147,6 +186,7 @@ struct imx335_mode { · 3 unmodified lines | ||
| 147 | 186 | * @exp_ctrl: Pointer to exposure control |
| 148 | 187 | * @again_ctrl: Pointer to analog gain control |
| 149 | 188 | * @vblank: Vertical blanking in lines |
| 189 | + * @lane_mode: Mode for number of connected data lanes | |
| 150 | 190 | * @cur_mode: Pointer to current selected sensor mode |
| 151 | 191 | * @mutex: Mutex for serializing sensor controls |
| 152 | 192 | * @link_freq_bitmap: Menu bitmap for link_freq_ctrl |
| @@ -159,6 +199,7 @@ struct imx335 { · 6 unmodified lines | ||
| 159 | 199 | struct media_pad pad; |
| 160 | 200 | struct gpio_desc *reset_gpio; |
| 161 | 201 | struct regulator_bulk_data supplies[ARRAY_SIZE(imx335_supply_name)]; |
| 202 | + struct regmap *cci; | |
| 162 | 203 | |
| 163 | 204 | struct clk *inclk; |
| 164 | 205 | struct v4l2_ctrl_handler ctrl_handler; |
| @@ -171,6 +212,7 @@ struct imx335 { · 6 unmodified lines | ||
| 171 | 212 | struct v4l2_ctrl *again_ctrl; |
| 172 | 213 | }; |
| 173 | 214 | u32 vblank; |
| 215 | + u32 lane_mode; | |
| 174 | 216 | const struct imx335_mode *cur_mode; |
| 175 | 217 | struct mutex mutex; |
| 176 | 218 | unsigned long link_freq_bitmap; |
| @@ -210,140 +252,135 @@ static const int imx335_tpg_val[] = { · 33 unmodified lines | ||
| 210 | 252 | }; |
| 211 | 253 | |
| 212 | 254 | /* Sensor mode registers */ |
| 213 | −static const struct imx335_reg mode_2592x1940_regs[] = { | |
| 214 | − {0x3000, 0x01}, | |
| 215 | − {0x3002, 0x00}, | |
| 216 | − {0x3018, 0x04}, | |
| 217 | − {0x302c, 0x3c}, | |
| 218 | − {0x302e, 0x20}, | |
| 219 | − {0x3056, 0x94}, | |
| 220 | − {0x3074, 0xc8}, | |
| 221 | − {0x3076, 0x28}, | |
| 222 | − {0x304c, 0x00}, | |
| 223 | − {0x31a1, 0x00}, | |
| 224 | − {0x3288, 0x21}, | |
| 225 | − {0x328a, 0x02}, | |
| 226 | − {0x3414, 0x05}, | |
| 227 | − {0x3416, 0x18}, | |
| 228 | − {0x3648, 0x01}, | |
| 229 | − {0x364a, 0x04}, | |
| 230 | − {0x364c, 0x04}, | |
| 231 | − {0x3678, 0x01}, | |
| 232 | − {0x367c, 0x31}, | |
| 233 | − {0x367e, 0x31}, | |
| 234 | − {0x3706, 0x10}, | |
| 235 | − {0x3708, 0x03}, | |
| 236 | − {0x3714, 0x02}, | |
| 237 | − {0x3715, 0x02}, | |
| 238 | − {0x3716, 0x01}, | |
| 239 | − {0x3717, 0x03}, | |
| 240 | − {0x371c, 0x3d}, | |
| 241 | − {0x371d, 0x3f}, | |
| 242 | − {0x372c, 0x00}, | |
| 243 | − {0x372d, 0x00}, | |
| 244 | − {0x372e, 0x46}, | |
| 245 | − {0x372f, 0x00}, | |
| 246 | − {0x3730, 0x89}, | |
| 247 | − {0x3731, 0x00}, | |
| 248 | − {0x3732, 0x08}, | |
| 249 | − {0x3733, 0x01}, | |
| 250 | − {0x3734, 0xfe}, | |
| 251 | − {0x3735, 0x05}, | |
| 252 | − {0x3740, 0x02}, | |
| 253 | − {0x375d, 0x00}, | |
| 254 | − {0x375e, 0x00}, | |
| 255 | − {0x375f, 0x11}, | |
| 256 | − {0x3760, 0x01}, | |
| 257 | − {0x3768, 0x1b}, | |
| 258 | − {0x3769, 0x1b}, | |
| 259 | − {0x376a, 0x1b}, | |
| 260 | − {0x376b, 0x1b}, | |
| 261 | − {0x376c, 0x1a}, | |
| 262 | − {0x376d, 0x17}, | |
| 263 | − {0x376e, 0x0f}, | |
| 264 | − {0x3776, 0x00}, | |
| 265 | − {0x3777, 0x00}, | |
| 266 | − {0x3778, 0x46}, | |
| 267 | − {0x3779, 0x00}, | |
| 268 | − {0x377a, 0x89}, | |
| 269 | − {0x377b, 0x00}, | |
| 270 | − {0x377c, 0x08}, | |
| 271 | − {0x377d, 0x01}, | |
| 272 | − {0x377e, 0x23}, | |
| 273 | − {0x377f, 0x02}, | |
| 274 | − {0x3780, 0xd9}, | |
| 275 | − {0x3781, 0x03}, | |
| 276 | − {0x3782, 0xf5}, | |
| 277 | − {0x3783, 0x06}, | |
| 278 | − {0x3784, 0xa5}, | |
| 279 | − {0x3788, 0x0f}, | |
| 280 | − {0x378a, 0xd9}, | |
| 281 | − {0x378b, 0x03}, | |
| 282 | − {0x378c, 0xeb}, | |
| 283 | − {0x378d, 0x05}, | |
| 284 | − {0x378e, 0x87}, | |
| 285 | − {0x378f, 0x06}, | |
| 286 | − {0x3790, 0xf5}, | |
| 287 | − {0x3792, 0x43}, | |
| 288 | − {0x3794, 0x7a}, | |
| 289 | − {0x3796, 0xa1}, | |
| 290 | − {0x37b0, 0x36}, | |
| 291 | − {0x3a00, 0x00}, | |
| 255 | +static const struct cci_reg_sequence mode_2592x1940_regs[] = { | |
| 256 | + { IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY }, | |
| 257 | + { IMX335_REG_MASTER_MODE, 0x00 }, | |
| 258 | + { IMX335_REG_WINMODE, 0x04 }, | |
| 259 | + { IMX335_REG_HTRIMMING_START, 48 }, | |
| 260 | + { IMX335_REG_HNUM, 2592 }, | |
| 261 | + { IMX335_REG_Y_OUT_SIZE, 1944 }, | |
| 262 | + { IMX335_REG_AREA3_ST_ADR_1, 176 }, | |
| 263 | + { IMX335_REG_AREA3_WIDTH_1, 3928 }, | |
| 264 | + { IMX335_REG_OPB_SIZE_V, 0 }, | |
| 265 | + { IMX335_REG_XVS_XHS_DRV, 0x00 }, | |
| 266 | + { CCI_REG8(0x3288), 0x21 }, | |
| 267 | + { CCI_REG8(0x328a), 0x02 }, | |
| 268 | + { CCI_REG8(0x3414), 0x05 }, | |
| 269 | + { CCI_REG8(0x3416), 0x18 }, | |
| 270 | + { CCI_REG8(0x3648), 0x01 }, | |
| 271 | + { CCI_REG8(0x364a), 0x04 }, | |
| 272 | + { CCI_REG8(0x364c), 0x04 }, | |
| 273 | + { CCI_REG8(0x3678), 0x01 }, | |
| 274 | + { CCI_REG8(0x367c), 0x31 }, | |
| 275 | + { CCI_REG8(0x367e), 0x31 }, | |
| 276 | + { CCI_REG8(0x3706), 0x10 }, | |
| 277 | + { CCI_REG8(0x3708), 0x03 }, | |
| 278 | + { CCI_REG8(0x3714), 0x02 }, | |
| 279 | + { CCI_REG8(0x3715), 0x02 }, | |
| 280 | + { CCI_REG8(0x3716), 0x01 }, | |
| 281 | + { CCI_REG8(0x3717), 0x03 }, | |
| 282 | + { CCI_REG8(0x371c), 0x3d }, | |
| 283 | + { CCI_REG8(0x371d), 0x3f }, | |
| 284 | + { CCI_REG8(0x372c), 0x00 }, | |
| 285 | + { CCI_REG8(0x372d), 0x00 }, | |
| 286 | + { CCI_REG8(0x372e), 0x46 }, | |
| 287 | + { CCI_REG8(0x372f), 0x00 }, | |
| 288 | + { CCI_REG8(0x3730), 0x89 }, | |
| 289 | + { CCI_REG8(0x3731), 0x00 }, | |
| 290 | + { CCI_REG8(0x3732), 0x08 }, | |
| 291 | + { CCI_REG8(0x3733), 0x01 }, | |
| 292 | + { CCI_REG8(0x3734), 0xfe }, | |
| 293 | + { CCI_REG8(0x3735), 0x05 }, | |
| 294 | + { CCI_REG8(0x3740), 0x02 }, | |
| 295 | + { CCI_REG8(0x375d), 0x00 }, | |
| 296 | + { CCI_REG8(0x375e), 0x00 }, | |
| 297 | + { CCI_REG8(0x375f), 0x11 }, | |
| 298 | + { CCI_REG8(0x3760), 0x01 }, | |
| 299 | + { CCI_REG8(0x3768), 0x1b }, | |
| 300 | + { CCI_REG8(0x3769), 0x1b }, | |
| 301 | + { CCI_REG8(0x376a), 0x1b }, | |
| 302 | + { CCI_REG8(0x376b), 0x1b }, | |
| 303 | + { CCI_REG8(0x376c), 0x1a }, | |
| 304 | + { CCI_REG8(0x376d), 0x17 }, | |
| 305 | + { CCI_REG8(0x376e), 0x0f }, | |
| 306 | + { CCI_REG8(0x3776), 0x00 }, | |
| 307 | + { CCI_REG8(0x3777), 0x00 }, | |
| 308 | + { CCI_REG8(0x3778), 0x46 }, | |
| 309 | + { CCI_REG8(0x3779), 0x00 }, | |
| 310 | + { CCI_REG8(0x377a), 0x89 }, | |
| 311 | + { CCI_REG8(0x377b), 0x00 }, | |
| 312 | + { CCI_REG8(0x377c), 0x08 }, | |
| 313 | + { CCI_REG8(0x377d), 0x01 }, | |
| 314 | + { CCI_REG8(0x377e), 0x23 }, | |
| 315 | + { CCI_REG8(0x377f), 0x02 }, | |
| 316 | + { CCI_REG8(0x3780), 0xd9 }, | |
| 317 | + { CCI_REG8(0x3781), 0x03 }, | |
| 318 | + { CCI_REG8(0x3782), 0xf5 }, | |
| 319 | + { CCI_REG8(0x3783), 0x06 }, | |
| 320 | + { CCI_REG8(0x3784), 0xa5 }, | |
| 321 | + { CCI_REG8(0x3788), 0x0f }, | |
| 322 | + { CCI_REG8(0x378a), 0xd9 }, | |
| 323 | + { CCI_REG8(0x378b), 0x03 }, | |
| 324 | + { CCI_REG8(0x378c), 0xeb }, | |
| 325 | + { CCI_REG8(0x378d), 0x05 }, | |
| 326 | + { CCI_REG8(0x378e), 0x87 }, | |
| 327 | + { CCI_REG8(0x378f), 0x06 }, | |
| 328 | + { CCI_REG8(0x3790), 0xf5 }, | |
| 329 | + { CCI_REG8(0x3792), 0x43 }, | |
| 330 | + { CCI_REG8(0x3794), 0x7a }, | |
| 331 | + { CCI_REG8(0x3796), 0xa1 }, | |
| 332 | + { CCI_REG8(0x37b0), 0x36 }, | |
| 333 | + { CCI_REG8(0x3a00), 0x00 }, | |
| 292 | 334 | }; |
| 293 | 335 | |
| 294 | −static const struct imx335_reg raw10_framefmt_regs[] = { | |
| 295 | − {0x3050, 0x00}, | |
| 296 | − {0x319d, 0x00}, | |
| 297 | − {0x341c, 0xff}, | |
| 298 | − {0x341d, 0x01}, | |
| 336 | +static const struct cci_reg_sequence raw10_framefmt_regs[] = { | |
| 337 | + { IMX335_REG_ADBIT, 0x00 }, | |
| 338 | + { IMX335_REG_MDBIT, 0x00 }, | |
| 339 | + { IMX335_REG_ADBIT1, 0x1ff }, | |
| 299 | 340 | }; |
| 300 | 341 | |
| 301 | −static const struct imx335_reg raw12_framefmt_regs[] = { | |
| 302 | − {0x3050, 0x01}, | |
| 303 | − {0x319d, 0x01}, | |
| 304 | − {0x341c, 0x47}, | |
| 305 | − {0x341d, 0x00}, | |
| 342 | +static const struct cci_reg_sequence raw12_framefmt_regs[] = { | |
| 343 | + { IMX335_REG_ADBIT, 0x01 }, | |
| 344 | + { IMX335_REG_MDBIT, 0x01 }, | |
| 345 | + { IMX335_REG_ADBIT1, 0x47 }, | |
| 306 | 346 | }; |
| 307 | 347 | |
| 308 | −static const struct imx335_reg mipi_data_rate_1188Mbps[] = { | |
| 309 | − {0x300c, 0x3b}, | |
| 310 | − {0x300d, 0x2a}, | |
| 311 | − {0x314c, 0xc6}, | |
| 312 | − {0x314d, 0x00}, | |
| 313 | − {0x315a, 0x02}, | |
| 314 | − {0x3168, 0xa0}, | |
| 315 | − {0x316a, 0x7e}, | |
| 316 | − {0x319e, 0x01}, | |
| 317 | − {0x3a18, 0x8f}, | |
| 318 | − {0x3a1a, 0x4f}, | |
| 319 | − {0x3a1c, 0x47}, | |
| 320 | − {0x3a1e, 0x37}, | |
| 321 | − {0x3a1f, 0x01}, | |
| 322 | − {0x3a20, 0x4f}, | |
| 323 | − {0x3a22, 0x87}, | |
| 324 | − {0x3a24, 0x4f}, | |
| 325 | − {0x3a26, 0x7f}, | |
| 326 | − {0x3a28, 0x3f}, | |
| 348 | +static const struct cci_reg_sequence mipi_data_rate_1188Mbps[] = { | |
| 349 | + { IMX335_REG_BCWAIT_TIME, 0x3b }, | |
| 350 | + { IMX335_REG_CPWAIT_TIME, 0x2a }, | |
| 351 | + { IMX335_REG_INCLKSEL1, 0x00c6 }, | |
| 352 | + { IMX335_REG_INCLKSEL2, 0x02 }, | |
| 353 | + { IMX335_REG_INCLKSEL3, 0xa0 }, | |
| 354 | + { IMX335_REG_INCLKSEL4, 0x7e }, | |
| 355 | + { IMX335_REG_SYSMODE, 0x01 }, | |
| 356 | + { IMX335_REG_TCLKPOST, 0x8f }, | |
| 357 | + { IMX335_REG_TCLKPREPARE, 0x4f }, | |
| 358 | + { IMX335_REG_TCLK_TRAIL, 0x47 }, | |
| 359 | + { IMX335_REG_TCLK_ZERO, 0x0137 }, | |
| 360 | + { IMX335_REG_THS_PREPARE, 0x4f }, | |
| 361 | + { IMX335_REG_THS_ZERO, 0x87 }, | |
| 362 | + { IMX335_REG_THS_TRAIL, 0x4f }, | |
| 363 | + { IMX335_REG_THS_EXIT, 0x7f }, | |
| 364 | + { IMX335_REG_TPLX, 0x3f }, | |
| 327 | 365 | }; |
| 328 | 366 | |
| 329 | −static const struct imx335_reg mipi_data_rate_891Mbps[] = { | |
| 330 | − {0x300c, 0x3b}, | |
| 331 | − {0x300d, 0x2a}, | |
| 332 | − {0x314c, 0x29}, | |
| 333 | − {0x314d, 0x01}, | |
| 334 | − {0x315a, 0x06}, | |
| 335 | − {0x3168, 0xa0}, | |
| 336 | − {0x316a, 0x7e}, | |
| 337 | − {0x319e, 0x02}, | |
| 338 | − {0x3a18, 0x7f}, | |
| 339 | − {0x3a1a, 0x37}, | |
| 340 | − {0x3a1c, 0x37}, | |
| 341 | − {0x3a1e, 0xf7}, | |
| 342 | − {0x3a20, 0x3f}, | |
| 343 | − {0x3a22, 0x6f}, | |
| 344 | − {0x3a24, 0x3f}, | |
| 345 | − {0x3a26, 0x5f}, | |
| 346 | − {0x3a28, 0x2f}, | |
| 367 | +static const struct cci_reg_sequence mipi_data_rate_891Mbps[] = { | |
| 368 | + { IMX335_REG_BCWAIT_TIME, 0x3b }, | |
| 369 | + { IMX335_REG_CPWAIT_TIME, 0x2a }, | |
| 370 | + { IMX335_REG_INCLKSEL1, 0x0129 }, | |
| 371 | + { IMX335_REG_INCLKSEL2, 0x06 }, | |
| 372 | + { IMX335_REG_INCLKSEL3, 0xa0 }, | |
| 373 | + { IMX335_REG_INCLKSEL4, 0x7e }, | |
| 374 | + { IMX335_REG_SYSMODE, 0x02 }, | |
| 375 | + { IMX335_REG_TCLKPOST, 0x7f }, | |
| 376 | + { IMX335_REG_TCLKPREPARE, 0x37 }, | |
| 377 | + { IMX335_REG_TCLK_TRAIL, 0x37 }, | |
| 378 | + { IMX335_REG_TCLK_ZERO, 0xf7 }, | |
| 379 | + { IMX335_REG_THS_PREPARE, 0x3f }, | |
| 380 | + { IMX335_REG_THS_ZERO, 0x6f }, | |
| 381 | + { IMX335_REG_THS_TRAIL, 0x3f }, | |
| 382 | + { IMX335_REG_THS_EXIT, 0x5f }, | |
| 383 | + { IMX335_REG_TPLX, 0x2f }, | |
| 347 | 384 | }; |
| 348 | 385 | |
| 349 | 386 | static const s64 link_freq[] = { |
| @@ -372,10 +409,10 @@ static const u32 imx335_mbus_codes[] = { · 22 unmodified lines | ||
| 372 | 409 | /* Supported sensor mode configurations */ |
| 373 | 410 | static const struct imx335_mode supported_mode = { |
| 374 | 411 | .width = 2592, |
| 375 | − .height = 1940, | |
| 412 | + .height = 1944, | |
| 376 | 413 | .hblank = 342, |
| 377 | − .vblank = 2560, | |
| 378 | − .vblank_min = 2560, | |
| 414 | + .vblank = 2556, | |
| 415 | + .vblank_min = 2556, | |
| 379 | 416 | .vblank_max = 133060, |
| 380 | 417 | .pclk = 396000000, |
| 381 | 418 | .reg_list = { |
| @@ -395,101 +432,6 @@ static inline struct imx335 *to_imx335(struct v4l2_subdev *subdev) · 13 unmodified lines | ||
| 395 | 432 | return container_of(subdev, struct imx335, sd); |
| 396 | 433 | } |
| 397 | 434 | |
| 398 | −/** | |
| 399 | − * imx335_read_reg() - Read registers. | |
| 400 | − * @imx335: pointer to imx335 device | |
| 401 | − * @reg: register address | |
| 402 | − * @len: length of bytes to read. Max supported bytes is 4 | |
| 403 | − * @val: pointer to register value to be filled. | |
| 404 | − * | |
| 405 | − * Big endian register addresses with little endian values. | |
| 406 | − * | |
| 407 | − * Return: 0 if successful, error code otherwise. | |
| 408 | − */ | |
| 409 | −static int imx335_read_reg(struct imx335 *imx335, u16 reg, u32 len, u32 *val) | |
| 410 | −{ | |
| 411 | − struct i2c_client *client = v4l2_get_subdevdata(&imx335->sd); | |
| 412 | − struct i2c_msg msgs[2] = {0}; | |
| 413 | − u8 addr_buf[2] = {0}; | |
| 414 | − u8 data_buf[4] = {0}; | |
| 415 | − int ret; | |
| 416 | − | |
| 417 | − if (WARN_ON(len > 4)) | |
| 418 | − return -EINVAL; | |
| 419 | − | |
| 420 | − put_unaligned_be16(reg, addr_buf); | |
| 421 | − | |
| 422 | − /* Write register address */ | |
| 423 | − msgs[0].addr = client->addr; | |
| 424 | − msgs[0].flags = 0; | |
| 425 | − msgs[0].len = ARRAY_SIZE(addr_buf); | |
| 426 | − msgs[0].buf = addr_buf; | |
| 427 | − | |
| 428 | − /* Read data from register */ | |
| 429 | − msgs[1].addr = client->addr; | |
| 430 | − msgs[1].flags = I2C_M_RD; | |
| 431 | − msgs[1].len = len; | |
| 432 | − msgs[1].buf = data_buf; | |
| 433 | − | |
| 434 | − ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); | |
| 435 | − if (ret != ARRAY_SIZE(msgs)) | |
| 436 | − return -EIO; | |
| 437 | − | |
| 438 | − *val = get_unaligned_le32(data_buf); | |
| 439 | − | |
| 440 | − return 0; | |
| 441 | −} | |
| 442 | − | |
| 443 | −/** | |
| 444 | − * imx335_write_reg() - Write register | |
| 445 | − * @imx335: pointer to imx335 device | |
| 446 | − * @reg: register address | |
| 447 | − * @len: length of bytes. Max supported bytes is 4 | |
| 448 | − * @val: register value | |
| 449 | − * | |
| 450 | − * Big endian register addresses with little endian values. | |
| 451 | − * | |
| 452 | − * Return: 0 if successful, error code otherwise. | |
| 453 | − */ | |
| 454 | −static int imx335_write_reg(struct imx335 *imx335, u16 reg, u32 len, u32 val) | |
| 455 | −{ | |
| 456 | − struct i2c_client *client = v4l2_get_subdevdata(&imx335->sd); | |
| 457 | − u8 buf[6] = {0}; | |
| 458 | − | |
| 459 | − if (WARN_ON(len > 4)) | |
| 460 | − return -EINVAL; | |
| 461 | − | |
| 462 | − put_unaligned_be16(reg, buf); | |
| 463 | − put_unaligned_le32(val, buf + 2); | |
| 464 | − if (i2c_master_send(client, buf, len + 2) != len + 2) | |
| 465 | − return -EIO; | |
| 466 | − | |
| 467 | − return 0; | |
| 468 | −} | |
| 469 | − | |
| 470 | −/** | |
| 471 | − * imx335_write_regs() - Write a list of registers | |
| 472 | − * @imx335: pointer to imx335 device | |
| 473 | − * @regs: list of registers to be written | |
| 474 | − * @len: length of registers array | |
| 475 | − * | |
| 476 | − * Return: 0 if successful. error code otherwise. | |
| 477 | − */ | |
| 478 | −static int imx335_write_regs(struct imx335 *imx335, | |
| 479 | − const struct imx335_reg *regs, u32 len) | |
| 480 | −{ | |
| 481 | − unsigned int i; | |
| 482 | − int ret; | |
| 483 | − | |
| 484 | − for (i = 0; i < len; i++) { | |
| 485 | − ret = imx335_write_reg(imx335, regs[i].address, 1, regs[i].val); | |
| 486 | − if (ret) | |
| 487 | − return ret; | |
| 488 | − } | |
| 489 | − | |
| 490 | − return 0; | |
| 491 | −} | |
| 492 | − | |
| 493 | 435 | /** |
| 494 | 436 | * imx335_update_controls() - Update control ranges based on streaming mode |
| 495 | 437 | * @imx335: pointer to imx335 device |
| @@ -526,7 +468,8 @@ static int imx335_update_controls(struct imx335 *imx335, · 30 unmodified lines | ||
| 526 | 468 | static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain) |
| 527 | 469 | { |
| 528 | 470 | u32 lpfr, shutter; |
| 529 | − int ret; | |
| 471 | + int ret_hold; | |
| 472 | + int ret = 0; | |
| 530 | 473 | |
| 531 | 474 | lpfr = imx335->vblank + imx335->cur_mode->height; |
| 532 | 475 | shutter = lpfr - exposure; |
| @@ -534,64 +477,55 @@ static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain) · 1 unmodified line | ||
| 534 | 477 | dev_dbg(imx335->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u\n", |
| 535 | 478 | exposure, gain, shutter, lpfr); |
| 536 | 479 | |
| 537 | − ret = imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 1); | |
| 538 | − if (ret) | |
| 539 | − return ret; | |
| 540 | − | |
| 541 | − ret = imx335_write_reg(imx335, IMX335_REG_LPFR, 3, lpfr); | |
| 542 | − if (ret) | |
| 543 | − goto error_release_group_hold; | |
| 544 | − | |
| 545 | − ret = imx335_write_reg(imx335, IMX335_REG_SHUTTER, 3, shutter); | |
| 546 | − if (ret) | |
| 547 | − goto error_release_group_hold; | |
| 548 | − | |
| 549 | − ret = imx335_write_reg(imx335, IMX335_REG_AGAIN, 2, gain); | |
| 550 | − | |
| 551 | −error_release_group_hold: | |
| 552 | − imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 0); | |
| 480 | + cci_write(imx335->cci, IMX335_REG_HOLD, 1, &ret); | |
| 481 | + cci_write(imx335->cci, IMX335_REG_VMAX, lpfr, &ret); | |
| 482 | + cci_write(imx335->cci, IMX335_REG_SHUTTER, shutter, &ret); | |
| 483 | + cci_write(imx335->cci, IMX335_REG_GAIN, gain, &ret); | |
| 484 | + /* | |
| 485 | + * Unconditionally attempt to release the hold, but track the | |
| 486 | + * error if the unhold itself fails. | |
| 487 | + */ | |
| 488 | + ret_hold = cci_write(imx335->cci, IMX335_REG_HOLD, 0, NULL); | |
| 489 | + if (ret_hold) | |
| 490 | + ret = ret_hold; | |
| 553 | 491 | |
| 554 | 492 | return ret; |
| 555 | 493 | } |
| 556 | 494 | |
| 557 | 495 | static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index) |
| 558 | 496 | { |
| 559 | − int ret; | |
| 497 | + int ret = 0; | |
| 560 | 498 | |
| 561 | 499 | if (pattern_index >= ARRAY_SIZE(imx335_tpg_val)) |
| 562 | 500 | return -EINVAL; |
| 563 | 501 | |
| 564 | 502 | if (pattern_index) { |
| 565 | − const struct imx335_reg tpg_enable_regs[] = { | |
| 566 | − { 0x3148, 0x10 }, | |
| 567 | − { 0x3280, 0x00 }, | |
| 568 | − { 0x329c, 0x01 }, | |
| 569 | − { 0x32a0, 0x11 }, | |
| 570 | − { 0x3302, 0x00 }, | |
| 571 | − { 0x3303, 0x00 }, | |
| 572 | − { 0x336c, 0x00 }, | |
| 503 | + const struct cci_reg_sequence tpg_enable_regs[] = { | |
| 504 | + { IMX335_REG_TPG_TESTCLKEN, 0x10 }, | |
| 505 | + { IMX335_REG_TPG_DIG_CLP_MODE, 0x00 }, | |
| 506 | + { IMX335_REG_TPG_EN_DUOUT, 0x01 }, | |
| 507 | + { IMX335_REG_TPG_COLORWIDTH, 0x11 }, | |
| 508 | + { IMX335_REG_BLKLEVEL, 0x00 }, | |
| 509 | + { IMX335_REG_WRJ_OPEN, 0x00 }, | |
| 573 | 510 | }; |
| 574 | 511 | |
| 575 | − ret = imx335_write_reg(imx335, IMX335_REG_TPG, 1, | |
| 576 | − imx335_tpg_val[pattern_index]); | |
| 577 | − if (ret) | |
| 578 | − return ret; | |
| 512 | + cci_write(imx335->cci, IMX335_REG_TPG, | |
| 513 | + imx335_tpg_val[pattern_index], &ret); | |
| 579 | 514 | |
| 580 | − ret = imx335_write_regs(imx335, tpg_enable_regs, | |
| 581 | − ARRAY_SIZE(tpg_enable_regs)); | |
| 515 | + cci_multi_reg_write(imx335->cci, tpg_enable_regs, | |
| 516 | + ARRAY_SIZE(tpg_enable_regs), &ret); | |
| 582 | 517 | } else { |
| 583 | − const struct imx335_reg tpg_disable_regs[] = { | |
| 584 | − { 0x3148, 0x00 }, | |
| 585 | − { 0x3280, 0x01 }, | |
| 586 | − { 0x329c, 0x00 }, | |
| 587 | − { 0x32a0, 0x10 }, | |
| 588 | − { 0x3302, 0x32 }, | |
| 589 | − { 0x3303, 0x00 }, | |
| 590 | − { 0x336c, 0x01 }, | |
| 518 | + const struct cci_reg_sequence tpg_disable_regs[] = { | |
| 519 | + { IMX335_REG_TPG_TESTCLKEN, 0x00 }, | |
| 520 | + { IMX335_REG_TPG_DIG_CLP_MODE, 0x01 }, | |
| 521 | + { IMX335_REG_TPG_EN_DUOUT, 0x00 }, | |
| 522 | + { IMX335_REG_TPG_COLORWIDTH, 0x10 }, | |
| 523 | + { IMX335_REG_BLKLEVEL, 0x32 }, | |
| 524 | + { IMX335_REG_WRJ_OPEN, 0x01 }, | |
| 591 | 525 | }; |
| 592 | 526 | |
| 593 | − ret = imx335_write_regs(imx335, tpg_disable_regs, | |
| 594 | − ARRAY_SIZE(tpg_disable_regs)); | |
| 527 | + cci_multi_reg_write(imx335->cci, tpg_disable_regs, | |
| 528 | + ARRAY_SIZE(tpg_disable_regs), &ret); | |
| 595 | 529 | } |
| 596 | 530 | |
| 597 | 531 | return ret; |
| @@ -890,12 +824,14 @@ static int imx335_set_framefmt(struct imx335 *imx335) · 292 unmodified lines | ||
| 890 | 824 | { |
| 891 | 825 | switch (imx335->cur_mbus_code) { |
| 892 | 826 | case MEDIA_BUS_FMT_SRGGB10_1X10: |
| 893 | − return imx335_write_regs(imx335, raw10_framefmt_regs, | |
| 894 | − ARRAY_SIZE(raw10_framefmt_regs)); | |
| 827 | + return cci_multi_reg_write(imx335->cci, raw10_framefmt_regs, | |
| 828 | + ARRAY_SIZE(raw10_framefmt_regs), | |
| 829 | + NULL); | |
| 895 | 830 | |
| 896 | 831 | case MEDIA_BUS_FMT_SRGGB12_1X12: |
| 897 | − return imx335_write_regs(imx335, raw12_framefmt_regs, | |
| 898 | − ARRAY_SIZE(raw12_framefmt_regs)); | |
| 832 | + return cci_multi_reg_write(imx335->cci, raw12_framefmt_regs, | |
| 833 | + ARRAY_SIZE(raw12_framefmt_regs), | |
| 834 | + NULL); | |
| 899 | 835 | } |
| 900 | 836 | |
| 901 | 837 | return -EINVAL; |
| @@ -914,7 +850,8 @@ static int imx335_start_streaming(struct imx335 *imx335) · 12 unmodified lines | ||
| 914 | 850 | |
| 915 | 851 | /* Setup PLL */ |
| 916 | 852 | reg_list = &link_freq_reglist[__ffs(imx335->link_freq_bitmap)]; |
| 917 | − ret = imx335_write_regs(imx335, reg_list->regs, reg_list->num_of_regs); | |
| 853 | + ret = cci_multi_reg_write(imx335->cci, reg_list->regs, | |
| 854 | + reg_list->num_of_regs, NULL); | |
| 918 | 855 | if (ret) { |
| 919 | 856 | dev_err(imx335->dev, "%s failed to set plls\n", __func__); |
| 920 | 857 | return ret; |
| @@ -922,8 +859,8 @@ static int imx335_start_streaming(struct imx335 *imx335) · 1 unmodified line | ||
| 922 | 859 | |
| 923 | 860 | /* Write sensor mode registers */ |
| 924 | 861 | reg_list = &imx335->cur_mode->reg_list; |
| 925 | − ret = imx335_write_regs(imx335, reg_list->regs, | |
| 926 | − reg_list->num_of_regs); | |
| 862 | + ret = cci_multi_reg_write(imx335->cci, reg_list->regs, | |
| 863 | + reg_list->num_of_regs, NULL); | |
| 927 | 864 | if (ret) { |
| 928 | 865 | dev_err(imx335->dev, "fail to write initial registers\n"); |
| 929 | 866 | return ret; |
| @@ -936,6 +873,12 @@ static int imx335_start_streaming(struct imx335 *imx335) · 6 unmodified lines | ||
| 936 | 873 | return ret; |
| 937 | 874 | } |
| 938 | 875 | |
| 876 | + /* Configure lanes */ | |
| 877 | + ret = cci_write(imx335->cci, IMX335_REG_LANEMODE, | |
| 878 | + imx335->lane_mode, NULL); | |
| 879 | + if (ret) | |
| 880 | + return ret; | |
| 881 | + | |
| 939 | 882 | /* Setup handler will write actual exposure and gain */ |
| 940 | 883 | ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler); |
| 941 | 884 | if (ret) { |
| @@ -944,8 +887,8 @@ static int imx335_start_streaming(struct imx335 *imx335) · 2 unmodified lines | ||
| 944 | 887 | } |
| 945 | 888 | |
| 946 | 889 | /* Start streaming */ |
| 947 | − ret = imx335_write_reg(imx335, IMX335_REG_MODE_SELECT, | |
| 948 | − 1, IMX335_MODE_STREAMING); | |
| 890 | + ret = cci_write(imx335->cci, IMX335_REG_MODE_SELECT, | |
| 891 | + IMX335_MODE_STREAMING, NULL); | |
| 949 | 892 | if (ret) { |
| 950 | 893 | dev_err(imx335->dev, "fail to start streaming\n"); |
| 951 | 894 | return ret; |
| @@ -965,8 +908,8 @@ static int imx335_start_streaming(struct imx335 *imx335) · 13 unmodified lines | ||
| 965 | 908 | */ |
| 966 | 909 | static int imx335_stop_streaming(struct imx335 *imx335) |
| 967 | 910 | { |
| 968 | − return imx335_write_reg(imx335, IMX335_REG_MODE_SELECT, | |
| 969 | − 1, IMX335_MODE_STANDBY); | |
| 911 | + return cci_write(imx335->cci, IMX335_REG_MODE_SELECT, | |
| 912 | + IMX335_MODE_STANDBY, NULL); | |
| 970 | 913 | } |
| 971 | 914 | |
| 972 | 915 | /** |
| @@ -1017,14 +960,14 @@ error_unlock: · 44 unmodified lines | ||
| 1017 | 960 | static int imx335_detect(struct imx335 *imx335) |
| 1018 | 961 | { |
| 1019 | 962 | int ret; |
| 1020 | − u32 val; | |
| 963 | + u64 val; | |
| 1021 | 964 | |
| 1022 | − ret = imx335_read_reg(imx335, IMX335_REG_ID, 2, &val); | |
| 965 | + ret = cci_read(imx335->cci, IMX335_REG_ID, &val, NULL); | |
| 1023 | 966 | if (ret) |
| 1024 | 967 | return ret; |
| 1025 | 968 | |
| 1026 | 969 | if (val != IMX335_ID) { |
| 1027 | − dev_err(imx335->dev, "chip id mismatch: %x!=%x\n", | |
| 970 | + dev_err(imx335->dev, "chip id mismatch: %x!=%llx\n", | |
| 1028 | 971 | IMX335_ID, val); |
| 1029 | 972 | return -ENXIO; |
| 1030 | 973 | } |
| @@ -1096,7 +1039,14 @@ static int imx335_parse_hw_config(struct imx335 *imx335) · 65 unmodified lines | ||
| 1096 | 1039 | if (ret) |
| 1097 | 1040 | return ret; |
| 1098 | 1041 | |
| 1099 | − if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX335_NUM_DATA_LANES) { | |
| 1042 | + switch (bus_cfg.bus.mipi_csi2.num_data_lanes) { | |
| 1043 | + case 2: | |
| 1044 | + imx335->lane_mode = IMX335_2LANE; | |
| 1045 | + break; | |
| 1046 | + case 4: | |
| 1047 | + imx335->lane_mode = IMX335_4LANE; | |
| 1048 | + break; | |
| 1049 | + default: | |
| 1100 | 1050 | dev_err(imx335->dev, |
| 1101 | 1051 | "number of CSI2 data lanes %d is not supported\n", |
| 1102 | 1052 | bus_cfg.bus.mipi_csi2.num_data_lanes); |
| @@ -1208,10 +1158,16 @@ static int imx335_init_controls(struct imx335 *imx335) · 105 unmodified lines | ||
| 1208 | 1158 | { |
| 1209 | 1159 | struct v4l2_ctrl_handler *ctrl_hdlr = &imx335->ctrl_handler; |
| 1210 | 1160 | const struct imx335_mode *mode = imx335->cur_mode; |
| 1161 | + struct v4l2_fwnode_device_properties props; | |
| 1211 | 1162 | u32 lpfr; |
| 1212 | 1163 | int ret; |
| 1213 | 1164 | |
| 1214 | − ret = v4l2_ctrl_handler_init(ctrl_hdlr, 7); | |
| 1165 | + ret = v4l2_fwnode_device_parse(imx335->dev, &props); | |
| 1166 | + if (ret) | |
| 1167 | + return ret; | |
| 1168 | + | |
| 1169 | + /* v4l2_fwnode_device_properties can add two more controls */ | |
| 1170 | + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9); | |
| 1215 | 1171 | if (ret) |
| 1216 | 1172 | return ret; |
| 1217 | 1173 | |
| @@ -1228,6 +1184,14 @@ static int imx335_init_controls(struct imx335 *imx335) · 10 unmodified lines | ||
| 1228 | 1184 | IMX335_EXPOSURE_STEP, |
| 1229 | 1185 | IMX335_EXPOSURE_DEFAULT); |
| 1230 | 1186 | |
| 1187 | + /* | |
| 1188 | + * The sensor has an analog gain and a digital gain, both controlled | |
| 1189 | + * through a single gain value, expressed in 0.3dB increments. Values | |
| 1190 | + * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values | |
| 1191 | + * up to 72.0dB (240) add further digital gain. Limit the range to | |
| 1192 | + * analog gain only, support for digital gain can be added separately | |
| 1193 | + * if needed. | |
| 1194 | + */ | |
| 1231 | 1195 | imx335->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, |
| 1232 | 1196 | &imx335_ctrl_ops, |
| 1233 | 1197 | V4L2_CID_ANALOGUE_GAIN, |
| @@ -1276,6 +1240,8 @@ static int imx335_init_controls(struct imx335 *imx335) · 42 unmodified lines | ||
| 1276 | 1240 | if (imx335->hblank_ctrl) |
| 1277 | 1241 | imx335->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; |
| 1278 | 1242 | |
| 1243 | + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx335_ctrl_ops, &props); | |
| 1244 | + | |
| 1279 | 1245 | if (ctrl_hdlr->error) { |
| 1280 | 1246 | dev_err(imx335->dev, "control init failed: %d\n", |
| 1281 | 1247 | ctrl_hdlr->error); |
| @@ -1304,6 +1270,11 @@ static int imx335_probe(struct i2c_client *client) · 22 unmodified lines | ||
| 1304 | 1270 | return -ENOMEM; |
| 1305 | 1271 | |
| 1306 | 1272 | imx335->dev = &client->dev; |
| 1273 | + imx335->cci = devm_cci_regmap_init_i2c(client, 16); | |
| 1274 | + if (IS_ERR(imx335->cci)) { | |
| 1275 | + dev_err(imx335->dev, "Unable to initialize I2C\n"); | |
| 1276 | + return -ENODEV; | |
| 1277 | + } | |
| 1307 | 1278 | |
| 1308 | 1279 | /* Initialize subdev */ |
| 1309 | 1280 | v4l2_i2c_subdev_init(&imx335->sd, client, &imx335_subdev_ops); |
| @@ -8,6 +8,9 @@ · 7 unmodified lines | ||
| 8 | 8 | * Copyright (C) 2015 Cogent Embedded, Inc. |
| 9 | 9 | */ |
| 10 | 10 | |
| 11 | +#ifndef __MEDIA_I2C_MAX9271_H__ | |
| 12 | +#define __MEDIA_I2C_MAX9271_H__ | |
| 13 | + | |
| 11 | 14 | #include <linux/i2c.h> |
| 12 | 15 | |
| 13 | 16 | #define MAX9271_DEFAULT_ADDR 0x40 |
| @@ -231,3 +234,5 @@ int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr); · 217 unmodified lines | ||
| 231 | 234 | * Return 0 on success or a negative error code on failure |
| 232 | 235 | */ |
| 233 | 236 | int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest); |
| 237 | + | |
| 238 | +#endif /* __MEDIA_I2C_MAX9271_H__ */ | |
| @@ -75,6 +75,8 @@ · 74 unmodified lines | ||
| 75 | 75 | #define OV2680_ACTIVE_START_TOP 8 |
| 76 | 76 | #define OV2680_MIN_CROP_WIDTH 2 |
| 77 | 77 | #define OV2680_MIN_CROP_HEIGHT 2 |
| 78 | +#define OV2680_MIN_VBLANK 4 | |
| 79 | +#define OV2680_MAX_VBLANK 0xffff | |
| 78 | 80 | |
| 79 | 81 | /* Fixed pre-div of 1/2 */ |
| 80 | 82 | #define OV2680_PLL_PREDIV0 2 |
| @@ -84,10 +86,7 @@ · 3 unmodified lines | ||
| 84 | 86 | |
| 85 | 87 | /* 66MHz pixel clock: 66MHz / 1704 * 1294 = 30fps */ |
| 86 | 88 | #define OV2680_PIXELS_PER_LINE 1704 |
| 87 | −#define OV2680_LINES_PER_FRAME 1294 | |
| 88 | − | |
| 89 | −/* If possible send 16 extra rows / lines to the ISP as padding */ | |
| 90 | −#define OV2680_END_MARGIN 16 | |
| 89 | +#define OV2680_LINES_PER_FRAME_30FPS 1294 | |
| 91 | 90 | |
| 92 | 91 | /* Max exposure time is VTS - 8 */ |
| 93 | 92 | #define OV2680_INTEGRATION_TIME_MARGIN 8 |
| @@ -130,6 +129,8 @@ struct ov2680_ctrls { · 36 unmodified lines | ||
| 130 | 129 | struct v4l2_ctrl *test_pattern; |
| 131 | 130 | struct v4l2_ctrl *link_freq; |
| 132 | 131 | struct v4l2_ctrl *pixel_rate; |
| 132 | + struct v4l2_ctrl *vblank; | |
| 133 | + struct v4l2_ctrl *hblank; | |
| 133 | 134 | }; |
| 134 | 135 | |
| 135 | 136 | struct ov2680_mode { |
| @@ -143,8 +144,6 @@ struct ov2680_mode { · 7 unmodified lines | ||
| 143 | 144 | u16 v_end; |
| 144 | 145 | u16 h_output_size; |
| 145 | 146 | u16 v_output_size; |
| 146 | − u16 hts; | |
| 147 | − u16 vts; | |
| 148 | 147 | }; |
| 149 | 148 | |
| 150 | 149 | struct ov2680_dev { |
| @@ -359,15 +358,11 @@ static void ov2680_calc_mode(struct ov2680_dev *sensor) · 208 unmodified lines | ||
| 359 | 358 | sensor->mode.v_start = (sensor->mode.crop.top + |
| 360 | 359 | (sensor->mode.crop.height - height) / 2) & ~1; |
| 361 | 360 | sensor->mode.h_end = |
| 362 | − min(sensor->mode.h_start + width + OV2680_END_MARGIN - 1, | |
| 363 | − OV2680_NATIVE_WIDTH - 1); | |
| 361 | + min(sensor->mode.h_start + width - 1, OV2680_NATIVE_WIDTH - 1); | |
| 364 | 362 | sensor->mode.v_end = |
| 365 | − min(sensor->mode.v_start + height + OV2680_END_MARGIN - 1, | |
| 366 | − OV2680_NATIVE_HEIGHT - 1); | |
| 363 | + min(sensor->mode.v_start + height - 1, OV2680_NATIVE_HEIGHT - 1); | |
| 367 | 364 | sensor->mode.h_output_size = orig_width; |
| 368 | 365 | sensor->mode.v_output_size = orig_height; |
| 369 | − sensor->mode.hts = OV2680_PIXELS_PER_LINE; | |
| 370 | − sensor->mode.vts = OV2680_LINES_PER_FRAME; | |
| 371 | 366 | } |
| 372 | 367 | |
| 373 | 368 | static int ov2680_set_mode(struct ov2680_dev *sensor) |
| @@ -402,9 +397,8 @@ static int ov2680_set_mode(struct ov2680_dev *sensor) · 28 unmodified lines | ||
| 402 | 397 | cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE, |
| 403 | 398 | sensor->mode.v_output_size, &ret); |
| 404 | 399 | cci_write(sensor->regmap, OV2680_REG_TIMING_HTS, |
| 405 | − sensor->mode.hts, &ret); | |
| 406 | − cci_write(sensor->regmap, OV2680_REG_TIMING_VTS, | |
| 407 | − sensor->mode.vts, &ret); | |
| 400 | + OV2680_PIXELS_PER_LINE, &ret); | |
| 401 | + /* VTS gets set by the vblank ctrl */ | |
| 408 | 402 | cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret); |
| 409 | 403 | cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret); |
| 410 | 404 | 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) · 67 unmodified lines | ||
| 478 | 472 | NULL); |
| 479 | 473 | } |
| 480 | 474 | |
| 475 | +static int ov2680_exposure_update_range(struct ov2680_dev *sensor) | |
| 476 | +{ | |
| 477 | + int exp_max = sensor->mode.fmt.height + sensor->ctrls.vblank->val - | |
| 478 | + OV2680_INTEGRATION_TIME_MARGIN; | |
| 479 | + | |
| 480 | + return __v4l2_ctrl_modify_range(sensor->ctrls.exposure, 0, exp_max, | |
| 481 | + 1, exp_max); | |
| 482 | +} | |
| 483 | + | |
| 481 | 484 | static int ov2680_stream_enable(struct ov2680_dev *sensor) |
| 482 | 485 | { |
| 483 | 486 | int ret; |
| @@ -644,7 +647,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, · 160 unmodified lines | ||
| 644 | 647 | struct v4l2_mbus_framefmt *try_fmt; |
| 645 | 648 | const struct v4l2_rect *crop; |
| 646 | 649 | unsigned int width, height; |
| 647 | − int ret = 0; | |
| 650 | + int def, max, ret = 0; | |
| 648 | 651 | |
| 649 | 652 | crop = __ov2680_get_pad_crop(sensor, sd_state, format->pad, |
| 650 | 653 | format->which); |
| @@ -673,6 +676,27 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, · 22 unmodified lines | ||
| 673 | 676 | sensor->mode.fmt = format->format; |
| 674 | 677 | ov2680_calc_mode(sensor); |
| 675 | 678 | |
| 679 | + /* vblank range is height dependent adjust and reset to default */ | |
| 680 | + max = OV2680_MAX_VBLANK - height; | |
| 681 | + def = OV2680_LINES_PER_FRAME_30FPS - height; | |
| 682 | + ret = __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV2680_MIN_VBLANK, | |
| 683 | + max, 1, def); | |
| 684 | + if (ret) | |
| 685 | + goto unlock; | |
| 686 | + | |
| 687 | + ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, def); | |
| 688 | + if (ret) | |
| 689 | + goto unlock; | |
| 690 | + | |
| 691 | + /* exposure range depends on vts which may have changed */ | |
| 692 | + ret = ov2680_exposure_update_range(sensor); | |
| 693 | + if (ret) | |
| 694 | + goto unlock; | |
| 695 | + | |
| 696 | + /* adjust hblank value for new width */ | |
| 697 | + def = OV2680_PIXELS_PER_LINE - width; | |
| 698 | + ret = __v4l2_ctrl_modify_range(sensor->ctrls.hblank, def, def, 1, def); | |
| 699 | + | |
| 676 | 700 | unlock: |
| 677 | 701 | mutex_unlock(&sensor->lock); |
| 678 | 702 | |
| @@ -842,6 +866,13 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl) · 163 unmodified lines | ||
| 842 | 866 | struct ov2680_dev *sensor = to_ov2680_dev(sd); |
| 843 | 867 | int ret; |
| 844 | 868 | |
| 869 | + /* Update exposure range on vblank changes */ | |
| 870 | + if (ctrl->id == V4L2_CID_VBLANK) { | |
| 871 | + ret = ov2680_exposure_update_range(sensor); | |
| 872 | + if (ret) | |
| 873 | + return ret; | |
| 874 | + } | |
| 875 | + | |
| 845 | 876 | /* Only apply changes to the controls if the device is powered up */ |
| 846 | 877 | if (!pm_runtime_get_if_in_use(sensor->sd.dev)) { |
| 847 | 878 | ov2680_set_bayer_order(sensor, &sensor->mode.fmt); |
| @@ -864,6 +895,10 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl) · 16 unmodified lines | ||
| 864 | 895 | case V4L2_CID_TEST_PATTERN: |
| 865 | 896 | ret = ov2680_test_pattern_set(sensor, ctrl->val); |
| 866 | 897 | break; |
| 898 | + case V4L2_CID_VBLANK: | |
| 899 | + ret = cci_write(sensor->regmap, OV2680_REG_TIMING_VTS, | |
| 900 | + sensor->mode.fmt.height + ctrl->val, NULL); | |
| 901 | + break; | |
| 867 | 902 | default: |
| 868 | 903 | ret = -EINVAL; |
| 869 | 904 | break; |
| @@ -922,8 +957,8 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) · 52 unmodified lines | ||
| 922 | 957 | const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops; |
| 923 | 958 | struct ov2680_ctrls *ctrls = &sensor->ctrls; |
| 924 | 959 | struct v4l2_ctrl_handler *hdl = &ctrls->handler; |
| 925 | − int exp_max = OV2680_LINES_PER_FRAME - OV2680_INTEGRATION_TIME_MARGIN; | |
| 926 | − int ret = 0; | |
| 960 | + struct v4l2_fwnode_device_properties props; | |
| 961 | + int def, max, ret = 0; | |
| 927 | 962 | |
| 928 | 963 | v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops); |
| 929 | 964 | sensor->sd.internal_ops = &ov2680_internal_ops; |
| @@ -948,8 +983,9 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) · 18 unmodified lines | ||
| 948 | 983 | ARRAY_SIZE(test_pattern_menu) - 1, |
| 949 | 984 | 0, 0, test_pattern_menu); |
| 950 | 985 | |
| 986 | + max = OV2680_LINES_PER_FRAME_30FPS - OV2680_INTEGRATION_TIME_MARGIN; | |
| 951 | 987 | ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, |
| 952 | − 0, exp_max, 1, exp_max); | |
| 988 | + 0, max, 1, max); | |
| 953 | 989 | |
| 954 | 990 | ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, |
| 955 | 991 | 0, 1023, 1, 250); |
| @@ -960,6 +996,21 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) · 4 unmodified lines | ||
| 960 | 996 | 0, sensor->pixel_rate, |
| 961 | 997 | 1, sensor->pixel_rate); |
| 962 | 998 | |
| 999 | + max = OV2680_MAX_VBLANK - OV2680_DEFAULT_HEIGHT; | |
| 1000 | + def = OV2680_LINES_PER_FRAME_30FPS - OV2680_DEFAULT_HEIGHT; | |
| 1001 | + ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, | |
| 1002 | + OV2680_MIN_VBLANK, max, 1, def); | |
| 1003 | + | |
| 1004 | + def = OV2680_PIXELS_PER_LINE - OV2680_DEFAULT_WIDTH; | |
| 1005 | + ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, | |
| 1006 | + def, def, 1, def); | |
| 1007 | + | |
| 1008 | + ret = v4l2_fwnode_device_parse(sensor->dev, &props); | |
| 1009 | + if (ret) | |
| 1010 | + goto cleanup_entity; | |
| 1011 | + | |
| 1012 | + v4l2_ctrl_new_fwnode_properties(hdl, ops, &props); | |
| 1013 | + | |
| 963 | 1014 | if (hdl->error) { |
| 964 | 1015 | ret = hdl->error; |
| 965 | 1016 | goto cleanup_entity; |
| @@ -968,6 +1019,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) · 2 unmodified lines | ||
| 968 | 1019 | ctrls->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; |
| 969 | 1020 | ctrls->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; |
| 970 | 1021 | ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; |
| 1022 | + ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; | |
| 971 | 1023 | |
| 972 | 1024 | sensor->sd.ctrl_handler = hdl; |
| 973 | 1025 | |
| @@ -1116,25 +1168,24 @@ static int ov2680_parse_dt(struct ov2680_dev *sensor) · 142 unmodified lines | ||
| 1116 | 1168 | sensor->pixel_rate = sensor->link_freq[0] * 2; |
| 1117 | 1169 | do_div(sensor->pixel_rate, 10); |
| 1118 | 1170 | |
| 1119 | − /* Verify bus cfg */ | |
| 1120 | − if (bus_cfg.bus.mipi_csi2.num_data_lanes != 1) { | |
| 1121 | − ret = dev_err_probe(dev, -EINVAL, | |
| 1122 | − "only a 1-lane CSI2 config is supported"); | |
| 1123 | − goto out_free_bus_cfg; | |
| 1171 | + if (!bus_cfg.nr_of_link_frequencies) { | |
| 1172 | + dev_warn(dev, "Consider passing 'link-frequencies' in DT\n"); | |
| 1173 | + goto skip_link_freq_validation; | |
| 1124 | 1174 | } |
| 1125 | 1175 | |
| 1126 | 1176 | for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) |
| 1127 | 1177 | if (bus_cfg.link_frequencies[i] == sensor->link_freq[0]) |
| 1128 | 1178 | break; |
| 1129 | 1179 | |
| 1130 | − if (bus_cfg.nr_of_link_frequencies == 0 || | |
| 1131 | − bus_cfg.nr_of_link_frequencies == i) { | |
| 1180 | + if (bus_cfg.nr_of_link_frequencies == i) { | |
| 1132 | 1181 | ret = dev_err_probe(dev, -EINVAL, |
| 1133 | 1182 | "supported link freq %lld not found\n", |
| 1134 | 1183 | sensor->link_freq[0]); |
| 1135 | 1184 | goto out_free_bus_cfg; |
| 1136 | 1185 | } |
| 1137 | 1186 | |
| 1187 | +skip_link_freq_validation: | |
| 1188 | + ret = 0; | |
| 1138 | 1189 | out_free_bus_cfg: |
| 1139 | 1190 | v4l2_fwnode_endpoint_free(&bus_cfg); |
| 1140 | 1191 | return ret; |
| @@ -768,14 +768,15 @@ static int ov2740_init_controls(struct ov2740 *ov2740) · 767 unmodified lines | ||
| 768 | 768 | cur_mode = ov2740->cur_mode; |
| 769 | 769 | size = ARRAY_SIZE(link_freq_menu_items); |
| 770 | 770 | |
| 771 | − ov2740->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops, | |
| 772 | − V4L2_CID_LINK_FREQ, | |
| 773 | − size - 1, 0, | |
| 774 | − link_freq_menu_items); | |
| 771 | + ov2740->link_freq = | |
| 772 | + v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops, | |
| 773 | + V4L2_CID_LINK_FREQ, size - 1, | |
| 774 | + ov2740->supported_modes->link_freq_index, | |
| 775 | + link_freq_menu_items); | |
| 775 | 776 | if (ov2740->link_freq) |
| 776 | 777 | ov2740->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; |
| 777 | 778 | |
| 778 | − pixel_rate = to_pixel_rate(OV2740_LINK_FREQ_360MHZ_INDEX); | |
| 779 | + pixel_rate = to_pixel_rate(ov2740->supported_modes->link_freq_index); | |
| 779 | 780 | ov2740->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, |
| 780 | 781 | V4L2_CID_PIXEL_RATE, 0, |
| 781 | 782 | pixel_rate, 1, pixel_rate); |
| @@ -1332,9 +1333,16 @@ static int ov2740_probe(struct i2c_client *client) · 550 unmodified lines | ||
| 1332 | 1333 | return dev_err_probe(dev, ret, "failed to check HW configuration\n"); |
| 1333 | 1334 | |
| 1334 | 1335 | ov2740->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); |
| 1335 | − if (IS_ERR(ov2740->reset_gpio)) | |
| 1336 | + if (IS_ERR(ov2740->reset_gpio)) { | |
| 1336 | 1337 | return dev_err_probe(dev, PTR_ERR(ov2740->reset_gpio), |
| 1337 | 1338 | "failed to get reset GPIO\n"); |
| 1339 | + } else if (ov2740->reset_gpio) { | |
| 1340 | + /* | |
| 1341 | + * Ensure reset is asserted for at least 20 ms before | |
| 1342 | + * ov2740_resume() deasserts it. | |
| 1343 | + */ | |
| 1344 | + msleep(20); | |
| 1345 | + } | |
| 1338 | 1346 | |
| 1339 | 1347 | ov2740->clk = devm_clk_get_optional(dev, "clk"); |
| 1340 | 1348 | if (IS_ERR(ov2740->clk)) |
| @@ -3,7 +3,7 @@ · 2 unmodified lines | ||
| 3 | 3 | * ov4689 driver |
| 4 | 4 | * |
| 5 | 5 | * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. |
| 6 | − * Copyright (C) 2022 Mikhail Rudenko | |
| 6 | + * Copyright (C) 2022, 2024 Mikhail Rudenko | |
| 7 | 7 | */ |
| 8 | 8 | |
| 9 | 9 | #include <linux/clk.h> |
| @@ -15,45 +15,86 @@ · 5 unmodified lines | ||
| 15 | 15 | #include <linux/regulator/consumer.h> |
| 16 | 16 | #include <media/media-entity.h> |
| 17 | 17 | #include <media/v4l2-async.h> |
| 18 | +#include <media/v4l2-cci.h> | |
| 18 | 19 | #include <media/v4l2-ctrls.h> |
| 19 | 20 | #include <media/v4l2-subdev.h> |
| 20 | 21 | #include <media/v4l2-fwnode.h> |
| 21 | 22 | |
| 22 | −#define CHIP_ID 0x004688 | |
| 23 | −#define OV4689_REG_CHIP_ID 0x300a | |
| 24 | − | |
| 25 | −#define OV4689_XVCLK_FREQ 24000000 | |
| 26 | − | |
| 27 | −#define OV4689_REG_CTRL_MODE 0x0100 | |
| 23 | +#define OV4689_REG_CTRL_MODE CCI_REG8(0x0100) | |
| 28 | 24 | #define OV4689_MODE_SW_STANDBY 0x0 |
| 29 | 25 | #define OV4689_MODE_STREAMING BIT(0) |
| 30 | 26 | |
| 31 | −#define OV4689_REG_EXPOSURE 0x3500 | |
| 27 | +#define OV4689_REG_CHIP_ID CCI_REG16(0x300a) | |
| 28 | +#define CHIP_ID 0x004688 | |
| 29 | + | |
| 30 | +#define OV4689_REG_EXPOSURE CCI_REG24(0x3500) | |
| 32 | 31 | #define OV4689_EXPOSURE_MIN 4 |
| 33 | 32 | #define OV4689_EXPOSURE_STEP 1 |
| 34 | −#define OV4689_VTS_MAX 0x7fff | |
| 35 | 33 | |
| 36 | −#define OV4689_REG_GAIN_H 0x3508 | |
| 37 | −#define OV4689_REG_GAIN_L 0x3509 | |
| 38 | −#define OV4689_GAIN_H_MASK 0x07 | |
| 39 | −#define OV4689_GAIN_H_SHIFT 8 | |
| 40 | −#define OV4689_GAIN_L_MASK 0xff | |
| 34 | +#define OV4689_REG_GAIN CCI_REG16(0x3508) | |
| 41 | 35 | #define OV4689_GAIN_STEP 1 |
| 42 | 36 | #define OV4689_GAIN_DEFAULT 0x80 |
| 43 | 37 | |
| 44 | −#define OV4689_REG_TEST_PATTERN 0x5040 | |
| 45 | −#define OV4689_TEST_PATTERN_ENABLE 0x80 | |
| 46 | −#define OV4689_TEST_PATTERN_DISABLE 0x0 | |
| 38 | +#define OV4689_REG_DIG_GAIN CCI_REG16(0x352a) | |
| 39 | +#define OV4689_DIG_GAIN_MIN 1 | |
| 40 | +#define OV4689_DIG_GAIN_MAX 0x7fff | |
| 41 | +#define OV4689_DIG_GAIN_STEP 1 | |
| 42 | +#define OV4689_DIG_GAIN_DEFAULT 0x800 | |
| 47 | 43 | |
| 48 | −#define OV4689_REG_VTS 0x380e | |
| 44 | +#define OV4689_REG_H_CROP_START CCI_REG16(0x3800) | |
| 45 | +#define OV4689_REG_V_CROP_START CCI_REG16(0x3802) | |
| 46 | +#define OV4689_REG_H_CROP_END CCI_REG16(0x3804) | |
| 47 | +#define OV4689_REG_V_CROP_END CCI_REG16(0x3806) | |
| 48 | +#define OV4689_REG_H_OUTPUT_SIZE CCI_REG16(0x3808) | |
| 49 | +#define OV4689_REG_V_OUTPUT_SIZE CCI_REG16(0x380a) | |
| 49 | 50 | |
| 50 | −#define REG_NULL 0xFFFF | |
| 51 | +#define OV4689_REG_HTS CCI_REG16(0x380c) | |
| 52 | +#define OV4689_HTS_DIVIDER 4 | |
| 53 | +#define OV4689_HTS_MAX 0x7fff | |
| 51 | 54 | |
| 52 | −#define OV4689_REG_VALUE_08BIT 1 | |
| 53 | −#define OV4689_REG_VALUE_16BIT 2 | |
| 54 | −#define OV4689_REG_VALUE_24BIT 3 | |
| 55 | +#define OV4689_REG_VTS CCI_REG16(0x380e) | |
| 56 | +#define OV4689_VTS_MAX 0x7fff | |
| 57 | + | |
| 58 | +#define OV4689_REG_H_WIN_OFF CCI_REG16(0x3810) | |
| 59 | +#define OV4689_REG_V_WIN_OFF CCI_REG16(0x3812) | |
| 60 | + | |
| 61 | +#define OV4689_REG_TIMING_FORMAT1 CCI_REG8(0x3820) /* Vertical */ | |
| 62 | +#define OV4689_REG_TIMING_FORMAT2 CCI_REG8(0x3821) /* Horizontal */ | |
| 63 | +#define OV4689_TIMING_FLIP_MASK GENMASK(2, 1) | |
| 64 | +#define OV4689_TIMING_FLIP_ARRAY BIT(1) | |
| 65 | +#define OV4689_TIMING_FLIP_DIGITAL BIT(2) | |
| 66 | +#define OV4689_TIMING_FLIP_BOTH (OV4689_TIMING_FLIP_ARRAY |\ | |
| 67 | + OV4689_TIMING_FLIP_DIGITAL) | |
| 68 | + | |
| 69 | +#define OV4689_REG_ANCHOR_LEFT_START CCI_REG16(0x4020) | |
| 70 | +#define OV4689_ANCHOR_LEFT_START_DEF 576 | |
| 71 | +#define OV4689_REG_ANCHOR_LEFT_END CCI_REG16(0x4022) | |
| 72 | +#define OV4689_ANCHOR_LEFT_END_DEF 831 | |
| 73 | +#define OV4689_REG_ANCHOR_RIGHT_START CCI_REG16(0x4024) | |
| 74 | +#define OV4689_ANCHOR_RIGHT_START_DEF 1984 | |
| 75 | +#define OV4689_REG_ANCHOR_RIGHT_END CCI_REG16(0x4026) | |
| 76 | +#define OV4689_ANCHOR_RIGHT_END_DEF 2239 | |
| 77 | + | |
| 78 | +#define OV4689_REG_VFIFO_CTRL_01 CCI_REG8(0x4601) | |
| 79 | + | |
| 80 | +#define OV4689_REG_WB_GAIN_RED CCI_REG16(0x500c) | |
| 81 | +#define OV4689_REG_WB_GAIN_BLUE CCI_REG16(0x5010) | |
| 82 | +#define OV4689_WB_GAIN_MIN 1 | |
| 83 | +#define OV4689_WB_GAIN_MAX 0xfff | |
| 84 | +#define OV4689_WB_GAIN_STEP 1 | |
| 85 | +#define OV4689_WB_GAIN_DEFAULT 0x400 | |
| 86 | + | |
| 87 | +#define OV4689_REG_TEST_PATTERN CCI_REG8(0x5040) | |
| 88 | +#define OV4689_TEST_PATTERN_ENABLE 0x80 | |
| 89 | +#define OV4689_TEST_PATTERN_DISABLE 0x0 | |
| 55 | 90 | |
| 56 | 91 | #define OV4689_LANES 4 |
| 92 | +#define OV4689_XVCLK_FREQ 24000000 | |
| 93 | + | |
| 94 | +#define OV4689_PIXEL_ARRAY_WIDTH 2720 | |
| 95 | +#define OV4689_PIXEL_ARRAY_HEIGHT 1536 | |
| 96 | +#define OV4689_DUMMY_ROWS 8 /* 8 dummy rows on each side */ | |
| 97 | +#define OV4689_DUMMY_COLUMNS 16 /* 16 dummy columns on each side */ | |
| 57 | 98 | |
| 58 | 99 | static const char *const ov4689_supply_names[] = { |
| 59 | 100 | "avdd", /* Analog power */ |
| @@ -61,11 +102,6 @@ static const char *const ov4689_supply_names[] = { · 1 unmodified line | ||
| 61 | 102 | "dvdd", /* Digital core power */ |
| 62 | 103 | }; |
| 63 | 104 | |
| 64 | −struct regval { | |
| 65 | − u16 addr; | |
| 66 | − u8 val; | |
| 67 | −}; | |
| 68 | − | |
| 69 | 105 | enum ov4689_mode_id { |
| 70 | 106 | OV4689_MODE_2688_1520 = 0, |
| 71 | 107 | OV4689_NUM_MODES, |
| @@ -75,20 +111,18 @@ struct ov4689_mode { · 3 unmodified lines | ||
| 75 | 111 | enum ov4689_mode_id id; |
| 76 | 112 | u32 width; |
| 77 | 113 | u32 height; |
| 78 | − u32 max_fps; | |
| 79 | 114 | u32 hts_def; |
| 115 | + u32 hts_min; | |
| 80 | 116 | u32 vts_def; |
| 81 | 117 | u32 exp_def; |
| 82 | 118 | u32 pixel_rate; |
| 83 | − u32 sensor_width; | |
| 84 | − u32 sensor_height; | |
| 85 | − u32 crop_top; | |
| 86 | − u32 crop_left; | |
| 87 | − const struct regval *reg_list; | |
| 119 | + const struct cci_reg_sequence *reg_list; | |
| 120 | + unsigned int num_regs; | |
| 88 | 121 | }; |
| 89 | 122 | |
| 90 | 123 | struct ov4689 { |
| 91 | − struct i2c_client *client; | |
| 124 | + struct device *dev; | |
| 125 | + struct regmap *regmap; | |
| 92 | 126 | struct clk *xvclk; |
| 93 | 127 | struct gpio_desc *reset_gpio; |
| 94 | 128 | struct gpio_desc *pwdn_gpio; |
| @@ -99,7 +133,6 @@ struct ov4689 { · 4 unmodified lines | ||
| 99 | 133 | |
| 100 | 134 | u32 clock_rate; |
| 101 | 135 | |
| 102 | − struct mutex mutex; /* lock to protect ctrls and cur_mode */ | |
| 103 | 136 | struct v4l2_ctrl_handler ctrl_handler; |
| 104 | 137 | struct v4l2_ctrl *exposure; |
| 105 | 138 | |
| @@ -119,95 +152,108 @@ struct ov4689_gain_range { · 13 unmodified lines | ||
| 119 | 152 | |
| 120 | 153 | /* |
| 121 | 154 | * Xclk 24Mhz |
| 122 | − * max_framerate 30fps | |
| 155 | + * max_framerate 90fps | |
| 123 | 156 | * mipi_datarate per lane 1008Mbps |
| 124 | 157 | */ |
| 125 | −static const struct regval ov4689_2688x1520_regs[] = { | |
| 126 | − {0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00}, | |
| 127 | − {0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03}, | |
| 128 | − {0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04}, | |
| 129 | − {0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00}, | |
| 130 | − {0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72}, | |
| 131 | − {0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01}, | |
| 132 | − {0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1}, | |
| 133 | − {0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00}, | |
| 134 | − {0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04}, | |
| 135 | − {0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00}, | |
| 136 | − {0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80}, | |
| 137 | − {0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00}, | |
| 138 | − {0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80}, | |
| 139 | − {0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00}, | |
| 140 | − {0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80}, | |
| 141 | − {0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00}, | |
| 142 | − {0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80}, | |
| 143 | − {0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00}, | |
| 144 | − {0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80}, | |
| 145 | − {0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08}, | |
| 146 | − {0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00}, | |
| 147 | − {0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00}, | |
| 148 | − {0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12}, | |
| 149 | − {0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5}, | |
| 150 | − {0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7}, | |
| 151 | − {0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60}, | |
| 152 | − {0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02}, | |
| 153 | − {0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10}, | |
| 154 | − {0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86}, | |
| 155 | − {0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22}, | |
| 156 | − {0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00}, | |
| 157 | − {0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01}, | |
| 158 | − {0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c}, | |
| 159 | − {0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b}, | |
| 160 | − {0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc}, | |
| 161 | − {0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33}, | |
| 162 | − {0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0}, | |
| 163 | − {0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23}, | |
| 164 | − {0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd}, | |
| 165 | − {0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00}, | |
| 166 | − {0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00}, | |
| 167 | − {0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04}, | |
| 168 | − {0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08}, | |
| 169 | − {0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00}, | |
| 170 | − {0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51}, | |
| 171 | − {0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18}, | |
| 172 | − {0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00}, | |
| 173 | − {0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04}, | |
| 174 | − {0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05}, | |
| 175 | − {0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80}, | |
| 176 | − {0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a}, | |
| 177 | − {0x380d, 0x0e}, {0x380e, 0x06}, {0x380f, 0x12}, | |
| 178 | − {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00}, | |
| 179 | − {0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01}, | |
| 180 | − {0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06}, | |
| 181 | − {0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01}, | |
| 182 | − {0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01}, | |
| 183 | − {0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08}, | |
| 184 | − {0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71}, | |
| 185 | − {0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1}, | |
| 186 | − {0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14}, | |
| 187 | − {0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00}, | |
| 188 | − {0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00}, | |
| 189 | − {0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10}, | |
| 190 | − {0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09}, | |
| 191 | − {0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f}, | |
| 192 | − {0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06}, | |
| 193 | − {0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02}, | |
| 194 | − {0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff}, | |
| 195 | − {0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00}, | |
| 196 | − {0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c}, | |
| 197 | − {0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01}, | |
| 198 | − {0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08}, | |
| 199 | − {0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10}, | |
| 200 | − {0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04}, | |
| 201 | − {0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93}, | |
| 202 | − {0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3}, | |
| 203 | − {0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00}, | |
| 204 | − {0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00}, | |
| 205 | − {0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10}, | |
| 206 | − {0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00}, | |
| 207 | − {0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00}, | |
| 208 | − {0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00}, | |
| 209 | − {0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00}, | |
| 210 | − {REG_NULL, 0x00}, | |
| 158 | +static const struct cci_reg_sequence ov4689_2688x1520_regs[] = { | |
| 159 | + /* System control*/ | |
| 160 | + { CCI_REG8(0x0103), 0x01 }, /* SC_CTRL0103 software_reset = 1 */ | |
| 161 | + { CCI_REG8(0x3000), 0x20 }, /* SC_CMMN_PAD_OEN0 FSIN_output_enable = 1 */ | |
| 162 | + { CCI_REG8(0x3021), 0x03 }, /* | |
| 163 | + * SC_CMMN_MISC_CTRL fst_stby_ctr = 0, | |
| 164 | + * sleep_no_latch_enable = 0 | |
| 165 | + */ | |
| 166 | + | |
| 167 | + /* AEC PK */ | |
| 168 | + { CCI_REG8(0x3503), 0x04 }, /* AEC_MANUAL gain_input_as_sensor_gain_format = 1 */ | |
| 169 | + | |
| 170 | + /* ADC and analog control*/ | |
| 171 | + { CCI_REG8(0x3603), 0x40 }, | |
| 172 | + { CCI_REG8(0x3604), 0x02 }, | |
| 173 | + { CCI_REG8(0x3609), 0x12 }, | |
| 174 | + { CCI_REG8(0x360c), 0x08 }, | |
| 175 | + { CCI_REG8(0x360f), 0xe5 }, | |
| 176 | + { CCI_REG8(0x3608), 0x8f }, | |
| 177 | + { CCI_REG8(0x3611), 0x00 }, | |
| 178 | + { CCI_REG8(0x3613), 0xf7 }, | |
| 179 | + { CCI_REG8(0x3616), 0x58 }, | |
| 180 | + { CCI_REG8(0x3619), 0x99 }, | |
| 181 | + { CCI_REG8(0x361b), 0x60 }, | |
| 182 | + { CCI_REG8(0x361e), 0x79 }, | |
| 183 | + { CCI_REG8(0x3634), 0x10 }, | |
| 184 | + { CCI_REG8(0x3635), 0x10 }, | |
| 185 | + { CCI_REG8(0x3636), 0x15 }, | |
| 186 | + { CCI_REG8(0x3646), 0x86 }, | |
| 187 | + { CCI_REG8(0x364a), 0x0b }, | |
| 188 | + | |
| 189 | + /* Sensor control */ | |
| 190 | + { CCI_REG8(0x3700), 0x17 }, | |
| 191 | + { CCI_REG8(0x3701), 0x22 }, | |
| 192 | + { CCI_REG8(0x3703), 0x10 }, | |
| 193 | + { CCI_REG8(0x370a), 0x37 }, | |
| 194 | + { CCI_REG8(0x3706), 0x63 }, | |
| 195 | + { CCI_REG8(0x3709), 0x3c }, | |
| 196 | + { CCI_REG8(0x370c), 0x30 }, | |
| 197 | + { CCI_REG8(0x3710), 0x24 }, | |
| 198 | + { CCI_REG8(0x3720), 0x28 }, | |
| 199 | + { CCI_REG8(0x3729), 0x7b }, | |
| 200 | + { CCI_REG8(0x372b), 0xbd }, | |
| 201 | + { CCI_REG8(0x372c), 0xbc }, | |
| 202 | + { CCI_REG8(0x372e), 0x52 }, | |
| 203 | + { CCI_REG8(0x373c), 0x0e }, | |
| 204 | + { CCI_REG8(0x373e), 0x33 }, | |
| 205 | + { CCI_REG8(0x3743), 0x10 }, | |
| 206 | + { CCI_REG8(0x3744), 0x88 }, | |
| 207 | + { CCI_REG8(0x3745), 0xc0 }, | |
| 208 | + { CCI_REG8(0x374c), 0x00 }, | |
| 209 | + { CCI_REG8(0x374e), 0x23 }, | |
| 210 | + { CCI_REG8(0x3751), 0x7b }, | |
| 211 | + { CCI_REG8(0x3753), 0xbd }, | |
| 212 | + { CCI_REG8(0x3754), 0xbc }, | |
| 213 | + { CCI_REG8(0x3756), 0x52 }, | |
| 214 | + { CCI_REG8(0x376b), 0x20 }, | |
| 215 | + { CCI_REG8(0x3774), 0x51 }, | |
| 216 | + { CCI_REG8(0x3776), 0xbd }, | |
| 217 | + { CCI_REG8(0x3777), 0xbd }, | |
| 218 | + { CCI_REG8(0x3781), 0x18 }, | |
| 219 | + { CCI_REG8(0x3783), 0x25 }, | |
| 220 | + { CCI_REG8(0x3798), 0x1b }, | |
| 221 | + | |
| 222 | + /* Timing control */ | |
| 223 | + { CCI_REG8(0x3819), 0x01 }, /* VSYNC_END_L vsync_end_point[7:0] = 0x01 */ | |
| 224 | + | |
| 225 | + /* OTP control */ | |
| 226 | + { CCI_REG8(0x3d85), 0x36 }, /* OTP_REG85 OTP_power_up_load_setting_enable = 1, | |
| 227 | + * OTP_power_up_load_data_enable = 1, | |
| 228 | + * OTP_bist_select = 1 (compare with zero) | |
| 229 | + */ | |
| 230 | + { CCI_REG8(0x3d8c), 0x71 }, /* OTP_SETTING_STT_ADDRESS_H */ | |
| 231 | + { CCI_REG8(0x3d8d), 0xcb }, /* OTP_SETTING_STT_ADDRESS_L */ | |
| 232 | + | |
| 233 | + /* BLC registers*/ | |
| 234 | + { CCI_REG8(0x4001), 0x40 }, /* DEBUG_MODE */ | |
| 235 | + { CCI_REG8(0x401b), 0x00 }, /* DEBUG_MODE */ | |
| 236 | + { CCI_REG8(0x401d), 0x00 }, /* DEBUG_MODE */ | |
| 237 | + { CCI_REG8(0x401f), 0x00 }, /* DEBUG_MODE */ | |
| 238 | + | |
| 239 | + /* ADC sync control */ | |
| 240 | + { CCI_REG8(0x4500), 0x6c }, /* ADC_SYNC_CTRL */ | |
| 241 | + { CCI_REG8(0x4503), 0x01 }, /* ADC_SYNC_CTRL */ | |
| 242 | + | |
| 243 | + /* Temperature monitor */ | |
| 244 | + { CCI_REG8(0x4d00), 0x04 }, /* TPM_CTRL_00 tmp_slope[15:8] = 0x04 */ | |
| 245 | + { CCI_REG8(0x4d01), 0x42 }, /* TPM_CTRL_01 tmp_slope[7:0] = 0x42 */ | |
| 246 | + { CCI_REG8(0x4d02), 0xd1 }, /* TPM_CTRL_02 tpm_offset[31:24] = 0xd1 */ | |
| 247 | + { CCI_REG8(0x4d03), 0x93 }, /* TPM_CTRL_03 tpm_offset[23:16] = 0x93 */ | |
| 248 | + { CCI_REG8(0x4d04), 0xf5 }, /* TPM_CTRL_04 tpm_offset[15:8] = 0xf5 */ | |
| 249 | + { CCI_REG8(0x4d05), 0xc1 }, /* TPM_CTRL_05 tpm_offset[7:0] = 0xc1 */ | |
| 250 | + | |
| 251 | + /* pre-ISP control */ | |
| 252 | + { CCI_REG8(0x5050), 0x0c }, /* DEBUG_MODE */ | |
| 253 | + | |
| 254 | + /* OTP-DPC control */ | |
| 255 | + { CCI_REG8(0x5501), 0x10 }, /* OTP_DPC_START_L otp_start_address[7:0] = 0x10 */ | |
| 256 | + { CCI_REG8(0x5503), 0x0f }, /* OTP_DPC_END_L otp_end_address[7:0] = 0x0f */ | |
| 211 | 257 | }; |
| 212 | 258 | |
| 213 | 259 | static const struct ov4689_mode supported_modes[] = { |
| @@ -215,16 +261,13 @@ static const struct ov4689_mode supported_modes[] = { · 1 unmodified line | ||
| 215 | 261 | .id = OV4689_MODE_2688_1520, |
| 216 | 262 | .width = 2688, |
| 217 | 263 | .height = 1520, |
| 218 | − .sensor_width = 2720, | |
| 219 | − .sensor_height = 1536, | |
| 220 | − .crop_top = 8, | |
| 221 | − .crop_left = 16, | |
| 222 | − .max_fps = 30, | |
| 223 | 264 | .exp_def = 1536, |
| 224 | − .hts_def = 4 * 2574, | |
| 265 | + .hts_def = 10296, | |
| 266 | + .hts_min = 3432, | |
| 225 | 267 | .vts_def = 1554, |
| 226 | 268 | .pixel_rate = 480000000, |
| 227 | 269 | .reg_list = ov4689_2688x1520_regs, |
| 270 | + .num_regs = ARRAY_SIZE(ov4689_2688x1520_regs), | |
| 228 | 271 | }, |
| 229 | 272 | }; |
| 230 | 273 | |
| @@ -277,83 +320,6 @@ static const struct ov4689_gain_range ov4689_gain_ranges[] = { · 46 unmodified lines | ||
| 277 | 320 | }, |
| 278 | 321 | }; |
| 279 | 322 | |
| 280 | −/* Write registers up to 4 at a time */ | |
| 281 | −static int ov4689_write_reg(struct i2c_client *client, u16 reg, u32 len, | |
| 282 | − u32 val) | |
| 283 | −{ | |
| 284 | − u32 buf_i, val_i; | |
| 285 | − __be32 val_be; | |
| 286 | − u8 *val_p; | |
| 287 | − u8 buf[6]; | |
| 288 | − | |
| 289 | − if (len > 4) | |
| 290 | − return -EINVAL; | |
| 291 | − | |
| 292 | − buf[0] = reg >> 8; | |
| 293 | − buf[1] = reg & 0xff; | |
| 294 | − | |
| 295 | − val_be = cpu_to_be32(val); | |
| 296 | − val_p = (u8 *)&val_be; | |
| 297 | − buf_i = 2; | |
| 298 | − val_i = 4 - len; | |
| 299 | − | |
| 300 | − while (val_i < 4) | |
| 301 | − buf[buf_i++] = val_p[val_i++]; | |
| 302 | − | |
| 303 | − if (i2c_master_send(client, buf, len + 2) != len + 2) | |
| 304 | − return -EIO; | |
| 305 | − | |
| 306 | − return 0; | |
| 307 | −} | |
| 308 | − | |
| 309 | −static int ov4689_write_array(struct i2c_client *client, | |
| 310 | − const struct regval *regs) | |
| 311 | −{ | |
| 312 | − int ret = 0; | |
| 313 | − u32 i; | |
| 314 | − | |
| 315 | − for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) | |
| 316 | − ret = ov4689_write_reg(client, regs[i].addr, | |
| 317 | − OV4689_REG_VALUE_08BIT, regs[i].val); | |
| 318 | − | |
| 319 | − return ret; | |
| 320 | −} | |
| 321 | − | |
| 322 | −/* Read registers up to 4 at a time */ | |
| 323 | −static int ov4689_read_reg(struct i2c_client *client, u16 reg, unsigned int len, | |
| 324 | − u32 *val) | |
| 325 | −{ | |
| 326 | − __be16 reg_addr_be = cpu_to_be16(reg); | |
| 327 | − struct i2c_msg msgs[2]; | |
| 328 | − __be32 data_be = 0; | |
| 329 | − u8 *data_be_p; | |
| 330 | − int ret; | |
| 331 | − | |
| 332 | − if (len > 4 || !len) | |
| 333 | − return -EINVAL; | |
| 334 | − | |
| 335 | − data_be_p = (u8 *)&data_be; | |
| 336 | − /* Write register address */ | |
| 337 | − msgs[0].addr = client->addr; | |
| 338 | − msgs[0].flags = 0; | |
| 339 | − msgs[0].len = 2; | |
| 340 | − msgs[0].buf = (u8 *)®_addr_be; | |
| 341 | − | |
| 342 | − /* Read data from register */ | |
| 343 | − msgs[1].addr = client->addr; | |
| 344 | − msgs[1].flags = I2C_M_RD; | |
| 345 | − msgs[1].len = len; | |
| 346 | − msgs[1].buf = &data_be_p[4 - len]; | |
| 347 | − | |
| 348 | − ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); | |
| 349 | − if (ret != ARRAY_SIZE(msgs)) | |
| 350 | − return -EIO; | |
| 351 | − | |
| 352 | − *val = be32_to_cpu(data_be); | |
| 353 | − | |
| 354 | − return 0; | |
| 355 | −} | |
| 356 | − | |
| 357 | 323 | static void ov4689_fill_fmt(const struct ov4689_mode *mode, |
| 358 | 324 | struct v4l2_mbus_framefmt *fmt) |
| 359 | 325 | { |
| @@ -376,19 +342,6 @@ static int ov4689_set_fmt(struct v4l2_subdev *sd, · 16 unmodified lines | ||
| 376 | 342 | return 0; |
| 377 | 343 | } |
| 378 | 344 | |
| 379 | −static int ov4689_get_fmt(struct v4l2_subdev *sd, | |
| 380 | − struct v4l2_subdev_state *sd_state, | |
| 381 | − struct v4l2_subdev_format *fmt) | |
| 382 | −{ | |
| 383 | − struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format; | |
| 384 | − struct ov4689 *ov4689 = to_ov4689(sd); | |
| 385 | − | |
| 386 | − /* only one mode supported for now */ | |
| 387 | − ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt); | |
| 388 | − | |
| 389 | − return 0; | |
| 390 | −} | |
| 391 | − | |
| 392 | 345 | static int ov4689_enum_mbus_code(struct v4l2_subdev *sd, |
| 393 | 346 | struct v4l2_subdev_state *sd_state, |
| 394 | 347 | struct v4l2_subdev_mbus_code_enum *code) |
| @@ -427,16 +380,14 @@ static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern) · 32 unmodified lines | ||
| 427 | 380 | else |
| 428 | 381 | val = OV4689_TEST_PATTERN_DISABLE; |
| 429 | 382 | |
| 430 | − return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN, | |
| 431 | − OV4689_REG_VALUE_08BIT, val); | |
| 383 | + return cci_write(ov4689->regmap, OV4689_REG_TEST_PATTERN, | |
| 384 | + val, NULL); | |
| 432 | 385 | } |
| 433 | 386 | |
| 434 | 387 | static int ov4689_get_selection(struct v4l2_subdev *sd, |
| 435 | 388 | struct v4l2_subdev_state *state, |
| 436 | 389 | struct v4l2_subdev_selection *sel) |
| 437 | 390 | { |
| 438 | − const struct ov4689_mode *mode = to_ov4689(sd)->cur_mode; | |
| 439 | − | |
| 440 | 391 | if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) |
| 441 | 392 | return -EINVAL; |
| 442 | 393 | |
| @@ -444,63 +395,114 @@ static int ov4689_get_selection(struct v4l2_subdev *sd, · 1 unmodified line | ||
| 444 | 395 | case V4L2_SEL_TGT_CROP_BOUNDS: |
| 445 | 396 | sel->r.top = 0; |
| 446 | 397 | sel->r.left = 0; |
| 447 | − sel->r.width = mode->sensor_width; | |
| 448 | − sel->r.height = mode->sensor_height; | |
| 398 | + sel->r.width = OV4689_PIXEL_ARRAY_WIDTH; | |
| 399 | + sel->r.height = OV4689_PIXEL_ARRAY_HEIGHT; | |
| 449 | 400 | return 0; |
| 450 | 401 | case V4L2_SEL_TGT_CROP: |
| 451 | 402 | case V4L2_SEL_TGT_CROP_DEFAULT: |
| 452 | − sel->r.top = mode->crop_top; | |
| 453 | − sel->r.left = mode->crop_left; | |
| 454 | − sel->r.width = mode->width; | |
| 455 | − sel->r.height = mode->height; | |
| 403 | + sel->r.top = OV4689_DUMMY_ROWS; | |
| 404 | + sel->r.left = OV4689_DUMMY_COLUMNS; | |
| 405 | + sel->r.width = | |
| 406 | + OV4689_PIXEL_ARRAY_WIDTH - 2 * OV4689_DUMMY_COLUMNS; | |
| 407 | + sel->r.height = | |
| 408 | + OV4689_PIXEL_ARRAY_HEIGHT - 2 * OV4689_DUMMY_ROWS; | |
| 456 | 409 | return 0; |
| 457 | 410 | } |
| 458 | 411 | |
| 459 | 412 | return -EINVAL; |
| 460 | 413 | } |
| 461 | 414 | |
| 415 | +static int ov4689_setup_timings(struct ov4689 *ov4689) | |
| 416 | +{ | |
| 417 | + const struct ov4689_mode *mode = ov4689->cur_mode; | |
| 418 | + struct regmap *rm = ov4689->regmap; | |
| 419 | + int ret = 0; | |
| 420 | + | |
| 421 | + cci_write(rm, OV4689_REG_H_CROP_START, 8, &ret); | |
| 422 | + cci_write(rm, OV4689_REG_V_CROP_START, 8, &ret); | |
| 423 | + cci_write(rm, OV4689_REG_H_CROP_END, 2711, &ret); | |
| 424 | + cci_write(rm, OV4689_REG_V_CROP_END, 1531, &ret); | |
| 425 | + | |
| 426 | + cci_write(rm, OV4689_REG_H_OUTPUT_SIZE, mode->width, &ret); | |
| 427 | + cci_write(rm, OV4689_REG_V_OUTPUT_SIZE, mode->height, &ret); | |
| 428 | + | |
| 429 | + cci_write(rm, OV4689_REG_H_WIN_OFF, 8, &ret); | |
| 430 | + cci_write(rm, OV4689_REG_V_WIN_OFF, 4, &ret); | |
| 431 | + | |
| 432 | + cci_write(rm, OV4689_REG_VFIFO_CTRL_01, 167, &ret); | |
| 433 | + | |
| 434 | + return ret; | |
| 435 | +} | |
| 436 | + | |
| 437 | +static int ov4689_setup_blc_anchors(struct ov4689 *ov4689) | |
| 438 | +{ | |
| 439 | + struct regmap *rm = ov4689->regmap; | |
| 440 | + int ret = 0; | |
| 441 | + | |
| 442 | + cci_write(rm, OV4689_REG_ANCHOR_LEFT_START, 16, &ret); | |
| 443 | + cci_write(rm, OV4689_REG_ANCHOR_LEFT_END, 1999, &ret); | |
| 444 | + cci_write(rm, OV4689_REG_ANCHOR_RIGHT_START, 2400, &ret); | |
| 445 | + cci_write(rm, OV4689_REG_ANCHOR_RIGHT_END, 2415, &ret); | |
| 446 | + | |
| 447 | + return ret; | |
| 448 | +} | |
| 449 | + | |
| 462 | 450 | static int ov4689_s_stream(struct v4l2_subdev *sd, int on) |
| 463 | 451 | { |
| 464 | 452 | struct ov4689 *ov4689 = to_ov4689(sd); |
| 465 | − struct i2c_client *client = ov4689->client; | |
| 453 | + struct v4l2_subdev_state *sd_state; | |
| 454 | + struct device *dev = ov4689->dev; | |
| 466 | 455 | int ret = 0; |
| 467 | 456 | |
| 468 | − mutex_lock(&ov4689->mutex); | |
| 457 | + sd_state = v4l2_subdev_lock_and_get_active_state(&ov4689->subdev); | |
| 469 | 458 | |
| 470 | 459 | if (on) { |
| 471 | − ret = pm_runtime_resume_and_get(&client->dev); | |
| 460 | + ret = pm_runtime_resume_and_get(dev); | |
| 472 | 461 | if (ret < 0) |
| 473 | 462 | goto unlock_and_return; |
| 474 | 463 | |
| 475 | − ret = ov4689_write_array(ov4689->client, | |
| 476 | − ov4689->cur_mode->reg_list); | |
| 464 | + ret = cci_multi_reg_write(ov4689->regmap, | |
| 465 | + ov4689->cur_mode->reg_list, | |
| 466 | + ov4689->cur_mode->num_regs, | |
| 467 | + NULL); | |
| 468 | + if (ret) { | |
| 469 | + pm_runtime_put(dev); | |
| 470 | + goto unlock_and_return; | |
| 471 | + } | |
| 472 | + | |
| 473 | + ret = ov4689_setup_timings(ov4689); | |
| 477 | 474 | if (ret) { |
| 478 | − pm_runtime_put(&client->dev); | |
| 475 | + pm_runtime_put(dev); | |
| 476 | + goto unlock_and_return; | |
| 477 | + } | |
| 478 | + | |
| 479 | + ret = ov4689_setup_blc_anchors(ov4689); | |
| 480 | + if (ret) { | |
| 481 | + pm_runtime_put(dev); | |
| 479 | 482 | goto unlock_and_return; |
| 480 | 483 | } |
| 481 | 484 | |
| 482 | 485 | ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler); |
| 483 | 486 | if (ret) { |
| 484 | − pm_runtime_put(&client->dev); | |
| 487 | + pm_runtime_put(dev); | |
| 485 | 488 | goto unlock_and_return; |
| 486 | 489 | } |
| 487 | 490 | |
| 488 | − ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE, | |
| 489 | − OV4689_REG_VALUE_08BIT, | |
| 490 | − OV4689_MODE_STREAMING); | |
| 491 | + ret = cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE, | |
| 492 | + OV4689_MODE_STREAMING, NULL); | |
| 491 | 493 | if (ret) { |
| 492 | − pm_runtime_put(&client->dev); | |
| 494 | + pm_runtime_put(dev); | |
| 493 | 495 | goto unlock_and_return; |
| 494 | 496 | } |
| 495 | 497 | } else { |
| 496 | − ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE, | |
| 497 | − OV4689_REG_VALUE_08BIT, | |
| 498 | − OV4689_MODE_SW_STANDBY); | |
| 499 | − pm_runtime_put(&client->dev); | |
| 498 | + cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE, | |
| 499 | + OV4689_MODE_SW_STANDBY, NULL); | |
| 500 | + pm_runtime_mark_last_busy(dev); | |
| 501 | + pm_runtime_put_autosuspend(dev); | |
| 500 | 502 | } |
| 501 | 503 | |
| 502 | 504 | unlock_and_return: |
| 503 | − mutex_unlock(&ov4689->mutex); | |
| 505 | + v4l2_subdev_unlock_state(sd_state); | |
| 504 | 506 | |
| 505 | 507 | return ret; |
| 506 | 508 | } |
| @@ -563,18 +565,13 @@ static int __maybe_unused ov4689_power_off(struct device *dev) · 56 unmodified lines | ||
| 563 | 565 | return 0; |
| 564 | 566 | } |
| 565 | 567 | |
| 566 | −static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | |
| 568 | +static int ov4689_init_state(struct v4l2_subdev *sd, | |
| 569 | + struct v4l2_subdev_state *sd_state) | |
| 567 | 570 | { |
| 568 | − struct ov4689 *ov4689 = to_ov4689(sd); | |
| 569 | − struct v4l2_mbus_framefmt *try_fmt; | |
| 570 | − | |
| 571 | − mutex_lock(&ov4689->mutex); | |
| 572 | − | |
| 573 | − try_fmt = v4l2_subdev_state_get_format(fh->state, 0); | |
| 574 | − /* Initialize try_fmt */ | |
| 575 | − ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], try_fmt); | |
| 571 | + struct v4l2_mbus_framefmt *fmt = | |
| 572 | + v4l2_subdev_state_get_format(sd_state, 0); | |
| 576 | 573 | |
| 577 | − mutex_unlock(&ov4689->mutex); | |
| 574 | + ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], fmt); | |
| 578 | 575 | |
| 579 | 576 | return 0; |
| 580 | 577 | } |
| @@ -583,10 +580,6 @@ static const struct dev_pm_ops ov4689_pm_ops = { · 2 unmodified lines | ||
| 583 | 580 | SET_RUNTIME_PM_OPS(ov4689_power_off, ov4689_power_on, NULL) |
| 584 | 581 | }; |
| 585 | 582 | |
| 586 | −static const struct v4l2_subdev_internal_ops ov4689_internal_ops = { | |
| 587 | − .open = ov4689_open, | |
| 588 | −}; | |
| 589 | − | |
| 590 | 583 | static const struct v4l2_subdev_video_ops ov4689_video_ops = { |
| 591 | 584 | .s_stream = ov4689_s_stream, |
| 592 | 585 | }; |
| @@ -594,11 +587,15 @@ static const struct v4l2_subdev_video_ops ov4689_video_ops = { · 1 unmodified line | ||
| 594 | 587 | static const struct v4l2_subdev_pad_ops ov4689_pad_ops = { |
| 595 | 588 | .enum_mbus_code = ov4689_enum_mbus_code, |
| 596 | 589 | .enum_frame_size = ov4689_enum_frame_sizes, |
| 597 | − .get_fmt = ov4689_get_fmt, | |
| 590 | + .get_fmt = v4l2_subdev_get_fmt, | |
| 598 | 591 | .set_fmt = ov4689_set_fmt, |
| 599 | 592 | .get_selection = ov4689_get_selection, |
| 600 | 593 | }; |
| 601 | 594 | |
| 595 | +static const struct v4l2_subdev_internal_ops ov4689_internal_ops = { | |
| 596 | + .init_state = ov4689_init_state, | |
| 597 | +}; | |
| 598 | + | |
| 602 | 599 | static const struct v4l2_subdev_ops ov4689_subdev_ops = { |
| 603 | 600 | .video = &ov4689_video_ops, |
| 604 | 601 | .pad = &ov4689_pad_ops, |
| @@ -610,7 +607,6 @@ static const struct v4l2_subdev_ops ov4689_subdev_ops = { · 5 unmodified lines | ||
| 610 | 607 | */ |
| 611 | 608 | static int ov4689_map_gain(struct ov4689 *ov4689, int logical_gain, int *result) |
| 612 | 609 | { |
| 613 | − const struct device *dev = &ov4689->client->dev; | |
| 614 | 610 | const struct ov4689_gain_range *range; |
| 615 | 611 | unsigned int n; |
| 616 | 612 | |
| @@ -621,7 +617,8 @@ static int ov4689_map_gain(struct ov4689 *ov4689, int logical_gain, int *result) · 4 unmodified lines | ||
| 621 | 617 | } |
| 622 | 618 | |
| 623 | 619 | if (n == ARRAY_SIZE(ov4689_gain_ranges)) { |
| 624 | − dev_warn_ratelimited(dev, "no mapping found for gain %d\n", | |
| 620 | + dev_warn_ratelimited(ov4689->dev, | |
| 621 | + "no mapping found for gain %d\n", | |
| 625 | 622 | logical_gain); |
| 626 | 623 | return -EINVAL; |
| 627 | 624 | } |
| @@ -637,10 +634,11 @@ static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl) · 9 unmodified lines | ||
| 637 | 634 | { |
| 638 | 635 | struct ov4689 *ov4689 = |
| 639 | 636 | container_of(ctrl->handler, struct ov4689, ctrl_handler); |
| 640 | − struct i2c_client *client = ov4689->client; | |
| 641 | − int sensor_gain; | |
| 637 | + struct regmap *regmap = ov4689->regmap; | |
| 638 | + struct device *dev = ov4689->dev; | |
| 639 | + int sensor_gain = 0; | |
| 642 | 640 | s64 max_expo; |
| 643 | − int ret; | |
| 641 | + int ret = 0; | |
| 644 | 642 | |
| 645 | 643 | /* Propagate change of current control to all related controls */ |
| 646 | 644 | switch (ctrl->id) { |
| @@ -654,44 +652,58 @@ static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl) · 7 unmodified lines | ||
| 654 | 652 | break; |
| 655 | 653 | } |
| 656 | 654 | |
| 657 | − if (!pm_runtime_get_if_in_use(&client->dev)) | |
| 655 | + if (!pm_runtime_get_if_in_use(dev)) | |
| 658 | 656 | return 0; |
| 659 | 657 | |
| 660 | 658 | switch (ctrl->id) { |
| 661 | 659 | case V4L2_CID_EXPOSURE: |
| 662 | − /* 4 least significant bits of expsoure are fractional part */ | |
| 663 | − ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE, | |
| 664 | − OV4689_REG_VALUE_24BIT, ctrl->val << 4); | |
| 660 | + /* 4 least significant bits of exposure are fractional part */ | |
| 661 | + cci_write(regmap, OV4689_REG_EXPOSURE, ctrl->val << 4, &ret); | |
| 665 | 662 | break; |
| 666 | 663 | case V4L2_CID_ANALOGUE_GAIN: |
| 667 | 664 | ret = ov4689_map_gain(ov4689, ctrl->val, &sensor_gain); |
| 668 | − | |
| 669 | − ret = ret ?: | |
| 670 | − ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_H, | |
| 671 | − OV4689_REG_VALUE_08BIT, | |
| 672 | − (sensor_gain >> OV4689_GAIN_H_SHIFT) & | |
| 673 | − OV4689_GAIN_H_MASK); | |
| 674 | − ret = ret ?: | |
| 675 | − ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_L, | |
| 676 | − OV4689_REG_VALUE_08BIT, | |
| 677 | − sensor_gain & OV4689_GAIN_L_MASK); | |
| 665 | + cci_write(regmap, OV4689_REG_GAIN, sensor_gain, &ret); | |
| 678 | 666 | break; |
| 679 | 667 | case V4L2_CID_VBLANK: |
| 680 | − ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS, | |
| 681 | − OV4689_REG_VALUE_16BIT, | |
| 682 | − ctrl->val + ov4689->cur_mode->height); | |
| 668 | + cci_write(regmap, OV4689_REG_VTS, | |
| 669 | + ctrl->val + ov4689->cur_mode->height, &ret); | |
| 683 | 670 | break; |
| 684 | 671 | case V4L2_CID_TEST_PATTERN: |
| 685 | 672 | ret = ov4689_enable_test_pattern(ov4689, ctrl->val); |
| 686 | 673 | break; |
| 674 | + case V4L2_CID_HBLANK: | |
| 675 | + cci_write(regmap, OV4689_REG_HTS, | |
| 676 | + (ctrl->val + ov4689->cur_mode->width) / | |
| 677 | + OV4689_HTS_DIVIDER, &ret); | |
| 678 | + break; | |
| 679 | + case V4L2_CID_VFLIP: | |
| 680 | + cci_update_bits(regmap, OV4689_REG_TIMING_FORMAT1, | |
| 681 | + OV4689_TIMING_FLIP_MASK, | |
| 682 | + ctrl->val ? OV4689_TIMING_FLIP_BOTH : 0, &ret); | |
| 683 | + break; | |
| 684 | + case V4L2_CID_HFLIP: | |
| 685 | + cci_update_bits(regmap, OV4689_REG_TIMING_FORMAT2, | |
| 686 | + OV4689_TIMING_FLIP_MASK, | |
| 687 | + ctrl->val ? 0 : OV4689_TIMING_FLIP_BOTH, &ret); | |
| 688 | + break; | |
| 689 | + case V4L2_CID_DIGITAL_GAIN: | |
| 690 | + cci_write(regmap, OV4689_REG_DIG_GAIN, ctrl->val, &ret); | |
| 691 | + break; | |
| 692 | + case V4L2_CID_RED_BALANCE: | |
| 693 | + cci_write(regmap, OV4689_REG_WB_GAIN_RED, ctrl->val, &ret); | |
| 694 | + break; | |
| 695 | + case V4L2_CID_BLUE_BALANCE: | |
| 696 | + cci_write(regmap, OV4689_REG_WB_GAIN_BLUE, ctrl->val, &ret); | |
| 697 | + break; | |
| 687 | 698 | default: |
| 688 | − dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", | |
| 699 | + dev_warn(dev, "%s Unhandled id:0x%x, val:0x%x\n", | |
| 689 | 700 | __func__, ctrl->id, ctrl->val); |
| 690 | 701 | ret = -EINVAL; |
| 691 | 702 | break; |
| 692 | 703 | } |
| 693 | 704 | |
| 694 | − pm_runtime_put(&client->dev); | |
| 705 | + pm_runtime_mark_last_busy(dev); | |
| 706 | + pm_runtime_put_autosuspend(dev); | |
| 695 | 707 | |
| 696 | 708 | return ret; |
| 697 | 709 | } |
| @@ -707,16 +719,15 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689) · 9 unmodified lines | ||
| 707 | 719 | struct v4l2_ctrl_handler *handler; |
| 708 | 720 | const struct ov4689_mode *mode; |
| 709 | 721 | s64 exposure_max, vblank_def; |
| 722 | + s64 hblank_def, hblank_min; | |
| 710 | 723 | struct v4l2_ctrl *ctrl; |
| 711 | − s64 h_blank_def; | |
| 712 | 724 | int ret; |
| 713 | 725 | |
| 714 | 726 | handler = &ov4689->ctrl_handler; |
| 715 | 727 | mode = ov4689->cur_mode; |
| 716 | − ret = v4l2_ctrl_handler_init(handler, 10); | |
| 728 | + ret = v4l2_ctrl_handler_init(handler, 15); | |
| 717 | 729 | if (ret) |
| 718 | 730 | return ret; |
| 719 | − handler->lock = &ov4689->mutex; | |
| 720 | 731 | |
| 721 | 732 | ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0, |
| 722 | 733 | link_freq_menu_items); |
| @@ -726,11 +737,11 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689) · 3 unmodified lines | ||
| 726 | 737 | v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, |
| 727 | 738 | mode->pixel_rate, 1, mode->pixel_rate); |
| 728 | 739 | |
| 729 | − h_blank_def = mode->hts_def - mode->width; | |
| 730 | − ctrl = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, h_blank_def, | |
| 731 | − h_blank_def, 1, h_blank_def); | |
| 732 | − if (ctrl) | |
| 733 | − ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; | |
| 740 | + hblank_def = mode->hts_def - mode->width; | |
| 741 | + hblank_min = mode->hts_min - mode->width; | |
| 742 | + v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_HBLANK, | |
| 743 | + hblank_min, OV4689_HTS_MAX - mode->width, | |
| 744 | + OV4689_HTS_DIVIDER, hblank_def); | |
| 734 | 745 | |
| 735 | 746 | vblank_def = mode->vts_def - mode->height; |
| 736 | 747 | v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK, |
| @@ -754,10 +765,24 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689) · 17 unmodified lines | ||
| 754 | 765 | ARRAY_SIZE(ov4689_test_pattern_menu) - 1, |
| 755 | 766 | 0, 0, ov4689_test_pattern_menu); |
| 756 | 767 | |
| 768 | + v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); | |
| 769 | + v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); | |
| 770 | + | |
| 771 | + v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_DIGITAL_GAIN, | |
| 772 | + OV4689_DIG_GAIN_MIN, OV4689_DIG_GAIN_MAX, | |
| 773 | + OV4689_DIG_GAIN_STEP, OV4689_DIG_GAIN_DEFAULT); | |
| 774 | + | |
| 775 | + v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_RED_BALANCE, | |
| 776 | + OV4689_WB_GAIN_MIN, OV4689_WB_GAIN_MAX, | |
| 777 | + OV4689_WB_GAIN_STEP, OV4689_WB_GAIN_DEFAULT); | |
| 778 | + | |
| 779 | + v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_BLUE_BALANCE, | |
| 780 | + OV4689_WB_GAIN_MIN, OV4689_WB_GAIN_MAX, | |
| 781 | + OV4689_WB_GAIN_STEP, OV4689_WB_GAIN_DEFAULT); | |
| 782 | + | |
| 757 | 783 | if (handler->error) { |
| 758 | 784 | ret = handler->error; |
| 759 | − dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n", | |
| 760 | − ret); | |
| 785 | + dev_err(ov4689->dev, "Failed to init controls(%d)\n", ret); | |
| 761 | 786 | goto err_free_handler; |
| 762 | 787 | } |
| 763 | 788 | |
| @@ -783,19 +808,18 @@ err_free_handler: · 19 unmodified lines | ||
| 783 | 808 | static int ov4689_check_sensor_id(struct ov4689 *ov4689, |
| 784 | 809 | struct i2c_client *client) |
| 785 | 810 | { |
| 786 | − struct device *dev = &ov4689->client->dev; | |
| 787 | − u32 id = 0; | |
| 811 | + struct device *dev = ov4689->dev; | |
| 812 | + u64 id = 0; | |
| 788 | 813 | int ret; |
| 789 | 814 | |
| 790 | − ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID, | |
| 791 | − OV4689_REG_VALUE_16BIT, &id); | |
| 815 | + ret = cci_read(ov4689->regmap, OV4689_REG_CHIP_ID, &id, NULL); | |
| 792 | 816 | if (ret) { |
| 793 | 817 | dev_err(dev, "Cannot read sensor ID\n"); |
| 794 | 818 | return ret; |
| 795 | 819 | } |
| 796 | 820 | |
| 797 | 821 | if (id != CHIP_ID) { |
| 798 | − dev_err(dev, "Unexpected sensor ID %06x, expected %06x\n", | |
| 822 | + dev_err(dev, "Unexpected sensor ID %06llx, expected %06x\n", | |
| 799 | 823 | id, CHIP_ID); |
| 800 | 824 | return -ENODEV; |
| 801 | 825 | } |
| @@ -812,7 +836,7 @@ static int ov4689_configure_regulators(struct ov4689 *ov4689) · 10 unmodified lines | ||
| 812 | 836 | for (i = 0; i < ARRAY_SIZE(ov4689_supply_names); i++) |
| 813 | 837 | ov4689->supplies[i].supply = ov4689_supply_names[i]; |
| 814 | 838 | |
| 815 | − return devm_regulator_bulk_get(&ov4689->client->dev, | |
| 839 | + return devm_regulator_bulk_get(ov4689->dev, | |
| 816 | 840 | ARRAY_SIZE(ov4689_supply_names), |
| 817 | 841 | ov4689->supplies); |
| 818 | 842 | } |
| @@ -881,7 +905,8 @@ static int ov4689_probe(struct i2c_client *client) · 62 unmodified lines | ||
| 881 | 905 | if (!ov4689) |
| 882 | 906 | return -ENOMEM; |
| 883 | 907 | |
| 884 | − ov4689->client = client; | |
| 908 | + ov4689->dev = dev; | |
| 909 | + | |
| 885 | 910 | ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520]; |
| 886 | 911 | |
| 887 | 912 | ov4689->xvclk = devm_clk_get_optional(dev, NULL); |
| @@ -905,6 +930,13 @@ static int ov4689_probe(struct i2c_client *client) · 17 unmodified lines | ||
| 905 | 930 | return -EINVAL; |
| 906 | 931 | } |
| 907 | 932 | |
| 933 | + ov4689->regmap = devm_cci_regmap_init_i2c(client, 16); | |
| 934 | + if (IS_ERR(ov4689->regmap)) { | |
| 935 | + ret = PTR_ERR(ov4689->regmap); | |
| 936 | + dev_err(dev, "failed to initialize CCI: %d\n", ret); | |
| 937 | + return ret; | |
| 938 | + } | |
| 939 | + | |
| 908 | 940 | ov4689->reset_gpio = devm_gpiod_get_optional(dev, "reset", |
| 909 | 941 | GPIOD_OUT_LOW); |
| 910 | 942 | if (IS_ERR(ov4689->reset_gpio)) { |
| @@ -923,13 +955,15 @@ static int ov4689_probe(struct i2c_client *client) · 12 unmodified lines | ||
| 923 | 955 | return dev_err_probe(dev, ret, |
| 924 | 956 | "Failed to get power regulators\n"); |
| 925 | 957 | |
| 926 | − mutex_init(&ov4689->mutex); | |
| 927 | − | |
| 928 | 958 | sd = &ov4689->subdev; |
| 929 | 959 | v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops); |
| 960 | + sd->internal_ops = &ov4689_internal_ops; | |
| 961 | + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | |
| 930 | 962 | ret = ov4689_initialize_controls(ov4689); |
| 931 | − if (ret) | |
| 932 | − goto err_destroy_mutex; | |
| 963 | + if (ret) { | |
| 964 | + dev_err(dev, "Failed to initialize controls\n"); | |
| 965 | + return ret; | |
| 966 | + } | |
| 933 | 967 | |
| 934 | 968 | ret = ov4689_power_on(dev); |
| 935 | 969 | if (ret) |
| @@ -939,35 +973,47 @@ static int ov4689_probe(struct i2c_client *client) · 3 unmodified lines | ||
| 939 | 973 | if (ret) |
| 940 | 974 | goto err_power_off; |
| 941 | 975 | |
| 942 | − sd->internal_ops = &ov4689_internal_ops; | |
| 943 | − sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | |
| 944 | 976 | |
| 945 | − ov4689->pad.flags = MEDIA_PAD_FL_SOURCE; | |
| 946 | 977 | sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; |
| 978 | + ov4689->pad.flags = MEDIA_PAD_FL_SOURCE; | |
| 947 | 979 | ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad); |
| 948 | 980 | if (ret < 0) |
| 949 | 981 | goto err_power_off; |
| 950 | 982 | |
| 951 | − ret = v4l2_async_register_subdev_sensor(sd); | |
| 983 | + sd->state_lock = ov4689->ctrl_handler.lock; | |
| 984 | + ret = v4l2_subdev_init_finalize(sd); | |
| 952 | 985 | if (ret) { |
| 953 | − dev_err(dev, "v4l2 async register subdev failed\n"); | |
| 986 | + dev_err(dev, "Could not register v4l2 device\n"); | |
| 954 | 987 | goto err_clean_entity; |
| 955 | 988 | } |
| 956 | 989 | |
| 957 | 990 | pm_runtime_set_active(dev); |
| 991 | + pm_runtime_get_noresume(dev); | |
| 958 | 992 | pm_runtime_enable(dev); |
| 959 | − pm_runtime_idle(dev); | |
| 993 | + pm_runtime_set_autosuspend_delay(dev, 1000); | |
| 994 | + pm_runtime_use_autosuspend(dev); | |
| 995 | + | |
| 996 | + ret = v4l2_async_register_subdev_sensor(sd); | |
| 997 | + if (ret) { | |
| 998 | + dev_err(dev, "v4l2 async register subdev failed\n"); | |
| 999 | + goto err_clean_subdev_pm; | |
| 1000 | + } | |
| 1001 | + | |
| 1002 | + pm_runtime_mark_last_busy(dev); | |
| 1003 | + pm_runtime_put_autosuspend(dev); | |
| 960 | 1004 | |
| 961 | 1005 | return 0; |
| 962 | 1006 | |
| 1007 | +err_clean_subdev_pm: | |
| 1008 | + pm_runtime_disable(dev); | |
| 1009 | + pm_runtime_put_noidle(dev); | |
| 1010 | + v4l2_subdev_cleanup(sd); | |
| 963 | 1011 | err_clean_entity: |
| 964 | 1012 | media_entity_cleanup(&sd->entity); |
| 965 | 1013 | err_power_off: |
| 966 | 1014 | ov4689_power_off(dev); |
| 967 | 1015 | err_free_handler: |
| 968 | 1016 | v4l2_ctrl_handler_free(&ov4689->ctrl_handler); |
| 969 | −err_destroy_mutex: | |
| 970 | − mutex_destroy(&ov4689->mutex); | |
| 971 | 1017 | |
| 972 | 1018 | return ret; |
| 973 | 1019 | } |
| @@ -979,9 +1025,8 @@ static void ov4689_remove(struct i2c_client *client) · 5 unmodified lines | ||
| 979 | 1025 | |
| 980 | 1026 | v4l2_async_unregister_subdev(sd); |
| 981 | 1027 | media_entity_cleanup(&sd->entity); |
| 982 | − | |
| 1028 | + v4l2_subdev_cleanup(sd); | |
| 983 | 1029 | v4l2_ctrl_handler_free(&ov4689->ctrl_handler); |
| 984 | − mutex_destroy(&ov4689->mutex); | |
| 985 | 1030 | |
| 986 | 1031 | pm_runtime_disable(&client->dev); |
| 987 | 1032 | if (!pm_runtime_status_suspended(&client->dev)) |
| @@ -463,8 +463,8 @@ static int rdacm20_initialize(struct rdacm20_device *dev) · 462 unmodified lines | ||
| 463 | 463 | return ret; |
| 464 | 464 | |
| 465 | 465 | /* |
| 466 | − * Ensure that we have a good link configuration before attempting to | |
| 467 | − * identify the device. | |
| 466 | + * Ensure that we have a good link configuration before attempting to | |
| 467 | + * identify the device. | |
| 468 | 468 | */ |
| 469 | 469 | ret = max9271_configure_i2c(&dev->serializer, |
| 470 | 470 | MAX9271_I2CSLVSH_469NS_234NS | |
| @@ -326,7 +326,7 @@ static int mipid02_configure_from_rx_speed(struct mipid02_dev *bridge, · 325 unmodified lines | ||
| 326 | 326 | } |
| 327 | 327 | |
| 328 | 328 | dev_dbg(&client->dev, "detect link_freq = %lld Hz", link_freq); |
| 329 | − do_div(ui_4, link_freq); | |
| 329 | + ui_4 = div64_u64(ui_4, link_freq); | |
| 330 | 330 | bridge->r.clk_lane_reg1 |= ui_4 << 2; |
| 331 | 331 | |
| 332 | 332 | return 0; |
| @@ -1521,11 +1521,14 @@ static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) · 1520 unmodified lines | ||
| 1521 | 1521 | return 0; |
| 1522 | 1522 | } |
| 1523 | 1523 | |
| 1524 | −static int tc358743_s_dv_timings(struct v4l2_subdev *sd, | |
| 1524 | +static int tc358743_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1525 | 1525 | struct v4l2_dv_timings *timings) |
| 1526 | 1526 | { |
| 1527 | 1527 | struct tc358743_state *state = to_state(sd); |
| 1528 | 1528 | |
| 1529 | + if (pad != 0) | |
| 1530 | + return -EINVAL; | |
| 1531 | + | |
| 1529 | 1532 | if (!timings) |
| 1530 | 1533 | return -EINVAL; |
| 1531 | 1534 | |
| @@ -1553,11 +1556,14 @@ static int tc358743_s_dv_timings(struct v4l2_subdev *sd, · 21 unmodified lines | ||
| 1553 | 1556 | return 0; |
| 1554 | 1557 | } |
| 1555 | 1558 | |
| 1556 | −static int tc358743_g_dv_timings(struct v4l2_subdev *sd, | |
| 1559 | +static int tc358743_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1557 | 1560 | struct v4l2_dv_timings *timings) |
| 1558 | 1561 | { |
| 1559 | 1562 | struct tc358743_state *state = to_state(sd); |
| 1560 | 1563 | |
| 1564 | + if (pad != 0) | |
| 1565 | + return -EINVAL; | |
| 1566 | + | |
| 1561 | 1567 | *timings = state->timings; |
| 1562 | 1568 | |
| 1563 | 1569 | return 0; |
| @@ -1573,11 +1579,14 @@ static int tc358743_enum_dv_timings(struct v4l2_subdev *sd, · 9 unmodified lines | ||
| 1573 | 1579 | &tc358743_timings_cap, NULL, NULL); |
| 1574 | 1580 | } |
| 1575 | 1581 | |
| 1576 | −static int tc358743_query_dv_timings(struct v4l2_subdev *sd, | |
| 1577 | − struct v4l2_dv_timings *timings) | |
| 1582 | +static int tc358743_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1583 | + struct v4l2_dv_timings *timings) | |
| 1578 | 1584 | { |
| 1579 | 1585 | int ret; |
| 1580 | 1586 | |
| 1587 | + if (pad != 0) | |
| 1588 | + return -EINVAL; | |
| 1589 | + | |
| 1581 | 1590 | ret = tc358743_get_detected_timings(sd, timings); |
| 1582 | 1591 | if (ret) |
| 1583 | 1592 | return ret; |
| @@ -1822,9 +1831,6 @@ static const struct v4l2_subdev_core_ops tc358743_core_ops = { · 238 unmodified lines | ||
| 1822 | 1831 | |
| 1823 | 1832 | static const struct v4l2_subdev_video_ops tc358743_video_ops = { |
| 1824 | 1833 | .g_input_status = tc358743_g_input_status, |
| 1825 | − .s_dv_timings = tc358743_s_dv_timings, | |
| 1826 | − .g_dv_timings = tc358743_g_dv_timings, | |
| 1827 | − .query_dv_timings = tc358743_query_dv_timings, | |
| 1828 | 1834 | .s_stream = tc358743_s_stream, |
| 1829 | 1835 | }; |
| 1830 | 1836 | |
| @@ -1834,6 +1840,9 @@ static const struct v4l2_subdev_pad_ops tc358743_pad_ops = { · 3 unmodified lines | ||
| 1834 | 1840 | .get_fmt = tc358743_get_fmt, |
| 1835 | 1841 | .get_edid = tc358743_g_edid, |
| 1836 | 1842 | .set_edid = tc358743_s_edid, |
| 1843 | + .s_dv_timings = tc358743_s_dv_timings, | |
| 1844 | + .g_dv_timings = tc358743_g_dv_timings, | |
| 1845 | + .query_dv_timings = tc358743_query_dv_timings, | |
| 1837 | 1846 | .enum_dv_timings = tc358743_enum_dv_timings, |
| 1838 | 1847 | .dv_timings_cap = tc358743_dv_timings_cap, |
| 1839 | 1848 | .get_mbus_config = tc358743_get_mbus_config, |
| @@ -2110,7 +2119,7 @@ static int tc358743_probe(struct i2c_client *client) · 270 unmodified lines | ||
| 2110 | 2119 | |
| 2111 | 2120 | tc358743_initial_setup(sd); |
| 2112 | 2121 | |
| 2113 | − tc358743_s_dv_timings(sd, &default_timing); | |
| 2122 | + tc358743_s_dv_timings(sd, 0, &default_timing); | |
| 2114 | 2123 | |
| 2115 | 2124 | tc358743_set_csi_color_space(sd); |
| 2116 | 2125 | |
| @@ -844,8 +844,7 @@ static unsigned long tc358746_find_pll_settings(struct tc358746 *tc358746, · 843 unmodified lines | ||
| 844 | 844 | continue; |
| 845 | 845 | |
| 846 | 846 | tmp = fout * postdiv; |
| 847 | − do_div(tmp, fin); | |
| 848 | − mul = tmp; | |
| 847 | + mul = div64_ul(tmp, fin); | |
| 849 | 848 | if (mul > 511) |
| 850 | 849 | continue; |
| 851 | 850 | |
| @@ -1669,8 +1669,8 @@ tda1997x_g_input_status(struct v4l2_subdev *sd, u32 *status) · 1668 unmodified lines | ||
| 1669 | 1669 | return 0; |
| 1670 | 1670 | }; |
| 1671 | 1671 | |
| 1672 | −static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, | |
| 1673 | − struct v4l2_dv_timings *timings) | |
| 1672 | +static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1673 | + struct v4l2_dv_timings *timings) | |
| 1674 | 1674 | { |
| 1675 | 1675 | struct tda1997x_state *state = to_state(sd); |
| 1676 | 1676 | |
| @@ -1694,7 +1694,7 @@ static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, · 17 unmodified lines | ||
| 1694 | 1694 | return 0; |
| 1695 | 1695 | } |
| 1696 | 1696 | |
| 1697 | −static int tda1997x_g_dv_timings(struct v4l2_subdev *sd, | |
| 1697 | +static int tda1997x_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1698 | 1698 | struct v4l2_dv_timings *timings) |
| 1699 | 1699 | { |
| 1700 | 1700 | struct tda1997x_state *state = to_state(sd); |
| @@ -1707,7 +1707,7 @@ static int tda1997x_g_dv_timings(struct v4l2_subdev *sd, · 6 unmodified lines | ||
| 1707 | 1707 | return 0; |
| 1708 | 1708 | } |
| 1709 | 1709 | |
| 1710 | −static int tda1997x_query_dv_timings(struct v4l2_subdev *sd, | |
| 1710 | +static int tda1997x_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 1711 | 1711 | struct v4l2_dv_timings *timings) |
| 1712 | 1712 | { |
| 1713 | 1713 | struct tda1997x_state *state = to_state(sd); |
| @@ -1724,9 +1724,6 @@ static int tda1997x_query_dv_timings(struct v4l2_subdev *sd, · 10 unmodified lines | ||
| 1724 | 1724 | |
| 1725 | 1725 | static const struct v4l2_subdev_video_ops tda1997x_video_ops = { |
| 1726 | 1726 | .g_input_status = tda1997x_g_input_status, |
| 1727 | − .s_dv_timings = tda1997x_s_dv_timings, | |
| 1728 | − .g_dv_timings = tda1997x_g_dv_timings, | |
| 1729 | − .query_dv_timings = tda1997x_query_dv_timings, | |
| 1730 | 1727 | }; |
| 1731 | 1728 | |
| 1732 | 1729 | |
| @@ -1930,6 +1927,9 @@ static const struct v4l2_subdev_pad_ops tda1997x_pad_ops = { · 197 unmodified lines | ||
| 1930 | 1927 | .set_fmt = tda1997x_set_format, |
| 1931 | 1928 | .get_edid = tda1997x_get_edid, |
| 1932 | 1929 | .set_edid = tda1997x_set_edid, |
| 1930 | + .s_dv_timings = tda1997x_s_dv_timings, | |
| 1931 | + .g_dv_timings = tda1997x_g_dv_timings, | |
| 1932 | + .query_dv_timings = tda1997x_query_dv_timings, | |
| 1933 | 1933 | .dv_timings_cap = tda1997x_get_dv_timings_cap, |
| 1934 | 1934 | .enum_dv_timings = tda1997x_enum_dv_timings, |
| 1935 | 1935 | }; |
| @@ -193,8 +193,8 @@ static int ths7303_s_stream(struct v4l2_subdev *sd, int enable) · 192 unmodified lines | ||
| 193 | 193 | } |
| 194 | 194 | |
| 195 | 195 | /* for setting filter for HD output */ |
| 196 | −static int ths7303_s_dv_timings(struct v4l2_subdev *sd, | |
| 197 | − struct v4l2_dv_timings *dv_timings) | |
| 196 | +static int ths7303_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 197 | + struct v4l2_dv_timings *dv_timings) | |
| 198 | 198 | { |
| 199 | 199 | struct ths7303_state *state = to_state(sd); |
| 200 | 200 | |
| @@ -210,7 +210,6 @@ static int ths7303_s_dv_timings(struct v4l2_subdev *sd, · 9 unmodified lines | ||
| 210 | 210 | static const struct v4l2_subdev_video_ops ths7303_video_ops = { |
| 211 | 211 | .s_stream = ths7303_s_stream, |
| 212 | 212 | .s_std_output = ths7303_s_std_output, |
| 213 | − .s_dv_timings = ths7303_s_dv_timings, | |
| 214 | 213 | }; |
| 215 | 214 | |
| 216 | 215 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
| @@ -317,9 +316,14 @@ static const struct v4l2_subdev_core_ops ths7303_core_ops = { · 100 unmodified lines | ||
| 317 | 316 | #endif |
| 318 | 317 | }; |
| 319 | 318 | |
| 319 | +static const struct v4l2_subdev_pad_ops ths7303_pad_ops = { | |
| 320 | + .s_dv_timings = ths7303_s_dv_timings, | |
| 321 | +}; | |
| 322 | + | |
| 320 | 323 | static const struct v4l2_subdev_ops ths7303_ops = { |
| 321 | 324 | .core = &ths7303_core_ops, |
| 322 | 325 | .video = &ths7303_video_ops, |
| 326 | + .pad = &ths7303_pad_ops, | |
| 323 | 327 | }; |
| 324 | 328 | |
| 325 | 329 | 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) · 357 unmodified lines | ||
| 358 | 358 | bt->hsync, bt->vsync); |
| 359 | 359 | } |
| 360 | 360 | |
| 361 | −static int ths8200_s_dv_timings(struct v4l2_subdev *sd, | |
| 361 | +static int ths8200_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 362 | 362 | struct v4l2_dv_timings *timings) |
| 363 | 363 | { |
| 364 | 364 | struct ths8200_state *state = to_state(sd); |
| 365 | 365 | |
| 366 | 366 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); |
| 367 | 367 | |
| 368 | + if (pad != 0) | |
| 369 | + return -EINVAL; | |
| 370 | + | |
| 368 | 371 | if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap, |
| 369 | 372 | NULL, NULL)) |
| 370 | 373 | return -EINVAL; |
| @@ -385,13 +388,16 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd, · 14 unmodified lines | ||
| 385 | 388 | return 0; |
| 386 | 389 | } |
| 387 | 390 | |
| 388 | −static int ths8200_g_dv_timings(struct v4l2_subdev *sd, | |
| 391 | +static int ths8200_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 389 | 392 | struct v4l2_dv_timings *timings) |
| 390 | 393 | { |
| 391 | 394 | struct ths8200_state *state = to_state(sd); |
| 392 | 395 | |
| 393 | 396 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); |
| 394 | 397 | |
| 398 | + if (pad != 0) | |
| 399 | + return -EINVAL; | |
| 400 | + | |
| 395 | 401 | *timings = state->dv_timings; |
| 396 | 402 | |
| 397 | 403 | return 0; |
| @@ -420,11 +426,11 @@ static int ths8200_dv_timings_cap(struct v4l2_subdev *sd, · 22 unmodified lines | ||
| 420 | 426 | /* Specific video subsystem operation handlers */ |
| 421 | 427 | static const struct v4l2_subdev_video_ops ths8200_video_ops = { |
| 422 | 428 | .s_stream = ths8200_s_stream, |
| 423 | − .s_dv_timings = ths8200_s_dv_timings, | |
| 424 | − .g_dv_timings = ths8200_g_dv_timings, | |
| 425 | 429 | }; |
| 426 | 430 | |
| 427 | 431 | static const struct v4l2_subdev_pad_ops ths8200_pad_ops = { |
| 432 | + .s_dv_timings = ths8200_s_dv_timings, | |
| 433 | + .g_dv_timings = ths8200_g_dv_timings, | |
| 428 | 434 | .enum_dv_timings = ths8200_enum_dv_timings, |
| 429 | 435 | .dv_timings_cap = ths8200_dv_timings_cap, |
| 430 | 436 | }; |
| @@ -546,13 +546,16 @@ static int tvp7002_write_inittab(struct v4l2_subdev *sd, · 545 unmodified lines | ||
| 546 | 546 | return error; |
| 547 | 547 | } |
| 548 | 548 | |
| 549 | −static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, | |
| 550 | − struct v4l2_dv_timings *dv_timings) | |
| 549 | +static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 550 | + struct v4l2_dv_timings *dv_timings) | |
| 551 | 551 | { |
| 552 | 552 | struct tvp7002 *device = to_tvp7002(sd); |
| 553 | 553 | const struct v4l2_bt_timings *bt = &dv_timings->bt; |
| 554 | 554 | int i; |
| 555 | 555 | |
| 556 | + if (pad != 0) | |
| 557 | + return -EINVAL; | |
| 558 | + | |
| 556 | 559 | if (dv_timings->type != V4L2_DV_BT_656_1120) |
| 557 | 560 | return -EINVAL; |
| 558 | 561 | for (i = 0; i < NUM_TIMINGS; i++) { |
| @@ -566,11 +569,14 @@ static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, · 7 unmodified lines | ||
| 566 | 569 | return -EINVAL; |
| 567 | 570 | } |
| 568 | 571 | |
| 569 | −static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, | |
| 570 | − struct v4l2_dv_timings *dv_timings) | |
| 572 | +static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 573 | + struct v4l2_dv_timings *dv_timings) | |
| 571 | 574 | { |
| 572 | 575 | struct tvp7002 *device = to_tvp7002(sd); |
| 573 | 576 | |
| 577 | + if (pad != 0) | |
| 578 | + return -EINVAL; | |
| 579 | + | |
| 574 | 580 | *dv_timings = device->current_timings->timings; |
| 575 | 581 | return 0; |
| 576 | 582 | } |
| @@ -659,12 +665,16 @@ static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) · 82 unmodified lines | ||
| 659 | 665 | return 0; |
| 660 | 666 | } |
| 661 | 667 | |
| 662 | −static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, | |
| 663 | − struct v4l2_dv_timings *timings) | |
| 668 | +static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad, | |
| 669 | + struct v4l2_dv_timings *timings) | |
| 664 | 670 | { |
| 665 | 671 | int index; |
| 666 | − int err = tvp7002_query_dv(sd, &index); | |
| 672 | + int err; | |
| 673 | + | |
| 674 | + if (pad != 0) | |
| 675 | + return -EINVAL; | |
| 667 | 676 | |
| 677 | + err = tvp7002_query_dv(sd, &index); | |
| 668 | 678 | if (err) |
| 669 | 679 | return err; |
| 670 | 680 | *timings = tvp7002_timings[index].timings; |
| @@ -861,9 +871,6 @@ static const struct v4l2_subdev_core_ops tvp7002_core_ops = { · 190 unmodified lines | ||
| 861 | 871 | |
| 862 | 872 | /* Specific video subsystem operation handlers */ |
| 863 | 873 | static const struct v4l2_subdev_video_ops tvp7002_video_ops = { |
| 864 | − .g_dv_timings = tvp7002_g_dv_timings, | |
| 865 | − .s_dv_timings = tvp7002_s_dv_timings, | |
| 866 | − .query_dv_timings = tvp7002_query_dv_timings, | |
| 867 | 874 | .s_stream = tvp7002_s_stream, |
| 868 | 875 | }; |
| 869 | 876 | |
| @@ -872,6 +879,9 @@ static const struct v4l2_subdev_pad_ops tvp7002_pad_ops = { · 2 unmodified lines | ||
| 872 | 879 | .enum_mbus_code = tvp7002_enum_mbus_code, |
| 873 | 880 | .get_fmt = tvp7002_get_pad_format, |
| 874 | 881 | .set_fmt = tvp7002_set_pad_format, |
| 882 | + .g_dv_timings = tvp7002_g_dv_timings, | |
| 883 | + .s_dv_timings = tvp7002_s_dv_timings, | |
| 884 | + .query_dv_timings = tvp7002_query_dv_timings, | |
| 875 | 885 | .enum_dv_timings = tvp7002_enum_dv_timings, |
| 876 | 886 | }; |
| 877 | 887 | |
| @@ -1001,7 +1011,7 @@ static int tvp7002_probe(struct i2c_client *c) · 123 unmodified lines | ||
| 1001 | 1011 | |
| 1002 | 1012 | /* Set registers according to default video mode */ |
| 1003 | 1013 | timings = device->current_timings->timings; |
| 1004 | − error = tvp7002_s_dv_timings(sd, &timings); | |
| 1014 | + error = tvp7002_s_dv_timings(sd, 0, &timings); | |
| 1005 | 1015 | |
| 1006 | 1016 | #if defined(CONFIG_MEDIA_CONTROLLER) |
| 1007 | 1017 | device->pad.flags = MEDIA_PAD_FL_SOURCE; |
| @@ -245,15 +245,14 @@ int __must_check media_devnode_register(struct media_device *mdev, · 244 unmodified lines | ||
| 245 | 245 | kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor); |
| 246 | 246 | |
| 247 | 247 | /* Part 3: Add the media and char device */ |
| 248 | + set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); | |
| 248 | 249 | ret = cdev_device_add(&devnode->cdev, &devnode->dev); |
| 249 | 250 | if (ret < 0) { |
| 251 | + clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); | |
| 250 | 252 | pr_err("%s: cdev_device_add failed\n", __func__); |
| 251 | 253 | goto cdev_add_error; |
| 252 | 254 | } |
| 253 | 255 | |
| 254 | − /* Part 4: Activate this minor. The char device can now be used. */ | |
| 255 | − set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); | |
| 256 | − | |
| 257 | 256 | return 0; |
| 258 | 257 | |
| 259 | 258 | cdev_add_error: |
| @@ -619,6 +619,12 @@ static int media_pipeline_explore_next_link(struct media_pipeline *pipe, · 618 unmodified lines | ||
| 619 | 619 | link = list_entry(entry->links, typeof(*link), list); |
| 620 | 620 | last_link = media_pipeline_walk_pop(walk); |
| 621 | 621 | |
| 622 | + if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) != MEDIA_LNK_FL_DATA_LINK) { | |
| 623 | + dev_dbg(walk->mdev->dev, | |
| 624 | + "media pipeline: skipping link (not data-link)\n"); | |
| 625 | + return 0; | |
| 626 | + } | |
| 627 | + | |
| 622 | 628 | dev_dbg(walk->mdev->dev, |
| 623 | 629 | "media pipeline: exploring link '%s':%u -> '%s':%u\n", |
| 624 | 630 | link->source->entity->name, link->source->index, |
| @@ -344,30 +344,7 @@ static struct sdio_driver smssdio_driver = { · 343 unmodified lines | ||
| 344 | 344 | .probe = smssdio_probe, |
| 345 | 345 | .remove = smssdio_remove, |
| 346 | 346 | }; |
| 347 | − | |
| 348 | −/*******************************************************************/ | |
| 349 | −/* Module functions */ | |
| 350 | −/*******************************************************************/ | |
| 351 | − | |
| 352 | −static int __init smssdio_module_init(void) | |
| 353 | −{ | |
| 354 | − int ret = 0; | |
| 355 | − | |
| 356 | − printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n"); | |
| 357 | − printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n"); | |
| 358 | − | |
| 359 | − ret = sdio_register_driver(&smssdio_driver); | |
| 360 | − | |
| 361 | − return ret; | |
| 362 | −} | |
| 363 | − | |
| 364 | −static void __exit smssdio_module_exit(void) | |
| 365 | −{ | |
| 366 | − sdio_unregister_driver(&smssdio_driver); | |
| 367 | −} | |
| 368 | − | |
| 369 | −module_init(smssdio_module_init); | |
| 370 | −module_exit(smssdio_module_exit); | |
| 347 | +module_sdio_driver(smssdio_driver); | |
| 371 | 348 | |
| 372 | 349 | MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver"); |
| 373 | 350 | MODULE_AUTHOR("Pierre Ossman"); |
| @@ -633,7 +633,7 @@ static int cobalt_s_dv_timings(struct file *file, void *priv_fh, · 632 unmodified lines | ||
| 633 | 633 | return -EBUSY; |
| 634 | 634 | |
| 635 | 635 | err = v4l2_subdev_call(s->sd, |
| 636 | − video, s_dv_timings, timings); | |
| 636 | + pad, s_dv_timings, 0, timings); | |
| 637 | 637 | if (!err) { |
| 638 | 638 | s->timings = *timings; |
| 639 | 639 | s->width = timings->bt.width; |
| @@ -653,7 +653,7 @@ static int cobalt_g_dv_timings(struct file *file, void *priv_fh, · 13 unmodified lines | ||
| 653 | 653 | return 0; |
| 654 | 654 | } |
| 655 | 655 | return v4l2_subdev_call(s->sd, |
| 656 | − video, g_dv_timings, timings); | |
| 656 | + pad, g_dv_timings, 0, timings); | |
| 657 | 657 | } |
| 658 | 658 | |
| 659 | 659 | 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, · 6 unmodified lines | ||
| 666 | 666 | return 0; |
| 667 | 667 | } |
| 668 | 668 | return v4l2_subdev_call(s->sd, |
| 669 | − video, query_dv_timings, timings); | |
| 669 | + pad, query_dv_timings, 0, timings); | |
| 670 | 670 | } |
| 671 | 671 | |
| 672 | 672 | 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, · 407 unmodified lines | ||
| 1080 | 1080 | if (s->input == 1) |
| 1081 | 1081 | timings = cea1080p60; |
| 1082 | 1082 | else |
| 1083 | − err = v4l2_subdev_call(s->sd, video, g_dv_timings, &timings); | |
| 1083 | + err = v4l2_subdev_call(s->sd, pad, g_dv_timings, 0, &timings); | |
| 1084 | 1084 | if (!err) |
| 1085 | 1085 | *f = v4l2_dv_timings_aspect_ratio(&timings); |
| 1086 | 1086 | return err; |
| @@ -1099,7 +1099,7 @@ static int cobalt_g_selection(struct file *file, void *fh, · 12 unmodified lines | ||
| 1099 | 1099 | if (s->input == 1) |
| 1100 | 1100 | timings = cea1080p60; |
| 1101 | 1101 | else |
| 1102 | − err = v4l2_subdev_call(s->sd, video, g_dv_timings, &timings); | |
| 1102 | + err = v4l2_subdev_call(s->sd, pad, g_dv_timings, 0, &timings); | |
| 1103 | 1103 | |
| 1104 | 1104 | if (err) |
| 1105 | 1105 | return err; |
| @@ -1243,7 +1243,7 @@ static int cobalt_node_register(struct cobalt *cobalt, int node) · 137 unmodified lines | ||
| 1243 | 1243 | if (s->sd) |
| 1244 | 1244 | vdev->ctrl_handler = s->sd->ctrl_handler; |
| 1245 | 1245 | s->timings = dv1080p60; |
| 1246 | − v4l2_subdev_call(s->sd, video, s_dv_timings, &s->timings); | |
| 1246 | + v4l2_subdev_call(s->sd, pad, s_dv_timings, 0, &s->timings); | |
| 1247 | 1247 | if (!s->is_output && s->sd) |
| 1248 | 1248 | cobalt_enable_input(s); |
| 1249 | 1249 | vdev->ioctl_ops = s->is_dummy ? &cobalt_ioctl_empty_ops : |
| @@ -1,11 +1,13 @@ | ||
| 1 | 1 | # SPDX-License-Identifier: GPL-2.0-only |
| 2 | 2 | |
| 3 | 3 | source "drivers/media/pci/intel/ipu3/Kconfig" |
| 4 | +source "drivers/media/pci/intel/ipu6/Kconfig" | |
| 4 | 5 | source "drivers/media/pci/intel/ivsc/Kconfig" |
| 5 | 6 | |
| 6 | 7 | config IPU_BRIDGE |
| 7 | 8 | tristate "Intel IPU Bridge" |
| 8 | − depends on I2C && ACPI | |
| 9 | + depends on ACPI || COMPILE_TEST | |
| 10 | + depends on I2C | |
| 9 | 11 | help |
| 10 | 12 | The IPU bridge is a helper library for Intel IPU drivers to |
| 11 | 13 | function on systems shipped with Windows. |
| @@ -5,3 +5,4 @@ · 4 unmodified lines | ||
| 5 | 5 | obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o |
| 6 | 6 | obj-y += ipu3/ |
| 7 | 7 | obj-y += ivsc/ |
| 8 | +obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ | |
| @@ -15,6 +15,8 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <media/ipu-bridge.h> |
| 16 | 16 | #include <media/v4l2-fwnode.h> |
| 17 | 17 | |
| 18 | +#define ADEV_DEV(adev) ACPI_PTR(&((adev)->dev)) | |
| 19 | + | |
| 18 | 20 | /* |
| 19 | 21 | * 92335fcf-3203-4472-af93-7b4453ac29da |
| 20 | 22 | * |
| @@ -87,6 +89,7 @@ static const char * const ipu_vcm_types[] = { · 66 unmodified lines | ||
| 87 | 89 | "lc898212axb", |
| 88 | 90 | }; |
| 89 | 91 | |
| 92 | +#if IS_ENABLED(CONFIG_ACPI) | |
| 90 | 93 | /* |
| 91 | 94 | * Used to figure out IVSC acpi device by ipu_bridge_get_ivsc_acpi_dev() |
| 92 | 95 | * instead of device and driver match to probe IVSC device. |
| @@ -100,13 +103,13 @@ static const struct acpi_device_id ivsc_acpi_ids[] = { · 7 unmodified lines | ||
| 100 | 103 | |
| 101 | 104 | static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) |
| 102 | 105 | { |
| 103 | − acpi_handle handle = acpi_device_handle(adev); | |
| 104 | − struct acpi_device *consumer, *ivsc_adev; | |
| 105 | 106 | unsigned int i; |
| 106 | 107 | |
| 107 | 108 | for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) { |
| 108 | 109 | const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i]; |
| 110 | + struct acpi_device *consumer, *ivsc_adev; | |
| 109 | 111 | |
| 112 | + acpi_handle handle = acpi_device_handle(adev); | |
| 110 | 113 | for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1) |
| 111 | 114 | /* camera sensor depends on IVSC in DSDT if exist */ |
| 112 | 115 | 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 · 5 unmodified lines | ||
| 118 | 121 | |
| 119 | 122 | return NULL; |
| 120 | 123 | } |
| 124 | +#else | |
| 125 | +static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) | |
| 126 | +{ | |
| 127 | + return NULL; | |
| 128 | +} | |
| 129 | +#endif | |
| 121 | 130 | |
| 122 | 131 | static int ipu_bridge_match_ivsc_dev(struct device *dev, const void *adev) |
| 123 | 132 | { |
| @@ -163,7 +172,7 @@ static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor, · 39 unmodified lines | ||
| 163 | 172 | csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); |
| 164 | 173 | if (!csi_dev) { |
| 165 | 174 | acpi_dev_put(adev); |
| 166 | − dev_err(&adev->dev, "Failed to find MEI CSI dev\n"); | |
| 175 | + dev_err(ADEV_DEV(adev), "Failed to find MEI CSI dev\n"); | |
| 167 | 176 | return -ENODEV; |
| 168 | 177 | } |
| 169 | 178 | |
| @@ -182,24 +191,25 @@ static int ipu_bridge_read_acpi_buffer(struct acpi_device *adev, char *id, · 12 unmodified lines | ||
| 182 | 191 | acpi_status status; |
| 183 | 192 | int ret = 0; |
| 184 | 193 | |
| 185 | − status = acpi_evaluate_object(adev->handle, id, NULL, &buffer); | |
| 194 | + status = acpi_evaluate_object(ACPI_PTR(adev->handle), | |
| 195 | + id, NULL, &buffer); | |
| 186 | 196 | if (ACPI_FAILURE(status)) |
| 187 | 197 | return -ENODEV; |
| 188 | 198 | |
| 189 | 199 | obj = buffer.pointer; |
| 190 | 200 | if (!obj) { |
| 191 | − dev_err(&adev->dev, "Couldn't locate ACPI buffer\n"); | |
| 201 | + dev_err(ADEV_DEV(adev), "Couldn't locate ACPI buffer\n"); | |
| 192 | 202 | return -ENODEV; |
| 193 | 203 | } |
| 194 | 204 | |
| 195 | 205 | if (obj->type != ACPI_TYPE_BUFFER) { |
| 196 | − dev_err(&adev->dev, "Not an ACPI buffer\n"); | |
| 206 | + dev_err(ADEV_DEV(adev), "Not an ACPI buffer\n"); | |
| 197 | 207 | ret = -ENODEV; |
| 198 | 208 | goto out_free_buff; |
| 199 | 209 | } |
| 200 | 210 | |
| 201 | 211 | if (obj->buffer.length > size) { |
| 202 | − dev_err(&adev->dev, "Given buffer is too small\n"); | |
| 212 | + dev_err(ADEV_DEV(adev), "Given buffer is too small\n"); | |
| 203 | 213 | ret = -EINVAL; |
| 204 | 214 | goto out_free_buff; |
| 205 | 215 | } |
| @@ -220,7 +230,7 @@ static u32 ipu_bridge_parse_rotation(struct acpi_device *adev, · 14 unmodified lines | ||
| 220 | 230 | case IPU_SENSOR_ROTATION_INVERTED: |
| 221 | 231 | return 180; |
| 222 | 232 | default: |
| 223 | − dev_warn(&adev->dev, | |
| 233 | + dev_warn(ADEV_DEV(adev), | |
| 224 | 234 | "Unknown rotation %d. Assume 0 degree rotation\n", |
| 225 | 235 | ssdb->degree); |
| 226 | 236 | return 0; |
| @@ -230,12 +240,14 @@ static u32 ipu_bridge_parse_rotation(struct acpi_device *adev, · 3 unmodified lines | ||
| 230 | 240 | static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_device *adev) |
| 231 | 241 | { |
| 232 | 242 | enum v4l2_fwnode_orientation orientation; |
| 233 | − struct acpi_pld_info *pld; | |
| 234 | − acpi_status status; | |
| 243 | + struct acpi_pld_info *pld = NULL; | |
| 244 | + acpi_status status = AE_ERROR; | |
| 235 | 245 | |
| 246 | +#if IS_ENABLED(CONFIG_ACPI) | |
| 236 | 247 | status = acpi_get_physical_device_location(adev->handle, &pld); |
| 248 | +#endif | |
| 237 | 249 | if (ACPI_FAILURE(status)) { |
| 238 | − dev_warn(&adev->dev, "_PLD call failed, using default orientation\n"); | |
| 250 | + dev_warn(ADEV_DEV(adev), "_PLD call failed, using default orientation\n"); | |
| 239 | 251 | return V4L2_FWNODE_ORIENTATION_EXTERNAL; |
| 240 | 252 | } |
| 241 | 253 | |
| @@ -253,7 +265,8 @@ static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_dev · 11 unmodified lines | ||
| 253 | 265 | orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL; |
| 254 | 266 | break; |
| 255 | 267 | default: |
| 256 | − dev_warn(&adev->dev, "Unknown _PLD panel val %d\n", pld->panel); | |
| 268 | + dev_warn(ADEV_DEV(adev), "Unknown _PLD panel val %d\n", | |
| 269 | + pld->panel); | |
| 257 | 270 | orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL; |
| 258 | 271 | break; |
| 259 | 272 | } |
| @@ -272,12 +285,12 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) · 12 unmodified lines | ||
| 272 | 285 | return ret; |
| 273 | 286 | |
| 274 | 287 | if (ssdb.vcmtype > ARRAY_SIZE(ipu_vcm_types)) { |
| 275 | − dev_warn(&adev->dev, "Unknown VCM type %d\n", ssdb.vcmtype); | |
| 288 | + dev_warn(ADEV_DEV(adev), "Unknown VCM type %d\n", ssdb.vcmtype); | |
| 276 | 289 | ssdb.vcmtype = 0; |
| 277 | 290 | } |
| 278 | 291 | |
| 279 | 292 | if (ssdb.lanes > IPU_MAX_LANES) { |
| 280 | − dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n"); | |
| 293 | + dev_err(ADEV_DEV(adev), "Number of lanes in SSDB is invalid\n"); | |
| 281 | 294 | return -EINVAL; |
| 282 | 295 | } |
| 283 | 296 | |
| @@ -465,8 +478,14 @@ static void ipu_bridge_create_connection_swnodes(struct ipu_bridge *bridge, · 181 unmodified lines | ||
| 465 | 478 | sensor->ipu_properties); |
| 466 | 479 | |
| 467 | 480 | if (sensor->csi_dev) { |
| 481 | + const char *device_hid = ""; | |
| 482 | + | |
| 483 | +#if IS_ENABLED(CONFIG_ACPI) | |
| 484 | + device_hid = acpi_device_hid(sensor->ivsc_adev); | |
| 485 | +#endif | |
| 486 | + | |
| 468 | 487 | snprintf(sensor->ivsc_name, sizeof(sensor->ivsc_name), "%s-%u", |
| 469 | − acpi_device_hid(sensor->ivsc_adev), sensor->link); | |
| 488 | + device_hid, sensor->link); | |
| 470 | 489 | |
| 471 | 490 | nodes[SWNODE_IVSC_HID] = NODE_SENSOR(sensor->ivsc_name, |
| 472 | 491 | sensor->ivsc_properties); |
| @@ -631,11 +650,15 @@ static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg, · 158 unmodified lines | ||
| 631 | 650 | { |
| 632 | 651 | struct fwnode_handle *fwnode, *primary; |
| 633 | 652 | struct ipu_sensor *sensor; |
| 634 | − struct acpi_device *adev; | |
| 653 | + struct acpi_device *adev = NULL; | |
| 635 | 654 | int ret; |
| 636 | 655 | |
| 656 | +#if IS_ENABLED(CONFIG_ACPI) | |
| 637 | 657 | for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) { |
| 638 | − if (!adev->status.enabled) | |
| 658 | +#else | |
| 659 | + while (true) { | |
| 660 | +#endif | |
| 661 | + if (!ACPI_PTR(adev->status.enabled)) | |
| 639 | 662 | continue; |
| 640 | 663 | |
| 641 | 664 | if (bridge->n_sensors >= IPU_MAX_PORTS) { |
| @@ -671,7 +694,7 @@ static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg, · 29 unmodified lines | ||
| 671 | 694 | goto err_free_swnodes; |
| 672 | 695 | } |
| 673 | 696 | |
| 674 | − sensor->adev = acpi_dev_get(adev); | |
| 697 | + sensor->adev = ACPI_PTR(acpi_dev_get(adev)); | |
| 675 | 698 | |
| 676 | 699 | primary = acpi_fwnode_handle(adev); |
| 677 | 700 | primary->secondary = fwnode; |
| @@ -727,11 +750,16 @@ static int ipu_bridge_ivsc_is_ready(void) · 49 unmodified lines | ||
| 727 | 750 | unsigned int i; |
| 728 | 751 | |
| 729 | 752 | for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) { |
| 753 | +#if IS_ENABLED(CONFIG_ACPI) | |
| 730 | 754 | const struct ipu_sensor_config *cfg = |
| 731 | 755 | &ipu_supported_sensors[i]; |
| 732 | 756 | |
| 733 | 757 | for_each_acpi_dev_match(sensor_adev, cfg->hid, NULL, -1) { |
| 734 | − if (!sensor_adev->status.enabled) | |
| 758 | +#else | |
| 759 | + while (true) { | |
| 760 | + sensor_adev = NULL; | |
| 761 | +#endif | |
| 762 | + if (!ACPI_PTR(sensor_adev->status.enabled)) | |
| 735 | 763 | continue; |
| 736 | 764 | |
| 737 | 765 | adev = ipu_bridge_get_ivsc_acpi_dev(sensor_adev); |
| @@ -4,9 +4,9 @@ · 3 unmodified lines | ||
| 4 | 4 | * |
| 5 | 5 | * Based partially on Intel IPU4 driver written by |
| 6 | 6 | * Sakari Ailus <sakari.ailus@linux.intel.com> |
| 7 | − * Samu Onkalo <samu.onkalo@intel.com> | |
| 7 | + * Samu Onkalo | |
| 8 | 8 | * Jouni Högander <jouni.hogander@intel.com> |
| 9 | − * Jouni Ukkonen <jouni.ukkonen@intel.com> | |
| 9 | + * Jouni Ukkonen | |
| 10 | 10 | * Antti Laakso <antti.laakso@intel.com> |
| 11 | 11 | * et al. |
| 12 | 12 | */ |
| @@ -1752,11 +1752,6 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, · 1739 unmodified lines | ||
| 1752 | 1752 | |
| 1753 | 1753 | v4l2_async_nf_init(&cio2->notifier, &cio2->v4l2_dev); |
| 1754 | 1754 | |
| 1755 | − /* Register notifier for subdevices we care */ | |
| 1756 | − r = cio2_parse_firmware(cio2); | |
| 1757 | − if (r) | |
| 1758 | − goto fail_clean_notifier; | |
| 1759 | − | |
| 1760 | 1755 | r = devm_request_irq(dev, pci_dev->irq, cio2_irq, IRQF_SHARED, |
| 1761 | 1756 | CIO2_NAME, cio2); |
| 1762 | 1757 | if (r) { |
| @@ -1764,6 +1759,11 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, · 1 unmodified line | ||
| 1764 | 1759 | goto fail_clean_notifier; |
| 1765 | 1760 | } |
| 1766 | 1761 | |
| 1762 | + /* Register notifier for subdevices we care */ | |
| 1763 | + r = cio2_parse_firmware(cio2); | |
| 1764 | + if (r) | |
| 1765 | + goto fail_clean_notifier; | |
| 1766 | + | |
| 1767 | 1767 | pm_runtime_put_noidle(dev); |
| 1768 | 1768 | pm_runtime_allow(dev); |
| 1769 | 1769 | |
| @@ -1807,16 +1807,10 @@ static int __maybe_unused cio2_runtime_suspend(struct device *dev) · 37 unmodified lines | ||
| 1807 | 1807 | struct pci_dev *pci_dev = to_pci_dev(dev); |
| 1808 | 1808 | struct cio2_device *cio2 = pci_get_drvdata(pci_dev); |
| 1809 | 1809 | void __iomem *const base = cio2->base; |
| 1810 | − u16 pm; | |
| 1811 | 1810 | |
| 1812 | 1811 | writel(CIO2_D0I3C_I3, base + CIO2_REG_D0I3C); |
| 1813 | 1812 | dev_dbg(dev, "cio2 runtime suspend.\n"); |
| 1814 | 1813 | |
| 1815 | − pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm); | |
| 1816 | − pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT; | |
| 1817 | − pm |= CIO2_PMCSR_D3; | |
| 1818 | − pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm); | |
| 1819 | − | |
| 1820 | 1814 | return 0; |
| 1821 | 1815 | } |
| 1822 | 1816 | |
| @@ -1825,15 +1819,10 @@ static int __maybe_unused cio2_runtime_resume(struct device *dev) · 2 unmodified lines | ||
| 1825 | 1819 | struct pci_dev *pci_dev = to_pci_dev(dev); |
| 1826 | 1820 | struct cio2_device *cio2 = pci_get_drvdata(pci_dev); |
| 1827 | 1821 | void __iomem *const base = cio2->base; |
| 1828 | − u16 pm; | |
| 1829 | 1822 | |
| 1830 | 1823 | writel(CIO2_D0I3C_RR, base + CIO2_REG_D0I3C); |
| 1831 | 1824 | dev_dbg(dev, "cio2 runtime resume.\n"); |
| 1832 | 1825 | |
| 1833 | − pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm); | |
| 1834 | − pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT; | |
| 1835 | − pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm); | |
| 1836 | − | |
| 1837 | 1826 | return 0; |
| 1838 | 1827 | } |
| 1839 | 1828 | |
| @@ -2006,10 +1995,10 @@ static struct pci_driver cio2_pci_driver = { · 166 unmodified lines | ||
| 2006 | 1995 | |
| 2007 | 1996 | module_pci_driver(cio2_pci_driver); |
| 2008 | 1997 | |
| 2009 | −MODULE_AUTHOR("Tuukka Toivonen <tuukka.toivonen@intel.com>"); | |
| 1998 | +MODULE_AUTHOR("Tuukka Toivonen"); | |
| 2010 | 1999 | MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); |
| 2011 | 2000 | MODULE_AUTHOR("Jian Xu Zheng"); |
| 2012 | −MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>"); | |
| 2001 | +MODULE_AUTHOR("Yuning Pu"); | |
| 2013 | 2002 | MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>"); |
| 2014 | 2003 | MODULE_LICENSE("GPL v2"); |
| 2015 | 2004 | MODULE_DESCRIPTION("IPU3 CIO2 driver"); |
| @@ -320,10 +320,6 @@ struct pci_dev; · 319 unmodified lines | ||
| 320 | 320 | #define CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT 0x4 |
| 321 | 321 | #define CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT 0x570 |
| 322 | 322 | |
| 323 | −#define CIO2_PMCSR_OFFSET 4U | |
| 324 | −#define CIO2_PMCSR_D0D3_SHIFT 2U | |
| 325 | −#define CIO2_PMCSR_D3 0x3 | |
| 326 | − | |
| 327 | 323 | struct cio2_csi2_timing { |
| 328 | 324 | s32 clk_termen; |
| 329 | 325 | s32 clk_settle; |
| @@ -0,0 +1,18 @@ | ||
| 1 | +config VIDEO_INTEL_IPU6 | |
| 2 | + tristate "Intel IPU6 driver" | |
| 3 | + depends on ACPI || COMPILE_TEST | |
| 4 | + depends on VIDEO_DEV | |
| 5 | + depends on X86 && X86_64 && HAS_DMA | |
| 6 | + select DMA_OPS | |
| 7 | + select IOMMU_IOVA | |
| 8 | + select VIDEO_V4L2_SUBDEV_API | |
| 9 | + select MEDIA_CONTROLLER | |
| 10 | + select VIDEOBUF2_DMA_CONTIG | |
| 11 | + select V4L2_FWNODE | |
| 12 | + select IPU_BRIDGE | |
| 13 | + help | |
| 14 | + This is the 6th Gen Intel Image Processing Unit, found in Intel SoCs | |
| 15 | + and used for capturing images and video from camera sensors. | |
| 16 | + | |
| 17 | + To compile this driver, say Y here! It contains 2 modules - | |
| 18 | + intel_ipu6 and intel_ipu6_isys. | |
| @@ -0,0 +1,23 @@ | ||
| 1 | +# SPDX-License-Identifier: GPL-2.0-only | |
| 2 | + | |
| 3 | +intel-ipu6-y := ipu6.o \ | |
| 4 | + ipu6-bus.o \ | |
| 5 | + ipu6-dma.o \ | |
| 6 | + ipu6-mmu.o \ | |
| 7 | + ipu6-buttress.o \ | |
| 8 | + ipu6-cpd.o \ | |
| 9 | + ipu6-fw-com.o | |
| 10 | + | |
| 11 | +obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6.o | |
| 12 | + | |
| 13 | +intel-ipu6-isys-y := ipu6-isys.o \ | |
| 14 | + ipu6-isys-csi2.o \ | |
| 15 | + ipu6-fw-isys.o \ | |
| 16 | + ipu6-isys-video.o \ | |
| 17 | + ipu6-isys-queue.o \ | |
| 18 | + ipu6-isys-subdev.o \ | |
| 19 | + ipu6-isys-mcd-phy.o \ | |
| 20 | + ipu6-isys-jsl-phy.o \ | |
| 21 | + ipu6-isys-dwc-phy.o | |
| 22 | + | |
| 23 | +obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6-isys.o | |
| @@ -0,0 +1,165 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013 - 2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/auxiliary_bus.h> | |
| 7 | +#include <linux/device.h> | |
| 8 | +#include <linux/dma-mapping.h> | |
| 9 | +#include <linux/err.h> | |
| 10 | +#include <linux/list.h> | |
| 11 | +#include <linux/mutex.h> | |
| 12 | +#include <linux/pci.h> | |
| 13 | +#include <linux/pm_domain.h> | |
| 14 | +#include <linux/pm_runtime.h> | |
| 15 | +#include <linux/slab.h> | |
| 16 | + | |
| 17 | +#include "ipu6.h" | |
| 18 | +#include "ipu6-bus.h" | |
| 19 | +#include "ipu6-buttress.h" | |
| 20 | +#include "ipu6-dma.h" | |
| 21 | + | |
| 22 | +static int bus_pm_runtime_suspend(struct device *dev) | |
| 23 | +{ | |
| 24 | + struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); | |
| 25 | + int ret; | |
| 26 | + | |
| 27 | + ret = pm_generic_runtime_suspend(dev); | |
| 28 | + if (ret) | |
| 29 | + return ret; | |
| 30 | + | |
| 31 | + ret = ipu6_buttress_power(dev, adev->ctrl, false); | |
| 32 | + if (!ret) | |
| 33 | + return 0; | |
| 34 | + | |
| 35 | + dev_err(dev, "power down failed!\n"); | |
| 36 | + | |
| 37 | + /* Powering down failed, attempt to resume device now */ | |
| 38 | + ret = pm_generic_runtime_resume(dev); | |
| 39 | + if (!ret) | |
| 40 | + return -EBUSY; | |
| 41 | + | |
| 42 | + return -EIO; | |
| 43 | +} | |
| 44 | + | |
| 45 | +static int bus_pm_runtime_resume(struct device *dev) | |
| 46 | +{ | |
| 47 | + struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); | |
| 48 | + int ret; | |
| 49 | + | |
| 50 | + ret = ipu6_buttress_power(dev, adev->ctrl, true); | |
| 51 | + if (ret) | |
| 52 | + return ret; | |
| 53 | + | |
| 54 | + ret = pm_generic_runtime_resume(dev); | |
| 55 | + if (ret) | |
| 56 | + goto out_err; | |
| 57 | + | |
| 58 | + return 0; | |
| 59 | + | |
| 60 | +out_err: | |
| 61 | + ipu6_buttress_power(dev, adev->ctrl, false); | |
| 62 | + | |
| 63 | + return -EBUSY; | |
| 64 | +} | |
| 65 | + | |
| 66 | +static struct dev_pm_domain ipu6_bus_pm_domain = { | |
| 67 | + .ops = { | |
| 68 | + .runtime_suspend = bus_pm_runtime_suspend, | |
| 69 | + .runtime_resume = bus_pm_runtime_resume, | |
| 70 | + }, | |
| 71 | +}; | |
| 72 | + | |
| 73 | +static DEFINE_MUTEX(ipu6_bus_mutex); | |
| 74 | + | |
| 75 | +static void ipu6_bus_release(struct device *dev) | |
| 76 | +{ | |
| 77 | + struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); | |
| 78 | + | |
| 79 | + kfree(adev->pdata); | |
| 80 | + kfree(adev); | |
| 81 | +} | |
| 82 | + | |
| 83 | +struct ipu6_bus_device * | |
| 84 | +ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent, | |
| 85 | + void *pdata, struct ipu6_buttress_ctrl *ctrl, | |
| 86 | + char *name) | |
| 87 | +{ | |
| 88 | + struct auxiliary_device *auxdev; | |
| 89 | + struct ipu6_bus_device *adev; | |
| 90 | + struct ipu6_device *isp = pci_get_drvdata(pdev); | |
| 91 | + int ret; | |
| 92 | + | |
| 93 | + adev = kzalloc(sizeof(*adev), GFP_KERNEL); | |
| 94 | + if (!adev) | |
| 95 | + return ERR_PTR(-ENOMEM); | |
| 96 | + | |
| 97 | + adev->dma_mask = DMA_BIT_MASK(isp->secure_mode ? IPU6_MMU_ADDR_BITS : | |
| 98 | + IPU6_MMU_ADDR_BITS_NON_SECURE); | |
| 99 | + adev->isp = isp; | |
| 100 | + adev->ctrl = ctrl; | |
| 101 | + adev->pdata = pdata; | |
| 102 | + auxdev = &adev->auxdev; | |
| 103 | + auxdev->name = name; | |
| 104 | + auxdev->id = (pci_domain_nr(pdev->bus) << 16) | | |
| 105 | + PCI_DEVID(pdev->bus->number, pdev->devfn); | |
| 106 | + | |
| 107 | + auxdev->dev.parent = parent; | |
| 108 | + auxdev->dev.release = ipu6_bus_release; | |
| 109 | + auxdev->dev.dma_ops = &ipu6_dma_ops; | |
| 110 | + auxdev->dev.dma_mask = &adev->dma_mask; | |
| 111 | + auxdev->dev.dma_parms = pdev->dev.dma_parms; | |
| 112 | + auxdev->dev.coherent_dma_mask = adev->dma_mask; | |
| 113 | + | |
| 114 | + ret = auxiliary_device_init(auxdev); | |
| 115 | + if (ret < 0) { | |
| 116 | + dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n", | |
| 117 | + ret); | |
| 118 | + kfree(adev); | |
| 119 | + return ERR_PTR(ret); | |
| 120 | + } | |
| 121 | + | |
| 122 | + dev_pm_domain_set(&auxdev->dev, &ipu6_bus_pm_domain); | |
| 123 | + | |
| 124 | + pm_runtime_forbid(&adev->auxdev.dev); | |
| 125 | + pm_runtime_enable(&adev->auxdev.dev); | |
| 126 | + | |
| 127 | + return adev; | |
| 128 | +} | |
| 129 | + | |
| 130 | +int ipu6_bus_add_device(struct ipu6_bus_device *adev) | |
| 131 | +{ | |
| 132 | + struct auxiliary_device *auxdev = &adev->auxdev; | |
| 133 | + int ret; | |
| 134 | + | |
| 135 | + ret = auxiliary_device_add(auxdev); | |
| 136 | + if (ret) { | |
| 137 | + auxiliary_device_uninit(auxdev); | |
| 138 | + return ret; | |
| 139 | + } | |
| 140 | + | |
| 141 | + mutex_lock(&ipu6_bus_mutex); | |
| 142 | + list_add(&adev->list, &adev->isp->devices); | |
| 143 | + mutex_unlock(&ipu6_bus_mutex); | |
| 144 | + | |
| 145 | + pm_runtime_allow(&auxdev->dev); | |
| 146 | + | |
| 147 | + return 0; | |
| 148 | +} | |
| 149 | + | |
| 150 | +void ipu6_bus_del_devices(struct pci_dev *pdev) | |
| 151 | +{ | |
| 152 | + struct ipu6_device *isp = pci_get_drvdata(pdev); | |
| 153 | + struct ipu6_bus_device *adev, *save; | |
| 154 | + | |
| 155 | + mutex_lock(&ipu6_bus_mutex); | |
| 156 | + | |
| 157 | + list_for_each_entry_safe(adev, save, &isp->devices, list) { | |
| 158 | + pm_runtime_disable(&adev->auxdev.dev); | |
| 159 | + list_del(&adev->list); | |
| 160 | + auxiliary_device_delete(&adev->auxdev); | |
| 161 | + auxiliary_device_uninit(&adev->auxdev); | |
| 162 | + } | |
| 163 | + | |
| 164 | + mutex_unlock(&ipu6_bus_mutex); | |
| 165 | +} | |
| @@ -0,0 +1,58 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2013 - 2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_BUS_H | |
| 5 | +#define IPU6_BUS_H | |
| 6 | + | |
| 7 | +#include <linux/auxiliary_bus.h> | |
| 8 | +#include <linux/container_of.h> | |
| 9 | +#include <linux/device.h> | |
| 10 | +#include <linux/irqreturn.h> | |
| 11 | +#include <linux/list.h> | |
| 12 | +#include <linux/scatterlist.h> | |
| 13 | +#include <linux/types.h> | |
| 14 | + | |
| 15 | +struct firmware; | |
| 16 | +struct pci_dev; | |
| 17 | + | |
| 18 | +#define IPU6_BUS_NAME IPU6_NAME "-bus" | |
| 19 | + | |
| 20 | +struct ipu6_buttress_ctrl; | |
| 21 | + | |
| 22 | +struct ipu6_bus_device { | |
| 23 | + struct auxiliary_device auxdev; | |
| 24 | + struct auxiliary_driver *auxdrv; | |
| 25 | + const struct ipu6_auxdrv_data *auxdrv_data; | |
| 26 | + struct list_head list; | |
| 27 | + void *pdata; | |
| 28 | + struct ipu6_mmu *mmu; | |
| 29 | + struct ipu6_device *isp; | |
| 30 | + struct ipu6_buttress_ctrl *ctrl; | |
| 31 | + u64 dma_mask; | |
| 32 | + const struct firmware *fw; | |
| 33 | + struct sg_table fw_sgt; | |
| 34 | + u64 *pkg_dir; | |
| 35 | + dma_addr_t pkg_dir_dma_addr; | |
| 36 | + unsigned int pkg_dir_size; | |
| 37 | +}; | |
| 38 | + | |
| 39 | +struct ipu6_auxdrv_data { | |
| 40 | + irqreturn_t (*isr)(struct ipu6_bus_device *adev); | |
| 41 | + irqreturn_t (*isr_threaded)(struct ipu6_bus_device *adev); | |
| 42 | + bool wake_isr_thread; | |
| 43 | +}; | |
| 44 | + | |
| 45 | +#define to_ipu6_bus_device(_dev) \ | |
| 46 | + container_of(to_auxiliary_dev(_dev), struct ipu6_bus_device, auxdev) | |
| 47 | +#define auxdev_to_adev(_auxdev) \ | |
| 48 | + container_of(_auxdev, struct ipu6_bus_device, auxdev) | |
| 49 | +#define ipu6_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev) | |
| 50 | + | |
| 51 | +struct ipu6_bus_device * | |
| 52 | +ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent, | |
| 53 | + void *pdata, struct ipu6_buttress_ctrl *ctrl, | |
| 54 | + char *name); | |
| 55 | +int ipu6_bus_add_device(struct ipu6_bus_device *adev); | |
| 56 | +void ipu6_bus_del_devices(struct pci_dev *pdev); | |
| 57 | + | |
| 58 | +#endif | |
| @@ -0,0 +1,917 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/bitfield.h> | |
| 7 | +#include <linux/bits.h> | |
| 8 | +#include <linux/completion.h> | |
| 9 | +#include <linux/delay.h> | |
| 10 | +#include <linux/device.h> | |
| 11 | +#include <linux/dma-mapping.h> | |
| 12 | +#include <linux/firmware.h> | |
| 13 | +#include <linux/interrupt.h> | |
| 14 | +#include <linux/iopoll.h> | |
| 15 | +#include <linux/math64.h> | |
| 16 | +#include <linux/mm.h> | |
| 17 | +#include <linux/mutex.h> | |
| 18 | +#include <linux/pci.h> | |
| 19 | +#include <linux/pfn.h> | |
| 20 | +#include <linux/pm_runtime.h> | |
| 21 | +#include <linux/scatterlist.h> | |
| 22 | +#include <linux/slab.h> | |
| 23 | +#include <linux/time64.h> | |
| 24 | + | |
| 25 | +#include "ipu6.h" | |
| 26 | +#include "ipu6-bus.h" | |
| 27 | +#include "ipu6-buttress.h" | |
| 28 | +#include "ipu6-platform-buttress-regs.h" | |
| 29 | + | |
| 30 | +#define BOOTLOADER_STATUS_OFFSET 0x15c | |
| 31 | + | |
| 32 | +#define BOOTLOADER_MAGIC_KEY 0xb00710ad | |
| 33 | + | |
| 34 | +#define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 | |
| 35 | +#define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 | |
| 36 | +#define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE | |
| 37 | + | |
| 38 | +#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10 | |
| 39 | + | |
| 40 | +#define BUTTRESS_POWER_TIMEOUT_US (200 * USEC_PER_MSEC) | |
| 41 | + | |
| 42 | +#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US (5 * USEC_PER_SEC) | |
| 43 | +#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US (10 * USEC_PER_SEC) | |
| 44 | +#define BUTTRESS_CSE_FWRESET_TIMEOUT_US (100 * USEC_PER_MSEC) | |
| 45 | + | |
| 46 | +#define BUTTRESS_IPC_TX_TIMEOUT_MS MSEC_PER_SEC | |
| 47 | +#define BUTTRESS_IPC_RX_TIMEOUT_MS MSEC_PER_SEC | |
| 48 | +#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US (1 * USEC_PER_SEC) | |
| 49 | +#define BUTTRESS_TSC_SYNC_TIMEOUT_US (5 * USEC_PER_MSEC) | |
| 50 | + | |
| 51 | +#define BUTTRESS_IPC_RESET_RETRY 2000 | |
| 52 | +#define BUTTRESS_CSE_IPC_RESET_RETRY 4 | |
| 53 | +#define BUTTRESS_IPC_CMD_SEND_RETRY 1 | |
| 54 | + | |
| 55 | +#define BUTTRESS_MAX_CONSECUTIVE_IRQS 100 | |
| 56 | + | |
| 57 | +static const u32 ipu6_adev_irq_mask[2] = { | |
| 58 | + BUTTRESS_ISR_IS_IRQ, | |
| 59 | + BUTTRESS_ISR_PS_IRQ | |
| 60 | +}; | |
| 61 | + | |
| 62 | +int ipu6_buttress_ipc_reset(struct ipu6_device *isp, | |
| 63 | + struct ipu6_buttress_ipc *ipc) | |
| 64 | +{ | |
| 65 | + unsigned int retries = BUTTRESS_IPC_RESET_RETRY; | |
| 66 | + struct ipu6_buttress *b = &isp->buttress; | |
| 67 | + u32 val = 0, csr_in_clr; | |
| 68 | + | |
| 69 | + if (!isp->secure_mode) { | |
| 70 | + dev_dbg(&isp->pdev->dev, "Skip IPC reset for non-secure mode"); | |
| 71 | + return 0; | |
| 72 | + } | |
| 73 | + | |
| 74 | + mutex_lock(&b->ipc_mutex); | |
| 75 | + | |
| 76 | + /* Clear-by-1 CSR (all bits), corresponding internal states. */ | |
| 77 | + val = readl(isp->base + ipc->csr_in); | |
| 78 | + writel(val, isp->base + ipc->csr_in); | |
| 79 | + | |
| 80 | + /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */ | |
| 81 | + writel(ENTRY, isp->base + ipc->csr_out); | |
| 82 | + /* | |
| 83 | + * Clear-by-1 all CSR bits EXCEPT following | |
| 84 | + * bits: | |
| 85 | + * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. | |
| 86 | + * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. | |
| 87 | + * C. Possibly custom bits, depending on | |
| 88 | + * their role. | |
| 89 | + */ | |
| 90 | + csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ | | |
| 91 | + BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID | | |
| 92 | + BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY; | |
| 93 | + | |
| 94 | + do { | |
| 95 | + usleep_range(400, 500); | |
| 96 | + val = readl(isp->base + ipc->csr_in); | |
| 97 | + switch (val) { | |
| 98 | + case ENTRY | EXIT: | |
| 99 | + case ENTRY | EXIT | QUERY: | |
| 100 | + /* | |
| 101 | + * 1) Clear-by-1 CSR bits | |
| 102 | + * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, | |
| 103 | + * IPC_PEER_COMP_ACTIONS_RST_PHASE2). | |
| 104 | + * 2) Set peer CSR bit | |
| 105 | + * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. | |
| 106 | + */ | |
| 107 | + writel(ENTRY | EXIT, isp->base + ipc->csr_in); | |
| 108 | + writel(QUERY, isp->base + ipc->csr_out); | |
| 109 | + break; | |
| 110 | + case ENTRY: | |
| 111 | + case ENTRY | QUERY: | |
| 112 | + /* | |
| 113 | + * 1) Clear-by-1 CSR bits | |
| 114 | + * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, | |
| 115 | + * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE). | |
| 116 | + * 2) Set peer CSR bit | |
| 117 | + * IPC_PEER_COMP_ACTIONS_RST_PHASE1. | |
| 118 | + */ | |
| 119 | + writel(ENTRY | QUERY, isp->base + ipc->csr_in); | |
| 120 | + writel(ENTRY, isp->base + ipc->csr_out); | |
| 121 | + break; | |
| 122 | + case EXIT: | |
| 123 | + case EXIT | QUERY: | |
| 124 | + /* | |
| 125 | + * Clear-by-1 CSR bit | |
| 126 | + * IPC_PEER_COMP_ACTIONS_RST_PHASE2. | |
| 127 | + * 1) Clear incoming doorbell. | |
| 128 | + * 2) Clear-by-1 all CSR bits EXCEPT following | |
| 129 | + * bits: | |
| 130 | + * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. | |
| 131 | + * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. | |
| 132 | + * C. Possibly custom bits, depending on | |
| 133 | + * their role. | |
| 134 | + * 3) Set peer CSR bit | |
| 135 | + * IPC_PEER_COMP_ACTIONS_RST_PHASE2. | |
| 136 | + */ | |
| 137 | + writel(EXIT, isp->base + ipc->csr_in); | |
| 138 | + writel(0, isp->base + ipc->db0_in); | |
| 139 | + writel(csr_in_clr, isp->base + ipc->csr_in); | |
| 140 | + writel(EXIT, isp->base + ipc->csr_out); | |
| 141 | + | |
| 142 | + /* | |
| 143 | + * Read csr_in again to make sure if RST_PHASE2 is done. | |
| 144 | + * If csr_in is QUERY, it should be handled again. | |
| 145 | + */ | |
| 146 | + usleep_range(200, 300); | |
| 147 | + val = readl(isp->base + ipc->csr_in); | |
| 148 | + if (val & QUERY) { | |
| 149 | + dev_dbg(&isp->pdev->dev, | |
| 150 | + "RST_PHASE2 retry csr_in = %x\n", val); | |
| 151 | + break; | |
| 152 | + } | |
| 153 | + mutex_unlock(&b->ipc_mutex); | |
| 154 | + return 0; | |
| 155 | + case QUERY: | |
| 156 | + /* | |
| 157 | + * 1) Clear-by-1 CSR bit | |
| 158 | + * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. | |
| 159 | + * 2) Set peer CSR bit | |
| 160 | + * IPC_PEER_COMP_ACTIONS_RST_PHASE1 | |
| 161 | + */ | |
| 162 | + writel(QUERY, isp->base + ipc->csr_in); | |
| 163 | + writel(ENTRY, isp->base + ipc->csr_out); | |
| 164 | + break; | |
| 165 | + default: | |
| 166 | + dev_warn_ratelimited(&isp->pdev->dev, | |
| 167 | + "Unexpected CSR 0x%x\n", val); | |
| 168 | + break; | |
| 169 | + } | |
| 170 | + } while (retries--); | |
| 171 | + | |
| 172 | + mutex_unlock(&b->ipc_mutex); | |
| 173 | + dev_err(&isp->pdev->dev, "Timed out while waiting for CSE\n"); | |
| 174 | + | |
| 175 | + return -ETIMEDOUT; | |
| 176 | +} | |
| 177 | + | |
| 178 | +static void ipu6_buttress_ipc_validity_close(struct ipu6_device *isp, | |
| 179 | + struct ipu6_buttress_ipc *ipc) | |
| 180 | +{ | |
| 181 | + writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ, | |
| 182 | + isp->base + ipc->csr_out); | |
| 183 | +} | |
| 184 | + | |
| 185 | +static int | |
| 186 | +ipu6_buttress_ipc_validity_open(struct ipu6_device *isp, | |
| 187 | + struct ipu6_buttress_ipc *ipc) | |
| 188 | +{ | |
| 189 | + unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID; | |
| 190 | + void __iomem *addr; | |
| 191 | + int ret; | |
| 192 | + u32 val; | |
| 193 | + | |
| 194 | + writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ, | |
| 195 | + isp->base + ipc->csr_out); | |
| 196 | + | |
| 197 | + addr = isp->base + ipc->csr_in; | |
| 198 | + ret = readl_poll_timeout(addr, val, val & mask, 200, | |
| 199 | + BUTTRESS_IPC_VALIDITY_TIMEOUT_US); | |
| 200 | + if (ret) { | |
| 201 | + dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val); | |
| 202 | + ipu6_buttress_ipc_validity_close(isp, ipc); | |
| 203 | + } | |
| 204 | + | |
| 205 | + return ret; | |
| 206 | +} | |
| 207 | + | |
| 208 | +static void ipu6_buttress_ipc_recv(struct ipu6_device *isp, | |
| 209 | + struct ipu6_buttress_ipc *ipc, u32 *ipc_msg) | |
| 210 | +{ | |
| 211 | + if (ipc_msg) | |
| 212 | + *ipc_msg = readl(isp->base + ipc->data0_in); | |
| 213 | + writel(0, isp->base + ipc->db0_in); | |
| 214 | +} | |
| 215 | + | |
| 216 | +static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp, | |
| 217 | + enum ipu6_buttress_ipc_domain ipc_domain, | |
| 218 | + struct ipu6_ipc_buttress_bulk_msg *msgs, | |
| 219 | + u32 size) | |
| 220 | +{ | |
| 221 | + unsigned long tx_timeout_jiffies, rx_timeout_jiffies; | |
| 222 | + unsigned int i, retry = BUTTRESS_IPC_CMD_SEND_RETRY; | |
| 223 | + struct ipu6_buttress *b = &isp->buttress; | |
| 224 | + struct ipu6_buttress_ipc *ipc; | |
| 225 | + u32 val; | |
| 226 | + int ret; | |
| 227 | + int tout; | |
| 228 | + | |
| 229 | + ipc = ipc_domain == IPU6_BUTTRESS_IPC_CSE ? &b->cse : &b->ish; | |
| 230 | + | |
| 231 | + mutex_lock(&b->ipc_mutex); | |
| 232 | + | |
| 233 | + ret = ipu6_buttress_ipc_validity_open(isp, ipc); | |
| 234 | + if (ret) { | |
| 235 | + dev_err(&isp->pdev->dev, "IPC validity open failed\n"); | |
| 236 | + goto out; | |
| 237 | + } | |
| 238 | + | |
| 239 | + tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS); | |
| 240 | + rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS); | |
| 241 | + | |
| 242 | + for (i = 0; i < size; i++) { | |
| 243 | + reinit_completion(&ipc->send_complete); | |
| 244 | + if (msgs[i].require_resp) | |
| 245 | + reinit_completion(&ipc->recv_complete); | |
| 246 | + | |
| 247 | + dev_dbg(&isp->pdev->dev, "bulk IPC command: 0x%x\n", | |
| 248 | + msgs[i].cmd); | |
| 249 | + writel(msgs[i].cmd, isp->base + ipc->data0_out); | |
| 250 | + val = BUTTRESS_IU2CSEDB0_BUSY | msgs[i].cmd_size; | |
| 251 | + writel(val, isp->base + ipc->db0_out); | |
| 252 | + | |
| 253 | + tout = wait_for_completion_timeout(&ipc->send_complete, | |
| 254 | + tx_timeout_jiffies); | |
| 255 | + if (!tout) { | |
| 256 | + dev_err(&isp->pdev->dev, "send IPC response timeout\n"); | |
| 257 | + if (!retry--) { | |
| 258 | + ret = -ETIMEDOUT; | |
| 259 | + goto out; | |
| 260 | + } | |
| 261 | + | |
| 262 | + /* Try again if CSE is not responding on first try */ | |
| 263 | + writel(0, isp->base + ipc->db0_out); | |
| 264 | + i--; | |
| 265 | + continue; | |
| 266 | + } | |
| 267 | + | |
| 268 | + retry = BUTTRESS_IPC_CMD_SEND_RETRY; | |
| 269 | + | |
| 270 | + if (!msgs[i].require_resp) | |
| 271 | + continue; | |
| 272 | + | |
| 273 | + tout = wait_for_completion_timeout(&ipc->recv_complete, | |
| 274 | + rx_timeout_jiffies); | |
| 275 | + if (!tout) { | |
| 276 | + dev_err(&isp->pdev->dev, "recv IPC response timeout\n"); | |
| 277 | + ret = -ETIMEDOUT; | |
| 278 | + goto out; | |
| 279 | + } | |
| 280 | + | |
| 281 | + if (ipc->nack_mask && | |
| 282 | + (ipc->recv_data & ipc->nack_mask) == ipc->nack) { | |
| 283 | + dev_err(&isp->pdev->dev, | |
| 284 | + "IPC NACK for cmd 0x%x\n", msgs[i].cmd); | |
| 285 | + ret = -EIO; | |
| 286 | + goto out; | |
| 287 | + } | |
| 288 | + | |
| 289 | + if (ipc->recv_data != msgs[i].expected_resp) { | |
| 290 | + dev_err(&isp->pdev->dev, | |
| 291 | + "expected resp: 0x%x, IPC response: 0x%x ", | |
| 292 | + msgs[i].expected_resp, ipc->recv_data); | |
| 293 | + ret = -EIO; | |
| 294 | + goto out; | |
| 295 | + } | |
| 296 | + } | |
| 297 | + | |
| 298 | + dev_dbg(&isp->pdev->dev, "bulk IPC commands done\n"); | |
| 299 | + | |
| 300 | +out: | |
| 301 | + ipu6_buttress_ipc_validity_close(isp, ipc); | |
| 302 | + mutex_unlock(&b->ipc_mutex); | |
| 303 | + return ret; | |
| 304 | +} | |
| 305 | + | |
| 306 | +static int | |
| 307 | +ipu6_buttress_ipc_send(struct ipu6_device *isp, | |
| 308 | + enum ipu6_buttress_ipc_domain ipc_domain, | |
| 309 | + u32 ipc_msg, u32 size, bool require_resp, | |
| 310 | + u32 expected_resp) | |
| 311 | +{ | |
| 312 | + struct ipu6_ipc_buttress_bulk_msg msg = { | |
| 313 | + .cmd = ipc_msg, | |
| 314 | + .cmd_size = size, | |
| 315 | + .require_resp = require_resp, | |
| 316 | + .expected_resp = expected_resp, | |
| 317 | + }; | |
| 318 | + | |
| 319 | + return ipu6_buttress_ipc_send_bulk(isp, ipc_domain, &msg, 1); | |
| 320 | +} | |
| 321 | + | |
| 322 | +static irqreturn_t ipu6_buttress_call_isr(struct ipu6_bus_device *adev) | |
| 323 | +{ | |
| 324 | + irqreturn_t ret = IRQ_WAKE_THREAD; | |
| 325 | + | |
| 326 | + if (!adev || !adev->auxdrv || !adev->auxdrv_data) | |
| 327 | + return IRQ_NONE; | |
| 328 | + | |
| 329 | + if (adev->auxdrv_data->isr) | |
| 330 | + ret = adev->auxdrv_data->isr(adev); | |
| 331 | + | |
| 332 | + if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded) | |
| 333 | + ret = IRQ_NONE; | |
| 334 | + | |
| 335 | + return ret; | |
| 336 | +} | |
| 337 | + | |
| 338 | +irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr) | |
| 339 | +{ | |
| 340 | + struct ipu6_device *isp = isp_ptr; | |
| 341 | + struct ipu6_bus_device *adev[] = { isp->isys, isp->psys }; | |
| 342 | + struct ipu6_buttress *b = &isp->buttress; | |
| 343 | + u32 reg_irq_sts = BUTTRESS_REG_ISR_STATUS; | |
| 344 | + irqreturn_t ret = IRQ_NONE; | |
| 345 | + u32 disable_irqs = 0; | |
| 346 | + u32 irq_status; | |
| 347 | + u32 i, count = 0; | |
| 348 | + | |
| 349 | + pm_runtime_get_noresume(&isp->pdev->dev); | |
| 350 | + | |
| 351 | + irq_status = readl(isp->base + reg_irq_sts); | |
| 352 | + if (!irq_status) { | |
| 353 | + pm_runtime_put_noidle(&isp->pdev->dev); | |
| 354 | + return IRQ_NONE; | |
| 355 | + } | |
| 356 | + | |
| 357 | + do { | |
| 358 | + writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR); | |
| 359 | + | |
| 360 | + for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask); i++) { | |
| 361 | + irqreturn_t r = ipu6_buttress_call_isr(adev[i]); | |
| 362 | + | |
| 363 | + if (!(irq_status & ipu6_adev_irq_mask[i])) | |
| 364 | + continue; | |
| 365 | + | |
| 366 | + if (r == IRQ_WAKE_THREAD) { | |
| 367 | + ret = IRQ_WAKE_THREAD; | |
| 368 | + disable_irqs |= ipu6_adev_irq_mask[i]; | |
| 369 | + } else if (ret == IRQ_NONE && r == IRQ_HANDLED) { | |
| 370 | + ret = IRQ_HANDLED; | |
| 371 | + } | |
| 372 | + } | |
| 373 | + | |
| 374 | + if ((irq_status & BUTTRESS_EVENT) && ret == IRQ_NONE) | |
| 375 | + ret = IRQ_HANDLED; | |
| 376 | + | |
| 377 | + if (irq_status & BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING) { | |
| 378 | + dev_dbg(&isp->pdev->dev, | |
| 379 | + "BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING\n"); | |
| 380 | + ipu6_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data); | |
| 381 | + complete(&b->cse.recv_complete); | |
| 382 | + } | |
| 383 | + | |
| 384 | + if (irq_status & BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING) { | |
| 385 | + dev_dbg(&isp->pdev->dev, | |
| 386 | + "BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING\n"); | |
| 387 | + ipu6_buttress_ipc_recv(isp, &b->ish, &b->ish.recv_data); | |
| 388 | + complete(&b->ish.recv_complete); | |
| 389 | + } | |
| 390 | + | |
| 391 | + if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE) { | |
| 392 | + dev_dbg(&isp->pdev->dev, | |
| 393 | + "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n"); | |
| 394 | + complete(&b->cse.send_complete); | |
| 395 | + } | |
| 396 | + | |
| 397 | + if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH) { | |
| 398 | + dev_dbg(&isp->pdev->dev, | |
| 399 | + "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n"); | |
| 400 | + complete(&b->ish.send_complete); | |
| 401 | + } | |
| 402 | + | |
| 403 | + if (irq_status & BUTTRESS_ISR_SAI_VIOLATION && | |
| 404 | + ipu6_buttress_get_secure_mode(isp)) | |
| 405 | + dev_err(&isp->pdev->dev, | |
| 406 | + "BUTTRESS_ISR_SAI_VIOLATION\n"); | |
| 407 | + | |
| 408 | + if (irq_status & (BUTTRESS_ISR_IS_FATAL_MEM_ERR | | |
| 409 | + BUTTRESS_ISR_PS_FATAL_MEM_ERR)) | |
| 410 | + dev_err(&isp->pdev->dev, | |
| 411 | + "BUTTRESS_ISR_FATAL_MEM_ERR\n"); | |
| 412 | + | |
| 413 | + if (irq_status & BUTTRESS_ISR_UFI_ERROR) | |
| 414 | + dev_err(&isp->pdev->dev, "BUTTRESS_ISR_UFI_ERROR\n"); | |
| 415 | + | |
| 416 | + if (++count == BUTTRESS_MAX_CONSECUTIVE_IRQS) { | |
| 417 | + dev_err(&isp->pdev->dev, "too many consecutive IRQs\n"); | |
| 418 | + ret = IRQ_NONE; | |
| 419 | + break; | |
| 420 | + } | |
| 421 | + | |
| 422 | + irq_status = readl(isp->base + reg_irq_sts); | |
| 423 | + } while (irq_status); | |
| 424 | + | |
| 425 | + if (disable_irqs) | |
| 426 | + writel(BUTTRESS_IRQS & ~disable_irqs, | |
| 427 | + isp->base + BUTTRESS_REG_ISR_ENABLE); | |
| 428 | + | |
| 429 | + pm_runtime_put(&isp->pdev->dev); | |
| 430 | + | |
| 431 | + return ret; | |
| 432 | +} | |
| 433 | + | |
| 434 | +irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr) | |
| 435 | +{ | |
| 436 | + struct ipu6_device *isp = isp_ptr; | |
| 437 | + struct ipu6_bus_device *adev[] = { isp->isys, isp->psys }; | |
| 438 | + const struct ipu6_auxdrv_data *drv_data = NULL; | |
| 439 | + irqreturn_t ret = IRQ_NONE; | |
| 440 | + unsigned int i; | |
| 441 | + | |
| 442 | + for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask) && adev[i]; i++) { | |
| 443 | + drv_data = adev[i]->auxdrv_data; | |
| 444 | + if (!drv_data) | |
| 445 | + continue; | |
| 446 | + | |
| 447 | + if (drv_data->wake_isr_thread && | |
| 448 | + drv_data->isr_threaded(adev[i]) == IRQ_HANDLED) | |
| 449 | + ret = IRQ_HANDLED; | |
| 450 | + } | |
| 451 | + | |
| 452 | + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); | |
| 453 | + | |
| 454 | + return ret; | |
| 455 | +} | |
| 456 | + | |
| 457 | +int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl, | |
| 458 | + bool on) | |
| 459 | +{ | |
| 460 | + struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp; | |
| 461 | + u32 pwr_sts, val; | |
| 462 | + int ret; | |
| 463 | + | |
| 464 | + if (!ctrl) | |
| 465 | + return 0; | |
| 466 | + | |
| 467 | + mutex_lock(&isp->buttress.power_mutex); | |
| 468 | + | |
| 469 | + if (!on) { | |
| 470 | + val = 0; | |
| 471 | + pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; | |
| 472 | + } else { | |
| 473 | + val = BUTTRESS_FREQ_CTL_START | | |
| 474 | + FIELD_PREP(BUTTRESS_FREQ_CTL_RATIO_MASK, | |
| 475 | + ctrl->ratio) | | |
| 476 | + FIELD_PREP(BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK, | |
| 477 | + ctrl->qos_floor) | | |
| 478 | + BUTTRESS_FREQ_CTL_ICCMAX_LEVEL; | |
| 479 | + | |
| 480 | + pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; | |
| 481 | + } | |
| 482 | + | |
| 483 | + writel(val, isp->base + ctrl->freq_ctl); | |
| 484 | + | |
| 485 | + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, | |
| 486 | + val, (val & ctrl->pwr_sts_mask) == pwr_sts, | |
| 487 | + 100, BUTTRESS_POWER_TIMEOUT_US); | |
| 488 | + if (ret) | |
| 489 | + dev_err(&isp->pdev->dev, | |
| 490 | + "Change power status timeout with 0x%x\n", val); | |
| 491 | + | |
| 492 | + ctrl->started = !ret && on; | |
| 493 | + | |
| 494 | + mutex_unlock(&isp->buttress.power_mutex); | |
| 495 | + | |
| 496 | + return ret; | |
| 497 | +} | |
| 498 | + | |
| 499 | +bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp) | |
| 500 | +{ | |
| 501 | + u32 val; | |
| 502 | + | |
| 503 | + val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); | |
| 504 | + | |
| 505 | + return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE; | |
| 506 | +} | |
| 507 | + | |
| 508 | +bool ipu6_buttress_auth_done(struct ipu6_device *isp) | |
| 509 | +{ | |
| 510 | + u32 val; | |
| 511 | + | |
| 512 | + if (!isp->secure_mode) | |
| 513 | + return true; | |
| 514 | + | |
| 515 | + val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); | |
| 516 | + val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val); | |
| 517 | + | |
| 518 | + return val == BUTTRESS_SECURITY_CTL_AUTH_DONE; | |
| 519 | +} | |
| 520 | +EXPORT_SYMBOL_NS_GPL(ipu6_buttress_auth_done, INTEL_IPU6); | |
| 521 | + | |
| 522 | +int ipu6_buttress_reset_authentication(struct ipu6_device *isp) | |
| 523 | +{ | |
| 524 | + int ret; | |
| 525 | + u32 val; | |
| 526 | + | |
| 527 | + if (!isp->secure_mode) { | |
| 528 | + dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n"); | |
| 529 | + return 0; | |
| 530 | + } | |
| 531 | + | |
| 532 | + writel(BUTTRESS_FW_RESET_CTL_START, isp->base + | |
| 533 | + BUTTRESS_REG_FW_RESET_CTL); | |
| 534 | + | |
| 535 | + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val, | |
| 536 | + val & BUTTRESS_FW_RESET_CTL_DONE, 500, | |
| 537 | + BUTTRESS_CSE_FWRESET_TIMEOUT_US); | |
| 538 | + if (ret) { | |
| 539 | + dev_err(&isp->pdev->dev, | |
| 540 | + "Time out while resetting authentication state\n"); | |
| 541 | + return ret; | |
| 542 | + } | |
| 543 | + | |
| 544 | + dev_dbg(&isp->pdev->dev, "FW reset for authentication done\n"); | |
| 545 | + writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL); | |
| 546 | + /* leave some time for HW restore */ | |
| 547 | + usleep_range(800, 1000); | |
| 548 | + | |
| 549 | + return 0; | |
| 550 | +} | |
| 551 | + | |
| 552 | +int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys, | |
| 553 | + const struct firmware *fw, struct sg_table *sgt) | |
| 554 | +{ | |
| 555 | + bool is_vmalloc = is_vmalloc_addr(fw->data); | |
| 556 | + struct page **pages; | |
| 557 | + const void *addr; | |
| 558 | + unsigned long n_pages; | |
| 559 | + unsigned int i; | |
| 560 | + int ret; | |
| 561 | + | |
| 562 | + if (!is_vmalloc && !virt_addr_valid(fw->data)) | |
| 563 | + return -EDOM; | |
| 564 | + | |
| 565 | + n_pages = PHYS_PFN(PAGE_ALIGN(fw->size)); | |
| 566 | + | |
| 567 | + pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL); | |
| 568 | + if (!pages) | |
| 569 | + return -ENOMEM; | |
| 570 | + | |
| 571 | + addr = fw->data; | |
| 572 | + for (i = 0; i < n_pages; i++) { | |
| 573 | + struct page *p = is_vmalloc ? | |
| 574 | + vmalloc_to_page(addr) : virt_to_page(addr); | |
| 575 | + | |
| 576 | + if (!p) { | |
| 577 | + ret = -ENOMEM; | |
| 578 | + goto out; | |
| 579 | + } | |
| 580 | + pages[i] = p; | |
| 581 | + addr += PAGE_SIZE; | |
| 582 | + } | |
| 583 | + | |
| 584 | + ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, fw->size, | |
| 585 | + GFP_KERNEL); | |
| 586 | + if (ret) { | |
| 587 | + ret = -ENOMEM; | |
| 588 | + goto out; | |
| 589 | + } | |
| 590 | + | |
| 591 | + ret = dma_map_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0); | |
| 592 | + if (ret < 0) { | |
| 593 | + ret = -ENOMEM; | |
| 594 | + sg_free_table(sgt); | |
| 595 | + goto out; | |
| 596 | + } | |
| 597 | + | |
| 598 | + dma_sync_sgtable_for_device(&sys->auxdev.dev, sgt, DMA_TO_DEVICE); | |
| 599 | + | |
| 600 | +out: | |
| 601 | + kfree(pages); | |
| 602 | + | |
| 603 | + return ret; | |
| 604 | +} | |
| 605 | +EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, INTEL_IPU6); | |
| 606 | + | |
| 607 | +void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys, | |
| 608 | + struct sg_table *sgt) | |
| 609 | +{ | |
| 610 | + dma_unmap_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0); | |
| 611 | + sg_free_table(sgt); | |
| 612 | +} | |
| 613 | +EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, INTEL_IPU6); | |
| 614 | + | |
| 615 | +int ipu6_buttress_authenticate(struct ipu6_device *isp) | |
| 616 | +{ | |
| 617 | + struct ipu6_buttress *b = &isp->buttress; | |
| 618 | + struct ipu6_psys_pdata *psys_pdata; | |
| 619 | + u32 data, mask, done, fail; | |
| 620 | + int ret; | |
| 621 | + | |
| 622 | + if (!isp->secure_mode) { | |
| 623 | + dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n"); | |
| 624 | + return 0; | |
| 625 | + } | |
| 626 | + | |
| 627 | + psys_pdata = isp->psys->pdata; | |
| 628 | + | |
| 629 | + mutex_lock(&b->auth_mutex); | |
| 630 | + | |
| 631 | + if (ipu6_buttress_auth_done(isp)) { | |
| 632 | + ret = 0; | |
| 633 | + goto out_unlock; | |
| 634 | + } | |
| 635 | + | |
| 636 | + /* | |
| 637 | + * Write address of FIT table to FW_SOURCE register | |
| 638 | + * Let's use fw address. I.e. not using FIT table yet | |
| 639 | + */ | |
| 640 | + data = lower_32_bits(isp->psys->pkg_dir_dma_addr); | |
| 641 | + writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO); | |
| 642 | + | |
| 643 | + data = upper_32_bits(isp->psys->pkg_dir_dma_addr); | |
| 644 | + writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI); | |
| 645 | + | |
| 646 | + /* | |
| 647 | + * Write boot_load into IU2CSEDATA0 | |
| 648 | + * Write sizeof(boot_load) | 0x2 << CLIENT_ID to | |
| 649 | + * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as | |
| 650 | + */ | |
| 651 | + dev_info(&isp->pdev->dev, "Sending BOOT_LOAD to CSE\n"); | |
| 652 | + | |
| 653 | + ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE, | |
| 654 | + BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD, | |
| 655 | + 1, true, | |
| 656 | + BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE); | |
| 657 | + if (ret) { | |
| 658 | + dev_err(&isp->pdev->dev, "CSE boot_load failed\n"); | |
| 659 | + goto out_unlock; | |
| 660 | + } | |
| 661 | + | |
| 662 | + mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK; | |
| 663 | + done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE; | |
| 664 | + fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED; | |
| 665 | + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, | |
| 666 | + ((data & mask) == done || | |
| 667 | + (data & mask) == fail), 500, | |
| 668 | + BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); | |
| 669 | + if (ret) { | |
| 670 | + dev_err(&isp->pdev->dev, "CSE boot_load timeout\n"); | |
| 671 | + goto out_unlock; | |
| 672 | + } | |
| 673 | + | |
| 674 | + if ((data & mask) == fail) { | |
| 675 | + dev_err(&isp->pdev->dev, "CSE auth failed\n"); | |
| 676 | + ret = -EINVAL; | |
| 677 | + goto out_unlock; | |
| 678 | + } | |
| 679 | + | |
| 680 | + ret = readl_poll_timeout(psys_pdata->base + BOOTLOADER_STATUS_OFFSET, | |
| 681 | + data, data == BOOTLOADER_MAGIC_KEY, 500, | |
| 682 | + BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); | |
| 683 | + if (ret) { | |
| 684 | + dev_err(&isp->pdev->dev, "Unexpected magic number 0x%x\n", | |
| 685 | + data); | |
| 686 | + goto out_unlock; | |
| 687 | + } | |
| 688 | + | |
| 689 | + /* | |
| 690 | + * Write authenticate_run into IU2CSEDATA0 | |
| 691 | + * Write sizeof(boot_load) | 0x2 << CLIENT_ID to | |
| 692 | + * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as | |
| 693 | + */ | |
| 694 | + dev_info(&isp->pdev->dev, "Sending AUTHENTICATE_RUN to CSE\n"); | |
| 695 | + ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE, | |
| 696 | + BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN, | |
| 697 | + 1, true, | |
| 698 | + BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE); | |
| 699 | + if (ret) { | |
| 700 | + dev_err(&isp->pdev->dev, "CSE authenticate_run failed\n"); | |
| 701 | + goto out_unlock; | |
| 702 | + } | |
| 703 | + | |
| 704 | + done = BUTTRESS_SECURITY_CTL_AUTH_DONE; | |
| 705 | + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, | |
| 706 | + ((data & mask) == done || | |
| 707 | + (data & mask) == fail), 500, | |
| 708 | + BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US); | |
| 709 | + if (ret) { | |
| 710 | + dev_err(&isp->pdev->dev, "CSE authenticate timeout\n"); | |
| 711 | + goto out_unlock; | |
| 712 | + } | |
| 713 | + | |
| 714 | + if ((data & mask) == fail) { | |
| 715 | + dev_err(&isp->pdev->dev, "CSE boot_load failed\n"); | |
| 716 | + ret = -EINVAL; | |
| 717 | + goto out_unlock; | |
| 718 | + } | |
| 719 | + | |
| 720 | + dev_info(&isp->pdev->dev, "CSE authenticate_run done\n"); | |
| 721 | + | |
| 722 | +out_unlock: | |
| 723 | + mutex_unlock(&b->auth_mutex); | |
| 724 | + | |
| 725 | + return ret; | |
| 726 | +} | |
| 727 | + | |
| 728 | +static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp) | |
| 729 | +{ | |
| 730 | + u32 val, mask, done; | |
| 731 | + int ret; | |
| 732 | + | |
| 733 | + mask = BUTTRESS_PWR_STATE_HH_STATUS_MASK; | |
| 734 | + | |
| 735 | + writel(BUTTRESS_FABRIC_CMD_START_TSC_SYNC, | |
| 736 | + isp->base + BUTTRESS_REG_FABRIC_CMD); | |
| 737 | + | |
| 738 | + val = readl(isp->base + BUTTRESS_REG_PWR_STATE); | |
| 739 | + val = FIELD_GET(mask, val); | |
| 740 | + if (val == BUTTRESS_PWR_STATE_HH_STATE_ERR) { | |
| 741 | + dev_err(&isp->pdev->dev, "Start tsc sync failed\n"); | |
| 742 | + return -EINVAL; | |
| 743 | + } | |
| 744 | + | |
| 745 | + done = BUTTRESS_PWR_STATE_HH_STATE_DONE; | |
| 746 | + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, val, | |
| 747 | + FIELD_GET(mask, val) == done, 500, | |
| 748 | + BUTTRESS_TSC_SYNC_TIMEOUT_US); | |
| 749 | + if (ret) | |
| 750 | + dev_err(&isp->pdev->dev, "Start tsc sync timeout\n"); | |
| 751 | + | |
| 752 | + return ret; | |
| 753 | +} | |
| 754 | + | |
| 755 | +int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp) | |
| 756 | +{ | |
| 757 | + unsigned int i; | |
| 758 | + | |
| 759 | + for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { | |
| 760 | + u32 val; | |
| 761 | + int ret; | |
| 762 | + | |
| 763 | + ret = ipu6_buttress_send_tsc_request(isp); | |
| 764 | + if (ret != -ETIMEDOUT) | |
| 765 | + return ret; | |
| 766 | + | |
| 767 | + val = readl(isp->base + BUTTRESS_REG_TSW_CTL); | |
| 768 | + val = val | BUTTRESS_TSW_CTL_SOFT_RESET; | |
| 769 | + writel(val, isp->base + BUTTRESS_REG_TSW_CTL); | |
| 770 | + val = val & ~BUTTRESS_TSW_CTL_SOFT_RESET; | |
| 771 | + writel(val, isp->base + BUTTRESS_REG_TSW_CTL); | |
| 772 | + } | |
| 773 | + | |
| 774 | + dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n"); | |
| 775 | + | |
| 776 | + return -ETIMEDOUT; | |
| 777 | +} | |
| 778 | +EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, INTEL_IPU6); | |
| 779 | + | |
| 780 | +void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val) | |
| 781 | +{ | |
| 782 | + u32 tsc_hi_1, tsc_hi_2, tsc_lo; | |
| 783 | + unsigned long flags; | |
| 784 | + | |
| 785 | + local_irq_save(flags); | |
| 786 | + tsc_hi_1 = readl(isp->base + BUTTRESS_REG_TSC_HI); | |
| 787 | + tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO); | |
| 788 | + tsc_hi_2 = readl(isp->base + BUTTRESS_REG_TSC_HI); | |
| 789 | + if (tsc_hi_1 == tsc_hi_2) { | |
| 790 | + *val = (u64)tsc_hi_1 << 32 | tsc_lo; | |
| 791 | + } else { | |
| 792 | + /* Check if TSC has rolled over */ | |
| 793 | + if (tsc_lo & BIT(31)) | |
| 794 | + *val = (u64)tsc_hi_1 << 32 | tsc_lo; | |
| 795 | + else | |
| 796 | + *val = (u64)tsc_hi_2 << 32 | tsc_lo; | |
| 797 | + } | |
| 798 | + local_irq_restore(flags); | |
| 799 | +} | |
| 800 | +EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_read, INTEL_IPU6); | |
| 801 | + | |
| 802 | +u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp) | |
| 803 | +{ | |
| 804 | + u64 ns = ticks * 10000; | |
| 805 | + | |
| 806 | + /* | |
| 807 | + * converting TSC tick count to ns is calculated by: | |
| 808 | + * Example (TSC clock frequency is 19.2MHz): | |
| 809 | + * ns = ticks * 1000 000 000 / 19.2Mhz | |
| 810 | + * = ticks * 1000 000 000 / 19200000Hz | |
| 811 | + * = ticks * 10000 / 192 ns | |
| 812 | + */ | |
| 813 | + return div_u64(ns, isp->buttress.ref_clk); | |
| 814 | +} | |
| 815 | +EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, INTEL_IPU6); | |
| 816 | + | |
| 817 | +void ipu6_buttress_restore(struct ipu6_device *isp) | |
| 818 | +{ | |
| 819 | + struct ipu6_buttress *b = &isp->buttress; | |
| 820 | + | |
| 821 | + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR); | |
| 822 | + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); | |
| 823 | + writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_WDT); | |
| 824 | +} | |
| 825 | + | |
| 826 | +int ipu6_buttress_init(struct ipu6_device *isp) | |
| 827 | +{ | |
| 828 | + int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY; | |
| 829 | + struct ipu6_buttress *b = &isp->buttress; | |
| 830 | + u32 val; | |
| 831 | + | |
| 832 | + mutex_init(&b->power_mutex); | |
| 833 | + mutex_init(&b->auth_mutex); | |
| 834 | + mutex_init(&b->cons_mutex); | |
| 835 | + mutex_init(&b->ipc_mutex); | |
| 836 | + init_completion(&b->ish.send_complete); | |
| 837 | + init_completion(&b->cse.send_complete); | |
| 838 | + init_completion(&b->ish.recv_complete); | |
| 839 | + init_completion(&b->cse.recv_complete); | |
| 840 | + | |
| 841 | + b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK; | |
| 842 | + b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK; | |
| 843 | + b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR; | |
| 844 | + b->cse.csr_out = BUTTRESS_REG_IU2CSECSR; | |
| 845 | + b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0; | |
| 846 | + b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0; | |
| 847 | + b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0; | |
| 848 | + b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0; | |
| 849 | + | |
| 850 | + /* no ISH on IPU6 */ | |
| 851 | + memset(&b->ish, 0, sizeof(b->ish)); | |
| 852 | + INIT_LIST_HEAD(&b->constraints); | |
| 853 | + | |
| 854 | + isp->secure_mode = ipu6_buttress_get_secure_mode(isp); | |
| 855 | + dev_info(&isp->pdev->dev, "IPU6 in %s mode touch 0x%x mask 0x%x\n", | |
| 856 | + isp->secure_mode ? "secure" : "non-secure", | |
| 857 | + readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH), | |
| 858 | + readl(isp->base + BUTTRESS_REG_CAMERA_MASK)); | |
| 859 | + | |
| 860 | + b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_WDT); | |
| 861 | + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR); | |
| 862 | + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); | |
| 863 | + | |
| 864 | + /* get ref_clk frequency by reading the indication in btrs control */ | |
| 865 | + val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL); | |
| 866 | + val = FIELD_GET(BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND, val); | |
| 867 | + | |
| 868 | + switch (val) { | |
| 869 | + case 0x0: | |
| 870 | + b->ref_clk = 240; | |
| 871 | + break; | |
| 872 | + case 0x1: | |
| 873 | + b->ref_clk = 192; | |
| 874 | + break; | |
| 875 | + case 0x2: | |
| 876 | + b->ref_clk = 384; | |
| 877 | + break; | |
| 878 | + default: | |
| 879 | + dev_warn(&isp->pdev->dev, | |
| 880 | + "Unsupported ref clock, use 19.2Mhz by default.\n"); | |
| 881 | + b->ref_clk = 192; | |
| 882 | + break; | |
| 883 | + } | |
| 884 | + | |
| 885 | + /* Retry couple of times in case of CSE initialization is delayed */ | |
| 886 | + do { | |
| 887 | + ret = ipu6_buttress_ipc_reset(isp, &b->cse); | |
| 888 | + if (ret) { | |
| 889 | + dev_warn(&isp->pdev->dev, | |
| 890 | + "IPC reset protocol failed, retrying\n"); | |
| 891 | + } else { | |
| 892 | + dev_dbg(&isp->pdev->dev, "IPC reset done\n"); | |
| 893 | + return 0; | |
| 894 | + } | |
| 895 | + } while (ipc_reset_retry--); | |
| 896 | + | |
| 897 | + dev_err(&isp->pdev->dev, "IPC reset protocol failed\n"); | |
| 898 | + | |
| 899 | + mutex_destroy(&b->power_mutex); | |
| 900 | + mutex_destroy(&b->auth_mutex); | |
| 901 | + mutex_destroy(&b->cons_mutex); | |
| 902 | + mutex_destroy(&b->ipc_mutex); | |
| 903 | + | |
| 904 | + return ret; | |
| 905 | +} | |
| 906 | + | |
| 907 | +void ipu6_buttress_exit(struct ipu6_device *isp) | |
| 908 | +{ | |
| 909 | + struct ipu6_buttress *b = &isp->buttress; | |
| 910 | + | |
| 911 | + writel(0, isp->base + BUTTRESS_REG_ISR_ENABLE); | |
| 912 | + | |
| 913 | + mutex_destroy(&b->power_mutex); | |
| 914 | + mutex_destroy(&b->auth_mutex); | |
| 915 | + mutex_destroy(&b->cons_mutex); | |
| 916 | + mutex_destroy(&b->ipc_mutex); | |
| 917 | +} | |
| @@ -0,0 +1,92 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2013--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_BUTTRESS_H | |
| 5 | +#define IPU6_BUTTRESS_H | |
| 6 | + | |
| 7 | +#include <linux/completion.h> | |
| 8 | +#include <linux/irqreturn.h> | |
| 9 | +#include <linux/list.h> | |
| 10 | +#include <linux/mutex.h> | |
| 11 | + | |
| 12 | +struct device; | |
| 13 | +struct firmware; | |
| 14 | +struct ipu6_device; | |
| 15 | +struct ipu6_bus_device; | |
| 16 | + | |
| 17 | +#define BUTTRESS_PS_FREQ_STEP 25U | |
| 18 | +#define BUTTRESS_MIN_FORCE_PS_FREQ (BUTTRESS_PS_FREQ_STEP * 8) | |
| 19 | +#define BUTTRESS_MAX_FORCE_PS_FREQ (BUTTRESS_PS_FREQ_STEP * 32) | |
| 20 | + | |
| 21 | +#define BUTTRESS_IS_FREQ_STEP 25U | |
| 22 | +#define BUTTRESS_MIN_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 8) | |
| 23 | +#define BUTTRESS_MAX_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 22) | |
| 24 | + | |
| 25 | +struct ipu6_buttress_ctrl { | |
| 26 | + u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off; | |
| 27 | + unsigned int ratio; | |
| 28 | + unsigned int qos_floor; | |
| 29 | + bool started; | |
| 30 | +}; | |
| 31 | + | |
| 32 | +struct ipu6_buttress_ipc { | |
| 33 | + struct completion send_complete; | |
| 34 | + struct completion recv_complete; | |
| 35 | + u32 nack; | |
| 36 | + u32 nack_mask; | |
| 37 | + u32 recv_data; | |
| 38 | + u32 csr_out; | |
| 39 | + u32 csr_in; | |
| 40 | + u32 db0_in; | |
| 41 | + u32 db0_out; | |
| 42 | + u32 data0_out; | |
| 43 | + u32 data0_in; | |
| 44 | +}; | |
| 45 | + | |
| 46 | +struct ipu6_buttress { | |
| 47 | + struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex; | |
| 48 | + struct ipu6_buttress_ipc cse; | |
| 49 | + struct ipu6_buttress_ipc ish; | |
| 50 | + struct list_head constraints; | |
| 51 | + u32 wdt_cached_value; | |
| 52 | + bool force_suspend; | |
| 53 | + u32 ref_clk; | |
| 54 | +}; | |
| 55 | + | |
| 56 | +enum ipu6_buttress_ipc_domain { | |
| 57 | + IPU6_BUTTRESS_IPC_CSE, | |
| 58 | + IPU6_BUTTRESS_IPC_ISH, | |
| 59 | +}; | |
| 60 | + | |
| 61 | +struct ipu6_ipc_buttress_bulk_msg { | |
| 62 | + u32 cmd; | |
| 63 | + u32 expected_resp; | |
| 64 | + bool require_resp; | |
| 65 | + u8 cmd_size; | |
| 66 | +}; | |
| 67 | + | |
| 68 | +int ipu6_buttress_ipc_reset(struct ipu6_device *isp, | |
| 69 | + struct ipu6_buttress_ipc *ipc); | |
| 70 | +int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys, | |
| 71 | + const struct firmware *fw, | |
| 72 | + struct sg_table *sgt); | |
| 73 | +void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys, | |
| 74 | + struct sg_table *sgt); | |
| 75 | +int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl, | |
| 76 | + bool on); | |
| 77 | +bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp); | |
| 78 | +int ipu6_buttress_authenticate(struct ipu6_device *isp); | |
| 79 | +int ipu6_buttress_reset_authentication(struct ipu6_device *isp); | |
| 80 | +bool ipu6_buttress_auth_done(struct ipu6_device *isp); | |
| 81 | +int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp); | |
| 82 | +void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val); | |
| 83 | +u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp); | |
| 84 | + | |
| 85 | +irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr); | |
| 86 | +irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr); | |
| 87 | +int ipu6_buttress_init(struct ipu6_device *isp); | |
| 88 | +void ipu6_buttress_exit(struct ipu6_device *isp); | |
| 89 | +void ipu6_buttress_csi_port_config(struct ipu6_device *isp, | |
| 90 | + u32 legacy, u32 combo); | |
| 91 | +void ipu6_buttress_restore(struct ipu6_device *isp); | |
| 92 | +#endif /* IPU6_BUTTRESS_H */ | |
| @@ -0,0 +1,362 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/bitfield.h> | |
| 7 | +#include <linux/bits.h> | |
| 8 | +#include <linux/err.h> | |
| 9 | +#include <linux/dma-mapping.h> | |
| 10 | +#include <linux/gfp_types.h> | |
| 11 | +#include <linux/math64.h> | |
| 12 | +#include <linux/sizes.h> | |
| 13 | +#include <linux/types.h> | |
| 14 | + | |
| 15 | +#include "ipu6.h" | |
| 16 | +#include "ipu6-bus.h" | |
| 17 | +#include "ipu6-cpd.h" | |
| 18 | + | |
| 19 | +/* 15 entries + header*/ | |
| 20 | +#define MAX_PKG_DIR_ENT_CNT 16 | |
| 21 | +/* 2 qword per entry/header */ | |
| 22 | +#define PKG_DIR_ENT_LEN 2 | |
| 23 | +/* PKG_DIR size in bytes */ | |
| 24 | +#define PKG_DIR_SIZE ((MAX_PKG_DIR_ENT_CNT) * \ | |
| 25 | + (PKG_DIR_ENT_LEN) * sizeof(u64)) | |
| 26 | +/* _IUPKDR_ */ | |
| 27 | +#define PKG_DIR_HDR_MARK 0x5f4955504b44525fULL | |
| 28 | + | |
| 29 | +/* $CPD */ | |
| 30 | +#define CPD_HDR_MARK 0x44504324 | |
| 31 | + | |
| 32 | +#define MAX_MANIFEST_SIZE (SZ_2K * sizeof(u32)) | |
| 33 | +#define MAX_METADATA_SIZE SZ_64K | |
| 34 | + | |
| 35 | +#define MAX_COMPONENT_ID 127 | |
| 36 | +#define MAX_COMPONENT_VERSION 0xffff | |
| 37 | + | |
| 38 | +#define MANIFEST_IDX 0 | |
| 39 | +#define METADATA_IDX 1 | |
| 40 | +#define MODULEDATA_IDX 2 | |
| 41 | +/* | |
| 42 | + * PKG_DIR Entry (type == id) | |
| 43 | + * 63:56 55 54:48 47:32 31:24 23:0 | |
| 44 | + * Rsvd Rsvd Type Version Rsvd Size | |
| 45 | + */ | |
| 46 | +#define PKG_DIR_SIZE_MASK GENMASK(23, 0) | |
| 47 | +#define PKG_DIR_VERSION_MASK GENMASK(47, 32) | |
| 48 | +#define PKG_DIR_TYPE_MASK GENMASK(54, 48) | |
| 49 | + | |
| 50 | +static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd, | |
| 51 | + u8 idx) | |
| 52 | +{ | |
| 53 | + const struct ipu6_cpd_hdr *cpd_hdr = cpd; | |
| 54 | + const struct ipu6_cpd_ent *ent; | |
| 55 | + | |
| 56 | + ent = (const struct ipu6_cpd_ent *)((const u8 *)cpd + cpd_hdr->hdr_len); | |
| 57 | + return ent + idx; | |
| 58 | +} | |
| 59 | + | |
| 60 | +#define ipu6_cpd_get_manifest(cpd) ipu6_cpd_get_entry(cpd, MANIFEST_IDX) | |
| 61 | +#define ipu6_cpd_get_metadata(cpd) ipu6_cpd_get_entry(cpd, METADATA_IDX) | |
| 62 | +#define ipu6_cpd_get_moduledata(cpd) ipu6_cpd_get_entry(cpd, MODULEDATA_IDX) | |
| 63 | + | |
| 64 | +static const struct ipu6_cpd_metadata_cmpnt_hdr * | |
| 65 | +ipu6_cpd_metadata_get_cmpnt(struct ipu6_device *isp, const void *metadata, | |
| 66 | + unsigned int metadata_size, u8 idx) | |
| 67 | +{ | |
| 68 | + size_t extn_size = sizeof(struct ipu6_cpd_metadata_extn); | |
| 69 | + size_t cmpnt_count = metadata_size - extn_size; | |
| 70 | + | |
| 71 | + cmpnt_count = div_u64(cmpnt_count, isp->cpd_metadata_cmpnt_size); | |
| 72 | + | |
| 73 | + if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) { | |
| 74 | + dev_err(&isp->pdev->dev, "Component index out of range (%d)\n", | |
| 75 | + idx); | |
| 76 | + return ERR_PTR(-EINVAL); | |
| 77 | + } | |
| 78 | + | |
| 79 | + return metadata + extn_size + idx * isp->cpd_metadata_cmpnt_size; | |
| 80 | +} | |
| 81 | + | |
| 82 | +static u32 ipu6_cpd_metadata_cmpnt_version(struct ipu6_device *isp, | |
| 83 | + const void *metadata, | |
| 84 | + unsigned int metadata_size, u8 idx) | |
| 85 | +{ | |
| 86 | + const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt; | |
| 87 | + | |
| 88 | + cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx); | |
| 89 | + if (IS_ERR(cmpnt)) | |
| 90 | + return PTR_ERR(cmpnt); | |
| 91 | + | |
| 92 | + return cmpnt->ver; | |
| 93 | +} | |
| 94 | + | |
| 95 | +static int ipu6_cpd_metadata_get_cmpnt_id(struct ipu6_device *isp, | |
| 96 | + const void *metadata, | |
| 97 | + unsigned int metadata_size, u8 idx) | |
| 98 | +{ | |
| 99 | + const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt; | |
| 100 | + | |
| 101 | + cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx); | |
| 102 | + if (IS_ERR(cmpnt)) | |
| 103 | + return PTR_ERR(cmpnt); | |
| 104 | + | |
| 105 | + return cmpnt->id; | |
| 106 | +} | |
| 107 | + | |
| 108 | +static int ipu6_cpd_parse_module_data(struct ipu6_device *isp, | |
| 109 | + const void *module_data, | |
| 110 | + unsigned int module_data_size, | |
| 111 | + dma_addr_t dma_addr_module_data, | |
| 112 | + u64 *pkg_dir, const void *metadata, | |
| 113 | + unsigned int metadata_size) | |
| 114 | +{ | |
| 115 | + const struct ipu6_cpd_module_data_hdr *module_data_hdr; | |
| 116 | + const struct ipu6_cpd_hdr *dir_hdr; | |
| 117 | + const struct ipu6_cpd_ent *dir_ent; | |
| 118 | + unsigned int i; | |
| 119 | + u8 len; | |
| 120 | + | |
| 121 | + if (!module_data) | |
| 122 | + return -EINVAL; | |
| 123 | + | |
| 124 | + module_data_hdr = module_data; | |
| 125 | + dir_hdr = module_data + module_data_hdr->hdr_len; | |
| 126 | + len = dir_hdr->hdr_len; | |
| 127 | + dir_ent = (const struct ipu6_cpd_ent *)(((u8 *)dir_hdr) + len); | |
| 128 | + | |
| 129 | + pkg_dir[0] = PKG_DIR_HDR_MARK; | |
| 130 | + /* pkg_dir entry count = component count + pkg_dir header */ | |
| 131 | + pkg_dir[1] = dir_hdr->ent_cnt + 1; | |
| 132 | + | |
| 133 | + for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) { | |
| 134 | + u64 *p = &pkg_dir[PKG_DIR_ENT_LEN * (1 + i)]; | |
| 135 | + int ver, id; | |
| 136 | + | |
| 137 | + *p++ = dma_addr_module_data + dir_ent->offset; | |
| 138 | + id = ipu6_cpd_metadata_get_cmpnt_id(isp, metadata, | |
| 139 | + metadata_size, i); | |
| 140 | + if (id < 0 || id > MAX_COMPONENT_ID) { | |
| 141 | + dev_err(&isp->pdev->dev, "Invalid CPD component id\n"); | |
| 142 | + return -EINVAL; | |
| 143 | + } | |
| 144 | + | |
| 145 | + ver = ipu6_cpd_metadata_cmpnt_version(isp, metadata, | |
| 146 | + metadata_size, i); | |
| 147 | + if (ver < 0 || ver > MAX_COMPONENT_VERSION) { | |
| 148 | + dev_err(&isp->pdev->dev, | |
| 149 | + "Invalid CPD component version\n"); | |
| 150 | + return -EINVAL; | |
| 151 | + } | |
| 152 | + | |
| 153 | + *p = FIELD_PREP(PKG_DIR_SIZE_MASK, dir_ent->len) | | |
| 154 | + FIELD_PREP(PKG_DIR_TYPE_MASK, id) | | |
| 155 | + FIELD_PREP(PKG_DIR_VERSION_MASK, ver); | |
| 156 | + } | |
| 157 | + | |
| 158 | + return 0; | |
| 159 | +} | |
| 160 | + | |
| 161 | +int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src) | |
| 162 | +{ | |
| 163 | + dma_addr_t dma_addr_src = sg_dma_address(adev->fw_sgt.sgl); | |
| 164 | + const struct ipu6_cpd_ent *ent, *man_ent, *met_ent; | |
| 165 | + struct device *dev = &adev->auxdev.dev; | |
| 166 | + struct ipu6_device *isp = adev->isp; | |
| 167 | + unsigned int man_sz, met_sz; | |
| 168 | + void *pkg_dir_pos; | |
| 169 | + int ret; | |
| 170 | + | |
| 171 | + man_ent = ipu6_cpd_get_manifest(src); | |
| 172 | + man_sz = man_ent->len; | |
| 173 | + | |
| 174 | + met_ent = ipu6_cpd_get_metadata(src); | |
| 175 | + met_sz = met_ent->len; | |
| 176 | + | |
| 177 | + adev->pkg_dir_size = PKG_DIR_SIZE + man_sz + met_sz; | |
| 178 | + adev->pkg_dir = dma_alloc_attrs(dev, adev->pkg_dir_size, | |
| 179 | + &adev->pkg_dir_dma_addr, GFP_KERNEL, 0); | |
| 180 | + if (!adev->pkg_dir) | |
| 181 | + return -ENOMEM; | |
| 182 | + | |
| 183 | + /* | |
| 184 | + * pkg_dir entry/header: | |
| 185 | + * qword | 63:56 | 55 | 54:48 | 47:32 | 31:24 | 23:0 | |
| 186 | + * N Address/Offset/"_IUPKDR_" | |
| 187 | + * N + 1 | rsvd | rsvd | type | ver | rsvd | size | |
| 188 | + * | |
| 189 | + * We can ignore other fields that size in N + 1 qword as they | |
| 190 | + * are 0 anyway. Just setting size for now. | |
| 191 | + */ | |
| 192 | + | |
| 193 | + ent = ipu6_cpd_get_moduledata(src); | |
| 194 | + | |
| 195 | + ret = ipu6_cpd_parse_module_data(isp, src + ent->offset, | |
| 196 | + ent->len, dma_addr_src + ent->offset, | |
| 197 | + adev->pkg_dir, src + met_ent->offset, | |
| 198 | + met_ent->len); | |
| 199 | + if (ret) { | |
| 200 | + dev_err(&isp->pdev->dev, "Failed to parse module data\n"); | |
| 201 | + dma_free_attrs(dev, adev->pkg_dir_size, | |
| 202 | + adev->pkg_dir, adev->pkg_dir_dma_addr, 0); | |
| 203 | + return ret; | |
| 204 | + } | |
| 205 | + | |
| 206 | + /* Copy manifest after pkg_dir */ | |
| 207 | + pkg_dir_pos = adev->pkg_dir + PKG_DIR_ENT_LEN * MAX_PKG_DIR_ENT_CNT; | |
| 208 | + memcpy(pkg_dir_pos, src + man_ent->offset, man_sz); | |
| 209 | + | |
| 210 | + /* Copy metadata after manifest */ | |
| 211 | + pkg_dir_pos += man_sz; | |
| 212 | + memcpy(pkg_dir_pos, src + met_ent->offset, met_sz); | |
| 213 | + | |
| 214 | + dma_sync_single_range_for_device(dev, adev->pkg_dir_dma_addr, | |
| 215 | + 0, adev->pkg_dir_size, DMA_TO_DEVICE); | |
| 216 | + | |
| 217 | + return 0; | |
| 218 | +} | |
| 219 | +EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6); | |
| 220 | + | |
| 221 | +void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev) | |
| 222 | +{ | |
| 223 | + dma_free_attrs(&adev->auxdev.dev, adev->pkg_dir_size, adev->pkg_dir, | |
| 224 | + adev->pkg_dir_dma_addr, 0); | |
| 225 | +} | |
| 226 | +EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6); | |
| 227 | + | |
| 228 | +static int ipu6_cpd_validate_cpd(struct ipu6_device *isp, const void *cpd, | |
| 229 | + unsigned long cpd_size, | |
| 230 | + unsigned long data_size) | |
| 231 | +{ | |
| 232 | + const struct ipu6_cpd_hdr *cpd_hdr = cpd; | |
| 233 | + const struct ipu6_cpd_ent *ent; | |
| 234 | + unsigned int i; | |
| 235 | + u8 len; | |
| 236 | + | |
| 237 | + len = cpd_hdr->hdr_len; | |
| 238 | + | |
| 239 | + /* Ensure cpd hdr is within moduledata */ | |
| 240 | + if (cpd_size < len) { | |
| 241 | + dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n"); | |
| 242 | + return -EINVAL; | |
| 243 | + } | |
| 244 | + | |
| 245 | + /* Sanity check for CPD header */ | |
| 246 | + if ((cpd_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) { | |
| 247 | + dev_err(&isp->pdev->dev, "Invalid CPD header\n"); | |
| 248 | + return -EINVAL; | |
| 249 | + } | |
| 250 | + | |
| 251 | + /* Ensure that all entries are within moduledata */ | |
| 252 | + ent = (const struct ipu6_cpd_ent *)(((const u8 *)cpd_hdr) + len); | |
| 253 | + for (i = 0; i < cpd_hdr->ent_cnt; i++, ent++) { | |
| 254 | + if (data_size < ent->offset || | |
| 255 | + data_size - ent->offset < ent->len) { | |
| 256 | + dev_err(&isp->pdev->dev, "Invalid CPD entry (%d)\n", i); | |
| 257 | + return -EINVAL; | |
| 258 | + } | |
| 259 | + } | |
| 260 | + | |
| 261 | + return 0; | |
| 262 | +} | |
| 263 | + | |
| 264 | +static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp, | |
| 265 | + const void *moduledata, | |
| 266 | + u32 moduledata_size) | |
| 267 | +{ | |
| 268 | + const struct ipu6_cpd_module_data_hdr *mod_hdr = moduledata; | |
| 269 | + int ret; | |
| 270 | + | |
| 271 | + /* Ensure moduledata hdr is within moduledata */ | |
| 272 | + if (moduledata_size < sizeof(*mod_hdr) || | |
| 273 | + moduledata_size < mod_hdr->hdr_len) { | |
| 274 | + dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n"); | |
| 275 | + return -EINVAL; | |
| 276 | + } | |
| 277 | + | |
| 278 | + dev_info(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date); | |
| 279 | + ret = ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len, | |
| 280 | + moduledata_size - mod_hdr->hdr_len, | |
| 281 | + moduledata_size); | |
| 282 | + if (ret) { | |
| 283 | + dev_err(&isp->pdev->dev, "Invalid CPD in moduledata\n"); | |
| 284 | + return ret; | |
| 285 | + } | |
| 286 | + | |
| 287 | + return 0; | |
| 288 | +} | |
| 289 | + | |
| 290 | +static int ipu6_cpd_validate_metadata(struct ipu6_device *isp, | |
| 291 | + const void *metadata, u32 meta_size) | |
| 292 | +{ | |
| 293 | + const struct ipu6_cpd_metadata_extn *extn = metadata; | |
| 294 | + | |
| 295 | + /* Sanity check for metadata size */ | |
| 296 | + if (meta_size < sizeof(*extn) || meta_size > MAX_METADATA_SIZE) { | |
| 297 | + dev_err(&isp->pdev->dev, "Invalid CPD metadata\n"); | |
| 298 | + return -EINVAL; | |
| 299 | + } | |
| 300 | + | |
| 301 | + /* Validate extension and image types */ | |
| 302 | + if (extn->extn_type != IPU6_CPD_METADATA_EXTN_TYPE_IUNIT || | |
| 303 | + extn->img_type != IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE) { | |
| 304 | + dev_err(&isp->pdev->dev, | |
| 305 | + "Invalid CPD metadata descriptor img_type (%d)\n", | |
| 306 | + extn->img_type); | |
| 307 | + return -EINVAL; | |
| 308 | + } | |
| 309 | + | |
| 310 | + /* Validate metadata size multiple of metadata components */ | |
| 311 | + if ((meta_size - sizeof(*extn)) % isp->cpd_metadata_cmpnt_size) { | |
| 312 | + dev_err(&isp->pdev->dev, "Invalid CPD metadata size\n"); | |
| 313 | + return -EINVAL; | |
| 314 | + } | |
| 315 | + | |
| 316 | + return 0; | |
| 317 | +} | |
| 318 | + | |
| 319 | +int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file, | |
| 320 | + unsigned long cpd_file_size) | |
| 321 | +{ | |
| 322 | + const struct ipu6_cpd_hdr *hdr = cpd_file; | |
| 323 | + const struct ipu6_cpd_ent *ent; | |
| 324 | + int ret; | |
| 325 | + | |
| 326 | + ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size, | |
| 327 | + cpd_file_size); | |
| 328 | + if (ret) { | |
| 329 | + dev_err(&isp->pdev->dev, "Invalid CPD in file\n"); | |
| 330 | + return ret; | |
| 331 | + } | |
| 332 | + | |
| 333 | + /* Check for CPD file marker */ | |
| 334 | + if (hdr->hdr_mark != CPD_HDR_MARK) { | |
| 335 | + dev_err(&isp->pdev->dev, "Invalid CPD header\n"); | |
| 336 | + return -EINVAL; | |
| 337 | + } | |
| 338 | + | |
| 339 | + /* Sanity check for manifest size */ | |
| 340 | + ent = ipu6_cpd_get_manifest(cpd_file); | |
| 341 | + if (ent->len > MAX_MANIFEST_SIZE) { | |
| 342 | + dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n"); | |
| 343 | + return -EINVAL; | |
| 344 | + } | |
| 345 | + | |
| 346 | + /* Validate metadata */ | |
| 347 | + ent = ipu6_cpd_get_metadata(cpd_file); | |
| 348 | + ret = ipu6_cpd_validate_metadata(isp, cpd_file + ent->offset, ent->len); | |
| 349 | + if (ret) { | |
| 350 | + dev_err(&isp->pdev->dev, "Invalid CPD metadata\n"); | |
| 351 | + return ret; | |
| 352 | + } | |
| 353 | + | |
| 354 | + /* Validate moduledata */ | |
| 355 | + ent = ipu6_cpd_get_moduledata(cpd_file); | |
| 356 | + ret = ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset, | |
| 357 | + ent->len); | |
| 358 | + if (ret) | |
| 359 | + dev_err(&isp->pdev->dev, "Invalid CPD moduledata\n"); | |
| 360 | + | |
| 361 | + return ret; | |
| 362 | +} | |
| @@ -0,0 +1,105 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2015--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_CPD_H | |
| 5 | +#define IPU6_CPD_H | |
| 6 | + | |
| 7 | +struct ipu6_device; | |
| 8 | +struct ipu6_bus_device; | |
| 9 | + | |
| 10 | +#define IPU6_CPD_SIZE_OF_FW_ARCH_VERSION 7 | |
| 11 | +#define IPU6_CPD_SIZE_OF_SYSTEM_VERSION 11 | |
| 12 | +#define IPU6_CPD_SIZE_OF_COMPONENT_NAME 12 | |
| 13 | + | |
| 14 | +#define IPU6_CPD_METADATA_EXTN_TYPE_IUNIT 0x10 | |
| 15 | + | |
| 16 | +#define IPU6_CPD_METADATA_IMAGE_TYPE_RESERVED 0 | |
| 17 | +#define IPU6_CPD_METADATA_IMAGE_TYPE_BOOTLOADER 1 | |
| 18 | +#define IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE 2 | |
| 19 | + | |
| 20 | +#define IPU6_CPD_PKG_DIR_PSYS_SERVER_IDX 0 | |
| 21 | +#define IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX 1 | |
| 22 | + | |
| 23 | +#define IPU6_CPD_PKG_DIR_CLIENT_PG_TYPE 3 | |
| 24 | + | |
| 25 | +#define IPU6_CPD_METADATA_HASH_KEY_SIZE 48 | |
| 26 | +#define IPU6SE_CPD_METADATA_HASH_KEY_SIZE 32 | |
| 27 | + | |
| 28 | +struct ipu6_cpd_module_data_hdr { | |
| 29 | + u32 hdr_len; | |
| 30 | + u32 endian; | |
| 31 | + u32 fw_pkg_date; | |
| 32 | + u32 hive_sdk_date; | |
| 33 | + u32 compiler_date; | |
| 34 | + u32 target_platform_type; | |
| 35 | + u8 sys_ver[IPU6_CPD_SIZE_OF_SYSTEM_VERSION]; | |
| 36 | + u8 fw_arch_ver[IPU6_CPD_SIZE_OF_FW_ARCH_VERSION]; | |
| 37 | + u8 rsvd[2]; | |
| 38 | +} __packed; | |
| 39 | + | |
| 40 | +/* | |
| 41 | + * ipu6_cpd_hdr structure updated as the chksum and | |
| 42 | + * sub_partition_name is unused on host side | |
| 43 | + * CSE layout version 1.6 for IPU6SE (hdr_len = 0x10) | |
| 44 | + * CSE layout version 1.7 for IPU6 (hdr_len = 0x14) | |
| 45 | + */ | |
| 46 | +struct ipu6_cpd_hdr { | |
| 47 | + u32 hdr_mark; | |
| 48 | + u32 ent_cnt; | |
| 49 | + u8 hdr_ver; | |
| 50 | + u8 ent_ver; | |
| 51 | + u8 hdr_len; | |
| 52 | +} __packed; | |
| 53 | + | |
| 54 | +struct ipu6_cpd_ent { | |
| 55 | + u8 name[IPU6_CPD_SIZE_OF_COMPONENT_NAME]; | |
| 56 | + u32 offset; | |
| 57 | + u32 len; | |
| 58 | + u8 rsvd[4]; | |
| 59 | +} __packed; | |
| 60 | + | |
| 61 | +struct ipu6_cpd_metadata_cmpnt_hdr { | |
| 62 | + u32 id; | |
| 63 | + u32 size; | |
| 64 | + u32 ver; | |
| 65 | +} __packed; | |
| 66 | + | |
| 67 | +struct ipu6_cpd_metadata_cmpnt { | |
| 68 | + struct ipu6_cpd_metadata_cmpnt_hdr hdr; | |
| 69 | + u8 sha2_hash[IPU6_CPD_METADATA_HASH_KEY_SIZE]; | |
| 70 | + u32 entry_point; | |
| 71 | + u32 icache_base_offs; | |
| 72 | + u8 attrs[16]; | |
| 73 | +} __packed; | |
| 74 | + | |
| 75 | +struct ipu6se_cpd_metadata_cmpnt { | |
| 76 | + struct ipu6_cpd_metadata_cmpnt_hdr hdr; | |
| 77 | + u8 sha2_hash[IPU6SE_CPD_METADATA_HASH_KEY_SIZE]; | |
| 78 | + u32 entry_point; | |
| 79 | + u32 icache_base_offs; | |
| 80 | + u8 attrs[16]; | |
| 81 | +} __packed; | |
| 82 | + | |
| 83 | +struct ipu6_cpd_metadata_extn { | |
| 84 | + u32 extn_type; | |
| 85 | + u32 len; | |
| 86 | + u32 img_type; | |
| 87 | + u8 rsvd[16]; | |
| 88 | +} __packed; | |
| 89 | + | |
| 90 | +struct ipu6_cpd_client_pkg_hdr { | |
| 91 | + u32 prog_list_offs; | |
| 92 | + u32 prog_list_size; | |
| 93 | + u32 prog_desc_offs; | |
| 94 | + u32 prog_desc_size; | |
| 95 | + u32 pg_manifest_offs; | |
| 96 | + u32 pg_manifest_size; | |
| 97 | + u32 prog_bin_offs; | |
| 98 | + u32 prog_bin_size; | |
| 99 | +} __packed; | |
| 100 | + | |
| 101 | +int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src); | |
| 102 | +void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev); | |
| 103 | +int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file, | |
| 104 | + unsigned long cpd_file_size); | |
| 105 | +#endif /* IPU6_CPD_H */ | |
| @@ -0,0 +1,502 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/cacheflush.h> | |
| 7 | +#include <linux/dma-mapping.h> | |
| 8 | +#include <linux/iova.h> | |
| 9 | +#include <linux/list.h> | |
| 10 | +#include <linux/mm.h> | |
| 11 | +#include <linux/vmalloc.h> | |
| 12 | +#include <linux/scatterlist.h> | |
| 13 | +#include <linux/slab.h> | |
| 14 | +#include <linux/types.h> | |
| 15 | + | |
| 16 | +#include "ipu6.h" | |
| 17 | +#include "ipu6-bus.h" | |
| 18 | +#include "ipu6-dma.h" | |
| 19 | +#include "ipu6-mmu.h" | |
| 20 | + | |
| 21 | +struct vm_info { | |
| 22 | + struct list_head list; | |
| 23 | + struct page **pages; | |
| 24 | + dma_addr_t ipu6_iova; | |
| 25 | + void *vaddr; | |
| 26 | + unsigned long size; | |
| 27 | +}; | |
| 28 | + | |
| 29 | +static struct vm_info *get_vm_info(struct ipu6_mmu *mmu, dma_addr_t iova) | |
| 30 | +{ | |
| 31 | + struct vm_info *info, *save; | |
| 32 | + | |
| 33 | + list_for_each_entry_safe(info, save, &mmu->vma_list, list) { | |
| 34 | + if (iova >= info->ipu6_iova && | |
| 35 | + iova < (info->ipu6_iova + info->size)) | |
| 36 | + return info; | |
| 37 | + } | |
| 38 | + | |
| 39 | + return NULL; | |
| 40 | +} | |
| 41 | + | |
| 42 | +static void __dma_clear_buffer(struct page *page, size_t size, | |
| 43 | + unsigned long attrs) | |
| 44 | +{ | |
| 45 | + void *ptr; | |
| 46 | + | |
| 47 | + if (!page) | |
| 48 | + return; | |
| 49 | + /* | |
| 50 | + * Ensure that the allocated pages are zeroed, and that any data | |
| 51 | + * lurking in the kernel direct-mapped region is invalidated. | |
| 52 | + */ | |
| 53 | + ptr = page_address(page); | |
| 54 | + memset(ptr, 0, size); | |
| 55 | + if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) | |
| 56 | + clflush_cache_range(ptr, size); | |
| 57 | +} | |
| 58 | + | |
| 59 | +static struct page **__dma_alloc_buffer(struct device *dev, size_t size, | |
| 60 | + gfp_t gfp, unsigned long attrs) | |
| 61 | +{ | |
| 62 | + int count = PHYS_PFN(size); | |
| 63 | + int array_size = count * sizeof(struct page *); | |
| 64 | + struct page **pages; | |
| 65 | + int i = 0; | |
| 66 | + | |
| 67 | + pages = kvzalloc(array_size, GFP_KERNEL); | |
| 68 | + if (!pages) | |
| 69 | + return NULL; | |
| 70 | + | |
| 71 | + gfp |= __GFP_NOWARN; | |
| 72 | + | |
| 73 | + while (count) { | |
| 74 | + int j, order = __fls(count); | |
| 75 | + | |
| 76 | + pages[i] = alloc_pages(gfp, order); | |
| 77 | + while (!pages[i] && order) | |
| 78 | + pages[i] = alloc_pages(gfp, --order); | |
| 79 | + if (!pages[i]) | |
| 80 | + goto error; | |
| 81 | + | |
| 82 | + if (order) { | |
| 83 | + split_page(pages[i], order); | |
| 84 | + j = 1 << order; | |
| 85 | + while (j--) | |
| 86 | + pages[i + j] = pages[i] + j; | |
| 87 | + } | |
| 88 | + | |
| 89 | + __dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs); | |
| 90 | + i += 1 << order; | |
| 91 | + count -= 1 << order; | |
| 92 | + } | |
| 93 | + | |
| 94 | + return pages; | |
| 95 | +error: | |
| 96 | + while (i--) | |
| 97 | + if (pages[i]) | |
| 98 | + __free_pages(pages[i], 0); | |
| 99 | + kvfree(pages); | |
| 100 | + return NULL; | |
| 101 | +} | |
| 102 | + | |
| 103 | +static void __dma_free_buffer(struct device *dev, struct page **pages, | |
| 104 | + size_t size, unsigned long attrs) | |
| 105 | +{ | |
| 106 | + int count = PHYS_PFN(size); | |
| 107 | + unsigned int i; | |
| 108 | + | |
| 109 | + for (i = 0; i < count && pages[i]; i++) { | |
| 110 | + __dma_clear_buffer(pages[i], PAGE_SIZE, attrs); | |
| 111 | + __free_pages(pages[i], 0); | |
| 112 | + } | |
| 113 | + | |
| 114 | + kvfree(pages); | |
| 115 | +} | |
| 116 | + | |
| 117 | +static void ipu6_dma_sync_single_for_cpu(struct device *dev, | |
| 118 | + dma_addr_t dma_handle, | |
| 119 | + size_t size, | |
| 120 | + enum dma_data_direction dir) | |
| 121 | +{ | |
| 122 | + void *vaddr; | |
| 123 | + u32 offset; | |
| 124 | + struct vm_info *info; | |
| 125 | + struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; | |
| 126 | + | |
| 127 | + info = get_vm_info(mmu, dma_handle); | |
| 128 | + if (WARN_ON(!info)) | |
| 129 | + return; | |
| 130 | + | |
| 131 | + offset = dma_handle - info->ipu6_iova; | |
| 132 | + if (WARN_ON(size > (info->size - offset))) | |
| 133 | + return; | |
| 134 | + | |
| 135 | + vaddr = info->vaddr + offset; | |
| 136 | + clflush_cache_range(vaddr, size); | |
| 137 | +} | |
| 138 | + | |
| 139 | +static void ipu6_dma_sync_sg_for_cpu(struct device *dev, | |
| 140 | + struct scatterlist *sglist, | |
| 141 | + int nents, enum dma_data_direction dir) | |
| 142 | +{ | |
| 143 | + struct scatterlist *sg; | |
| 144 | + int i; | |
| 145 | + | |
| 146 | + for_each_sg(sglist, sg, nents, i) | |
| 147 | + clflush_cache_range(page_to_virt(sg_page(sg)), sg->length); | |
| 148 | +} | |
| 149 | + | |
| 150 | +static void *ipu6_dma_alloc(struct device *dev, size_t size, | |
| 151 | + dma_addr_t *dma_handle, gfp_t gfp, | |
| 152 | + unsigned long attrs) | |
| 153 | +{ | |
| 154 | + struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; | |
| 155 | + struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev; | |
| 156 | + dma_addr_t pci_dma_addr, ipu6_iova; | |
| 157 | + struct vm_info *info; | |
| 158 | + unsigned long count; | |
| 159 | + struct page **pages; | |
| 160 | + struct iova *iova; | |
| 161 | + unsigned int i; | |
| 162 | + int ret; | |
| 163 | + | |
| 164 | + info = kzalloc(sizeof(*info), GFP_KERNEL); | |
| 165 | + if (!info) | |
| 166 | + return NULL; | |
| 167 | + | |
| 168 | + size = PAGE_ALIGN(size); | |
| 169 | + count = PHYS_PFN(size); | |
| 170 | + | |
| 171 | + iova = alloc_iova(&mmu->dmap->iovad, count, | |
| 172 | + PHYS_PFN(dma_get_mask(dev)), 0); | |
| 173 | + if (!iova) | |
| 174 | + goto out_kfree; | |
| 175 | + | |
| 176 | + pages = __dma_alloc_buffer(dev, size, gfp, attrs); | |
| 177 | + if (!pages) | |
| 178 | + goto out_free_iova; | |
| 179 | + | |
| 180 | + dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n", | |
| 181 | + size, iova->pfn_lo, iova->pfn_hi); | |
| 182 | + for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) { | |
| 183 | + pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0, | |
| 184 | + PAGE_SIZE, DMA_BIDIRECTIONAL, | |
| 185 | + attrs); | |
| 186 | + dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n", | |
| 187 | + &pci_dma_addr); | |
| 188 | + if (dma_mapping_error(&pdev->dev, pci_dma_addr)) { | |
| 189 | + dev_err(dev, "pci_dma_mapping for page[%d] failed", i); | |
| 190 | + goto out_unmap; | |
| 191 | + } | |
| 192 | + | |
| 193 | + ret = ipu6_mmu_map(mmu->dmap->mmu_info, | |
| 194 | + PFN_PHYS(iova->pfn_lo + i), pci_dma_addr, | |
| 195 | + PAGE_SIZE); | |
| 196 | + if (ret) { | |
| 197 | + dev_err(dev, "ipu6_mmu_map for pci_dma[%d] %pad failed", | |
| 198 | + i, &pci_dma_addr); | |
| 199 | + dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, | |
| 200 | + PAGE_SIZE, DMA_BIDIRECTIONAL, | |
| 201 | + attrs); | |
| 202 | + goto out_unmap; | |
| 203 | + } | |
| 204 | + } | |
| 205 | + | |
| 206 | + info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL); | |
| 207 | + if (!info->vaddr) | |
| 208 | + goto out_unmap; | |
| 209 | + | |
| 210 | + *dma_handle = PFN_PHYS(iova->pfn_lo); | |
| 211 | + | |
| 212 | + info->pages = pages; | |
| 213 | + info->ipu6_iova = *dma_handle; | |
| 214 | + info->size = size; | |
| 215 | + list_add(&info->list, &mmu->vma_list); | |
| 216 | + | |
| 217 | + return info->vaddr; | |
| 218 | + | |
| 219 | +out_unmap: | |
| 220 | + while (i--) { | |
| 221 | + ipu6_iova = PFN_PHYS(iova->pfn_lo + i); | |
| 222 | + pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info, | |
| 223 | + ipu6_iova); | |
| 224 | + dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, | |
| 225 | + DMA_BIDIRECTIONAL, attrs); | |
| 226 | + | |
| 227 | + ipu6_mmu_unmap(mmu->dmap->mmu_info, ipu6_iova, PAGE_SIZE); | |
| 228 | + } | |
| 229 | + | |
| 230 | + __dma_free_buffer(dev, pages, size, attrs); | |
| 231 | + | |
| 232 | +out_free_iova: | |
| 233 | + __free_iova(&mmu->dmap->iovad, iova); | |
| 234 | +out_kfree: | |
| 235 | + kfree(info); | |
| 236 | + | |
| 237 | + return NULL; | |
| 238 | +} | |
| 239 | + | |
| 240 | +static void ipu6_dma_free(struct device *dev, size_t size, void *vaddr, | |
| 241 | + dma_addr_t dma_handle, | |
| 242 | + unsigned long attrs) | |
| 243 | +{ | |
| 244 | + struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; | |
| 245 | + struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev; | |
| 246 | + struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle)); | |
| 247 | + dma_addr_t pci_dma_addr, ipu6_iova; | |
| 248 | + struct vm_info *info; | |
| 249 | + struct page **pages; | |
| 250 | + unsigned int i; | |
| 251 | + | |
| 252 | + if (WARN_ON(!iova)) | |
| 253 | + return; | |
| 254 | + | |
| 255 | + info = get_vm_info(mmu, dma_handle); | |
| 256 | + if (WARN_ON(!info)) | |
| 257 | + return; | |
| 258 | + | |
| 259 | + if (WARN_ON(!info->vaddr)) | |
| 260 | + return; | |
| 261 | + | |
| 262 | + if (WARN_ON(!info->pages)) | |
| 263 | + return; | |
| 264 | + | |
| 265 | + list_del(&info->list); | |
| 266 | + | |
| 267 | + size = PAGE_ALIGN(size); | |
| 268 | + | |
| 269 | + pages = info->pages; | |
| 270 | + | |
| 271 | + vunmap(vaddr); | |
| 272 | + | |
| 273 | + for (i = 0; i < PHYS_PFN(size); i++) { | |
| 274 | + ipu6_iova = PFN_PHYS(iova->pfn_lo + i); | |
| 275 | + pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info, | |
| 276 | + ipu6_iova); | |
| 277 | + dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, | |
| 278 | + DMA_BIDIRECTIONAL, attrs); | |
| 279 | + } | |
| 280 | + | |
| 281 | + ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), | |
| 282 | + PFN_PHYS(iova_size(iova))); | |
| 283 | + | |
| 284 | + __dma_free_buffer(dev, pages, size, attrs); | |
| 285 | + | |
| 286 | + mmu->tlb_invalidate(mmu); | |
| 287 | + | |
| 288 | + __free_iova(&mmu->dmap->iovad, iova); | |
| 289 | + | |
| 290 | + kfree(info); | |
| 291 | +} | |
| 292 | + | |
| 293 | +static int ipu6_dma_mmap(struct device *dev, struct vm_area_struct *vma, | |
| 294 | + void *addr, dma_addr_t iova, size_t size, | |
| 295 | + unsigned long attrs) | |
| 296 | +{ | |
| 297 | + struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; | |
| 298 | + size_t count = PHYS_PFN(PAGE_ALIGN(size)); | |
| 299 | + struct vm_info *info; | |
| 300 | + size_t i; | |
| 301 | + int ret; | |
| 302 | + | |
| 303 | + info = get_vm_info(mmu, iova); | |
| 304 | + if (!info) | |
| 305 | + return -EFAULT; | |
| 306 | + | |
| 307 | + if (!info->vaddr) | |
| 308 | + return -EFAULT; | |
| 309 | + | |
| 310 | + if (vma->vm_start & ~PAGE_MASK) | |
| 311 | + return -EINVAL; | |
| 312 | + | |
| 313 | + if (size > info->size) | |
| 314 | + return -EFAULT; | |
| 315 | + | |
| 316 | + for (i = 0; i < count; i++) { | |
| 317 | + ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i), | |
| 318 | + info->pages[i]); | |
| 319 | + if (ret < 0) | |
| 320 | + return ret; | |
| 321 | + } | |
| 322 | + | |
| 323 | + return 0; | |
| 324 | +} | |
| 325 | + | |
| 326 | +static void ipu6_dma_unmap_sg(struct device *dev, | |
| 327 | + struct scatterlist *sglist, | |
| 328 | + int nents, enum dma_data_direction dir, | |
| 329 | + unsigned long attrs) | |
| 330 | +{ | |
| 331 | + struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev; | |
| 332 | + struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; | |
| 333 | + struct iova *iova = find_iova(&mmu->dmap->iovad, | |
| 334 | + PHYS_PFN(sg_dma_address(sglist))); | |
| 335 | + int i, npages, count; | |
| 336 | + struct scatterlist *sg; | |
| 337 | + dma_addr_t pci_dma_addr; | |
| 338 | + | |
| 339 | + if (!nents) | |
| 340 | + return; | |
| 341 | + | |
| 342 | + if (WARN_ON(!iova)) | |
| 343 | + return; | |
| 344 | + | |
| 345 | + if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) | |
| 346 | + ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL); | |
| 347 | + | |
| 348 | + /* get the nents as orig_nents given by caller */ | |
| 349 | + count = 0; | |
| 350 | + npages = iova_size(iova); | |
| 351 | + for_each_sg(sglist, sg, nents, i) { | |
| 352 | + if (sg_dma_len(sg) == 0 || | |
| 353 | + sg_dma_address(sg) == DMA_MAPPING_ERROR) | |
| 354 | + break; | |
| 355 | + | |
| 356 | + npages -= PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg))); | |
| 357 | + count++; | |
| 358 | + if (npages <= 0) | |
| 359 | + break; | |
| 360 | + } | |
| 361 | + | |
| 362 | + /* | |
| 363 | + * Before IPU6 mmu unmap, return the pci dma address back to sg | |
| 364 | + * assume the nents is less than orig_nents as the least granule | |
| 365 | + * is 1 SZ_4K page | |
| 366 | + */ | |
| 367 | + dev_dbg(dev, "trying to unmap concatenated %u ents\n", count); | |
| 368 | + for_each_sg(sglist, sg, count, i) { | |
| 369 | + dev_dbg(dev, "ipu unmap sg[%d] %pad\n", i, &sg_dma_address(sg)); | |
| 370 | + pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info, | |
| 371 | + sg_dma_address(sg)); | |
| 372 | + dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n", | |
| 373 | + &pci_dma_addr, i); | |
| 374 | + sg_dma_address(sg) = pci_dma_addr; | |
| 375 | + } | |
| 376 | + | |
| 377 | + dev_dbg(dev, "ipu6_mmu_unmap low pfn %lu high pfn %lu\n", | |
| 378 | + iova->pfn_lo, iova->pfn_hi); | |
| 379 | + ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), | |
| 380 | + PFN_PHYS(iova_size(iova))); | |
| 381 | + | |
| 382 | + mmu->tlb_invalidate(mmu); | |
| 383 | + | |
| 384 | + dma_unmap_sg_attrs(&pdev->dev, sglist, nents, dir, attrs); | |
| 385 | + | |
| 386 | + __free_iova(&mmu->dmap->iovad, iova); | |
| 387 | +} | |
| 388 | + | |
| 389 | +static int ipu6_dma_map_sg(struct device *dev, struct scatterlist *sglist, | |
| 390 | + int nents, enum dma_data_direction dir, | |
| 391 | + unsigned long attrs) | |
| 392 | +{ | |
| 393 | + struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; | |
| 394 | + struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev; | |
| 395 | + struct scatterlist *sg; | |
| 396 | + struct iova *iova; | |
| 397 | + size_t npages = 0; | |
| 398 | + unsigned long iova_addr; | |
| 399 | + int i, count; | |
| 400 | + | |
| 401 | + for_each_sg(sglist, sg, nents, i) { | |
| 402 | + if (sg->offset) { | |
| 403 | + dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n", | |
| 404 | + i, sg->offset); | |
| 405 | + return -EFAULT; | |
| 406 | + } | |
| 407 | + } | |
| 408 | + | |
| 409 | + dev_dbg(dev, "pci_dma_map_sg trying to map %d ents\n", nents); | |
| 410 | + count = dma_map_sg_attrs(&pdev->dev, sglist, nents, dir, attrs); | |
| 411 | + if (count <= 0) { | |
| 412 | + dev_err(dev, "pci_dma_map_sg %d ents failed\n", nents); | |
| 413 | + return 0; | |
| 414 | + } | |
| 415 | + | |
| 416 | + dev_dbg(dev, "pci_dma_map_sg %d ents mapped\n", count); | |
| 417 | + | |
| 418 | + for_each_sg(sglist, sg, count, i) | |
| 419 | + npages += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg))); | |
| 420 | + | |
| 421 | + iova = alloc_iova(&mmu->dmap->iovad, npages, | |
| 422 | + PHYS_PFN(dma_get_mask(dev)), 0); | |
| 423 | + if (!iova) | |
| 424 | + return 0; | |
| 425 | + | |
| 426 | + dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, | |
| 427 | + iova->pfn_hi); | |
| 428 | + | |
| 429 | + iova_addr = iova->pfn_lo; | |
| 430 | + for_each_sg(sglist, sg, count, i) { | |
| 431 | + int ret; | |
| 432 | + | |
| 433 | + dev_dbg(dev, "mapping entry %d: iova 0x%llx phy %pad size %d\n", | |
| 434 | + i, PFN_PHYS(iova_addr), &sg_dma_address(sg), | |
| 435 | + sg_dma_len(sg)); | |
| 436 | + | |
| 437 | + ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), | |
| 438 | + sg_dma_address(sg), | |
| 439 | + PAGE_ALIGN(sg_dma_len(sg))); | |
| 440 | + if (ret) | |
| 441 | + goto out_fail; | |
| 442 | + | |
| 443 | + sg_dma_address(sg) = PFN_PHYS(iova_addr); | |
| 444 | + | |
| 445 | + iova_addr += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg))); | |
| 446 | + } | |
| 447 | + | |
| 448 | + if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) | |
| 449 | + ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL); | |
| 450 | + | |
| 451 | + return count; | |
| 452 | + | |
| 453 | +out_fail: | |
| 454 | + ipu6_dma_unmap_sg(dev, sglist, i, dir, attrs); | |
| 455 | + | |
| 456 | + return 0; | |
| 457 | +} | |
| 458 | + | |
| 459 | +/* | |
| 460 | + * Create scatter-list for the already allocated DMA buffer | |
| 461 | + */ | |
| 462 | +static int ipu6_dma_get_sgtable(struct device *dev, struct sg_table *sgt, | |
| 463 | + void *cpu_addr, dma_addr_t handle, size_t size, | |
| 464 | + unsigned long attrs) | |
| 465 | +{ | |
| 466 | + struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; | |
| 467 | + struct vm_info *info; | |
| 468 | + int n_pages; | |
| 469 | + int ret = 0; | |
| 470 | + | |
| 471 | + info = get_vm_info(mmu, handle); | |
| 472 | + if (!info) | |
| 473 | + return -EFAULT; | |
| 474 | + | |
| 475 | + if (!info->vaddr) | |
| 476 | + return -EFAULT; | |
| 477 | + | |
| 478 | + if (WARN_ON(!info->pages)) | |
| 479 | + return -ENOMEM; | |
| 480 | + | |
| 481 | + n_pages = PHYS_PFN(PAGE_ALIGN(size)); | |
| 482 | + | |
| 483 | + ret = sg_alloc_table_from_pages(sgt, info->pages, n_pages, 0, size, | |
| 484 | + GFP_KERNEL); | |
| 485 | + if (ret) | |
| 486 | + dev_warn(dev, "IPU6 get sgt table failed\n"); | |
| 487 | + | |
| 488 | + return ret; | |
| 489 | +} | |
| 490 | + | |
| 491 | +const struct dma_map_ops ipu6_dma_ops = { | |
| 492 | + .alloc = ipu6_dma_alloc, | |
| 493 | + .free = ipu6_dma_free, | |
| 494 | + .mmap = ipu6_dma_mmap, | |
| 495 | + .map_sg = ipu6_dma_map_sg, | |
| 496 | + .unmap_sg = ipu6_dma_unmap_sg, | |
| 497 | + .sync_single_for_cpu = ipu6_dma_sync_single_for_cpu, | |
| 498 | + .sync_single_for_device = ipu6_dma_sync_single_for_cpu, | |
| 499 | + .sync_sg_for_cpu = ipu6_dma_sync_sg_for_cpu, | |
| 500 | + .sync_sg_for_device = ipu6_dma_sync_sg_for_cpu, | |
| 501 | + .get_sgtable = ipu6_dma_get_sgtable, | |
| 502 | +}; | |
| @@ -0,0 +1,19 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2013--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_DMA_H | |
| 5 | +#define IPU6_DMA_H | |
| 6 | + | |
| 7 | +#include <linux/dma-map-ops.h> | |
| 8 | +#include <linux/iova.h> | |
| 9 | + | |
| 10 | +struct ipu6_mmu_info; | |
| 11 | + | |
| 12 | +struct ipu6_dma_mapping { | |
| 13 | + struct ipu6_mmu_info *mmu_info; | |
| 14 | + struct iova_domain iovad; | |
| 15 | +}; | |
| 16 | + | |
| 17 | +extern const struct dma_map_ops ipu6_dma_ops; | |
| 18 | + | |
| 19 | +#endif /* IPU6_DMA_H */ | |
| @@ -0,0 +1,413 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/device.h> | |
| 7 | +#include <linux/dma-mapping.h> | |
| 8 | +#include <linux/io.h> | |
| 9 | +#include <linux/math.h> | |
| 10 | +#include <linux/overflow.h> | |
| 11 | +#include <linux/slab.h> | |
| 12 | +#include <linux/types.h> | |
| 13 | + | |
| 14 | +#include "ipu6-bus.h" | |
| 15 | +#include "ipu6-fw-com.h" | |
| 16 | + | |
| 17 | +/* | |
| 18 | + * FWCOM layer is a shared resource between FW and driver. It consist | |
| 19 | + * of token queues to both send and receive directions. Queue is simply | |
| 20 | + * an array of structures with read and write indexes to the queue. | |
| 21 | + * There are 1...n queues to both directions. Queues locates in | |
| 22 | + * system RAM and are mapped to ISP MMU so that both CPU and ISP can | |
| 23 | + * see the same buffer. Indexes are located in ISP DMEM so that FW code | |
| 24 | + * can poll those with very low latency and cost. CPU access to indexes is | |
| 25 | + * more costly but that happens only at message sending time and | |
| 26 | + * interrupt triggered message handling. CPU doesn't need to poll indexes. | |
| 27 | + * wr_reg / rd_reg are offsets to those dmem location. They are not | |
| 28 | + * the indexes itself. | |
| 29 | + */ | |
| 30 | + | |
| 31 | +/* Shared structure between driver and FW - do not modify */ | |
| 32 | +struct ipu6_fw_sys_queue { | |
| 33 | + u64 host_address; | |
| 34 | + u32 vied_address; | |
| 35 | + u32 size; | |
| 36 | + u32 token_size; | |
| 37 | + u32 wr_reg; /* reg number in subsystem's regmem */ | |
| 38 | + u32 rd_reg; | |
| 39 | + u32 _align; | |
| 40 | +} __packed; | |
| 41 | + | |
| 42 | +struct ipu6_fw_sys_queue_res { | |
| 43 | + u64 host_address; | |
| 44 | + u32 vied_address; | |
| 45 | + u32 reg; | |
| 46 | +} __packed; | |
| 47 | + | |
| 48 | +enum syscom_state { | |
| 49 | + /* Program load or explicit host setting should init to this */ | |
| 50 | + SYSCOM_STATE_UNINIT = 0x57a7e000, | |
| 51 | + /* SP Syscom sets this when it is ready for use */ | |
| 52 | + SYSCOM_STATE_READY = 0x57a7e001, | |
| 53 | + /* SP Syscom sets this when no more syscom accesses will happen */ | |
| 54 | + SYSCOM_STATE_INACTIVE = 0x57a7e002, | |
| 55 | +}; | |
| 56 | + | |
| 57 | +enum syscom_cmd { | |
| 58 | + /* Program load or explicit host setting should init to this */ | |
| 59 | + SYSCOM_COMMAND_UNINIT = 0x57a7f000, | |
| 60 | + /* Host Syscom requests syscom to become inactive */ | |
| 61 | + SYSCOM_COMMAND_INACTIVE = 0x57a7f001, | |
| 62 | +}; | |
| 63 | + | |
| 64 | +/* firmware config: data that sent from the host to SP via DDR */ | |
| 65 | +/* Cell copies data into a context */ | |
| 66 | + | |
| 67 | +struct ipu6_fw_syscom_config { | |
| 68 | + u32 firmware_address; | |
| 69 | + | |
| 70 | + u32 num_input_queues; | |
| 71 | + u32 num_output_queues; | |
| 72 | + | |
| 73 | + /* ISP pointers to an array of ipu6_fw_sys_queue structures */ | |
| 74 | + u32 input_queue; | |
| 75 | + u32 output_queue; | |
| 76 | + | |
| 77 | + /* ISYS / PSYS private data */ | |
| 78 | + u32 specific_addr; | |
| 79 | + u32 specific_size; | |
| 80 | +}; | |
| 81 | + | |
| 82 | +struct ipu6_fw_com_context { | |
| 83 | + struct ipu6_bus_device *adev; | |
| 84 | + void __iomem *dmem_addr; | |
| 85 | + int (*cell_ready)(struct ipu6_bus_device *adev); | |
| 86 | + void (*cell_start)(struct ipu6_bus_device *adev); | |
| 87 | + | |
| 88 | + void *dma_buffer; | |
| 89 | + dma_addr_t dma_addr; | |
| 90 | + unsigned int dma_size; | |
| 91 | + unsigned long attrs; | |
| 92 | + | |
| 93 | + struct ipu6_fw_sys_queue *input_queue; /* array of host to SP queues */ | |
| 94 | + struct ipu6_fw_sys_queue *output_queue; /* array of SP to host */ | |
| 95 | + | |
| 96 | + u32 config_vied_addr; | |
| 97 | + | |
| 98 | + unsigned int buttress_boot_offset; | |
| 99 | + void __iomem *base_addr; | |
| 100 | +}; | |
| 101 | + | |
| 102 | +#define FW_COM_WR_REG 0 | |
| 103 | +#define FW_COM_RD_REG 4 | |
| 104 | + | |
| 105 | +#define REGMEM_OFFSET 0 | |
| 106 | +#define TUNIT_MAGIC_PATTERN 0x5a5a5a5a | |
| 107 | + | |
| 108 | +enum regmem_id { | |
| 109 | + /* pass pkg_dir address to SPC in non-secure mode */ | |
| 110 | + PKG_DIR_ADDR_REG = 0, | |
| 111 | + /* Tunit CFG blob for secure - provided by host.*/ | |
| 112 | + TUNIT_CFG_DWR_REG = 1, | |
| 113 | + /* syscom commands - modified by the host */ | |
| 114 | + SYSCOM_COMMAND_REG = 2, | |
| 115 | + /* Store interrupt status - updated by SP */ | |
| 116 | + SYSCOM_IRQ_REG = 3, | |
| 117 | + /* first syscom queue pointer register */ | |
| 118 | + SYSCOM_QPR_BASE_REG = 4 | |
| 119 | +}; | |
| 120 | + | |
| 121 | +#define BUTTRESS_FW_BOOT_PARAMS_0 0x4000 | |
| 122 | +#define BUTTRESS_FW_BOOT_PARAM_REG(base, offset, id) \ | |
| 123 | + ((base) + BUTTRESS_FW_BOOT_PARAMS_0 + ((offset) + (id)) * 4) | |
| 124 | + | |
| 125 | +enum buttress_syscom_id { | |
| 126 | + /* pass syscom configuration to SPC */ | |
| 127 | + SYSCOM_CONFIG_ID = 0, | |
| 128 | + /* syscom state - modified by SP */ | |
| 129 | + SYSCOM_STATE_ID = 1, | |
| 130 | + /* syscom vtl0 addr mask */ | |
| 131 | + SYSCOM_VTL0_ADDR_MASK_ID = 2, | |
| 132 | + SYSCOM_ID_MAX | |
| 133 | +}; | |
| 134 | + | |
| 135 | +static void ipu6_sys_queue_init(struct ipu6_fw_sys_queue *q, unsigned int size, | |
| 136 | + unsigned int token_size, | |
| 137 | + struct ipu6_fw_sys_queue_res *res) | |
| 138 | +{ | |
| 139 | + unsigned int buf_size = (size + 1) * token_size; | |
| 140 | + | |
| 141 | + q->size = size + 1; | |
| 142 | + q->token_size = token_size; | |
| 143 | + | |
| 144 | + /* acquire the shared buffer space */ | |
| 145 | + q->host_address = res->host_address; | |
| 146 | + res->host_address += buf_size; | |
| 147 | + q->vied_address = res->vied_address; | |
| 148 | + res->vied_address += buf_size; | |
| 149 | + | |
| 150 | + /* acquire the shared read and writer pointers */ | |
| 151 | + q->wr_reg = res->reg; | |
| 152 | + res->reg++; | |
| 153 | + q->rd_reg = res->reg; | |
| 154 | + res->reg++; | |
| 155 | +} | |
| 156 | + | |
| 157 | +void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg, | |
| 158 | + struct ipu6_bus_device *adev, void __iomem *base) | |
| 159 | +{ | |
| 160 | + size_t conf_size, inq_size, outq_size, specific_size; | |
| 161 | + struct ipu6_fw_syscom_config *config_host_addr; | |
| 162 | + unsigned int sizeinput = 0, sizeoutput = 0; | |
| 163 | + struct ipu6_fw_sys_queue_res res; | |
| 164 | + struct ipu6_fw_com_context *ctx; | |
| 165 | + struct device *dev = &adev->auxdev.dev; | |
| 166 | + size_t sizeall, offset; | |
| 167 | + unsigned long attrs = 0; | |
| 168 | + void *specific_host_addr; | |
| 169 | + unsigned int i; | |
| 170 | + | |
| 171 | + if (!cfg || !cfg->cell_start || !cfg->cell_ready) | |
| 172 | + return NULL; | |
| 173 | + | |
| 174 | + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | |
| 175 | + if (!ctx) | |
| 176 | + return NULL; | |
| 177 | + ctx->dmem_addr = base + cfg->dmem_addr + REGMEM_OFFSET; | |
| 178 | + ctx->adev = adev; | |
| 179 | + ctx->cell_start = cfg->cell_start; | |
| 180 | + ctx->cell_ready = cfg->cell_ready; | |
| 181 | + ctx->buttress_boot_offset = cfg->buttress_boot_offset; | |
| 182 | + ctx->base_addr = base; | |
| 183 | + | |
| 184 | + /* | |
| 185 | + * Allocate DMA mapped memory. Allocate one big chunk. | |
| 186 | + */ | |
| 187 | + /* Base cfg for FW */ | |
| 188 | + conf_size = roundup(sizeof(struct ipu6_fw_syscom_config), 8); | |
| 189 | + /* Descriptions of the queues */ | |
| 190 | + inq_size = size_mul(cfg->num_input_queues, | |
| 191 | + sizeof(struct ipu6_fw_sys_queue)); | |
| 192 | + outq_size = size_mul(cfg->num_output_queues, | |
| 193 | + sizeof(struct ipu6_fw_sys_queue)); | |
| 194 | + /* FW specific information structure */ | |
| 195 | + specific_size = roundup(cfg->specific_size, 8); | |
| 196 | + | |
| 197 | + sizeall = conf_size + inq_size + outq_size + specific_size; | |
| 198 | + | |
| 199 | + for (i = 0; i < cfg->num_input_queues; i++) | |
| 200 | + sizeinput += size_mul(cfg->input[i].queue_size + 1, | |
| 201 | + cfg->input[i].token_size); | |
| 202 | + | |
| 203 | + for (i = 0; i < cfg->num_output_queues; i++) | |
| 204 | + sizeoutput += size_mul(cfg->output[i].queue_size + 1, | |
| 205 | + cfg->output[i].token_size); | |
| 206 | + | |
| 207 | + sizeall += sizeinput + sizeoutput; | |
| 208 | + | |
| 209 | + ctx->dma_buffer = dma_alloc_attrs(dev, sizeall, &ctx->dma_addr, | |
| 210 | + GFP_KERNEL, attrs); | |
| 211 | + ctx->attrs = attrs; | |
| 212 | + if (!ctx->dma_buffer) { | |
| 213 | + dev_err(dev, "failed to allocate dma memory\n"); | |
| 214 | + kfree(ctx); | |
| 215 | + return NULL; | |
| 216 | + } | |
| 217 | + | |
| 218 | + ctx->dma_size = sizeall; | |
| 219 | + | |
| 220 | + config_host_addr = ctx->dma_buffer; | |
| 221 | + ctx->config_vied_addr = ctx->dma_addr; | |
| 222 | + | |
| 223 | + offset = conf_size; | |
| 224 | + ctx->input_queue = ctx->dma_buffer + offset; | |
| 225 | + config_host_addr->input_queue = ctx->dma_addr + offset; | |
| 226 | + config_host_addr->num_input_queues = cfg->num_input_queues; | |
| 227 | + | |
| 228 | + offset += inq_size; | |
| 229 | + ctx->output_queue = ctx->dma_buffer + offset; | |
| 230 | + config_host_addr->output_queue = ctx->dma_addr + offset; | |
| 231 | + config_host_addr->num_output_queues = cfg->num_output_queues; | |
| 232 | + | |
| 233 | + /* copy firmware specific data */ | |
| 234 | + offset += outq_size; | |
| 235 | + specific_host_addr = ctx->dma_buffer + offset; | |
| 236 | + config_host_addr->specific_addr = ctx->dma_addr + offset; | |
| 237 | + config_host_addr->specific_size = cfg->specific_size; | |
| 238 | + if (cfg->specific_addr && cfg->specific_size) | |
| 239 | + memcpy(specific_host_addr, cfg->specific_addr, | |
| 240 | + cfg->specific_size); | |
| 241 | + | |
| 242 | + /* initialize input queues */ | |
| 243 | + offset += specific_size; | |
| 244 | + res.reg = SYSCOM_QPR_BASE_REG; | |
| 245 | + res.host_address = (u64)(ctx->dma_buffer + offset); | |
| 246 | + res.vied_address = ctx->dma_addr + offset; | |
| 247 | + for (i = 0; i < cfg->num_input_queues; i++) | |
| 248 | + ipu6_sys_queue_init(ctx->input_queue + i, | |
| 249 | + cfg->input[i].queue_size, | |
| 250 | + cfg->input[i].token_size, &res); | |
| 251 | + | |
| 252 | + /* initialize output queues */ | |
| 253 | + offset += sizeinput; | |
| 254 | + res.host_address = (u64)(ctx->dma_buffer + offset); | |
| 255 | + res.vied_address = ctx->dma_addr + offset; | |
| 256 | + for (i = 0; i < cfg->num_output_queues; i++) { | |
| 257 | + ipu6_sys_queue_init(ctx->output_queue + i, | |
| 258 | + cfg->output[i].queue_size, | |
| 259 | + cfg->output[i].token_size, &res); | |
| 260 | + } | |
| 261 | + | |
| 262 | + return ctx; | |
| 263 | +} | |
| 264 | +EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_prepare, INTEL_IPU6); | |
| 265 | + | |
| 266 | +int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx) | |
| 267 | +{ | |
| 268 | + /* write magic pattern to disable the tunit trace */ | |
| 269 | + writel(TUNIT_MAGIC_PATTERN, ctx->dmem_addr + TUNIT_CFG_DWR_REG * 4); | |
| 270 | + /* Check if SP is in valid state */ | |
| 271 | + if (!ctx->cell_ready(ctx->adev)) | |
| 272 | + return -EIO; | |
| 273 | + | |
| 274 | + /* store syscom uninitialized command */ | |
| 275 | + writel(SYSCOM_COMMAND_UNINIT, ctx->dmem_addr + SYSCOM_COMMAND_REG * 4); | |
| 276 | + | |
| 277 | + /* store syscom uninitialized state */ | |
| 278 | + writel(SYSCOM_STATE_UNINIT, | |
| 279 | + BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr, | |
| 280 | + ctx->buttress_boot_offset, | |
| 281 | + SYSCOM_STATE_ID)); | |
| 282 | + | |
| 283 | + /* store firmware configuration address */ | |
| 284 | + writel(ctx->config_vied_addr, | |
| 285 | + BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr, | |
| 286 | + ctx->buttress_boot_offset, | |
| 287 | + SYSCOM_CONFIG_ID)); | |
| 288 | + ctx->cell_start(ctx->adev); | |
| 289 | + | |
| 290 | + return 0; | |
| 291 | +} | |
| 292 | +EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_open, INTEL_IPU6); | |
| 293 | + | |
| 294 | +int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx) | |
| 295 | +{ | |
| 296 | + int state; | |
| 297 | + | |
| 298 | + state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr, | |
| 299 | + ctx->buttress_boot_offset, | |
| 300 | + SYSCOM_STATE_ID)); | |
| 301 | + if (state != SYSCOM_STATE_READY) | |
| 302 | + return -EBUSY; | |
| 303 | + | |
| 304 | + /* set close request flag */ | |
| 305 | + writel(SYSCOM_COMMAND_INACTIVE, ctx->dmem_addr + | |
| 306 | + SYSCOM_COMMAND_REG * 4); | |
| 307 | + | |
| 308 | + return 0; | |
| 309 | +} | |
| 310 | +EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_close, INTEL_IPU6); | |
| 311 | + | |
| 312 | +int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force) | |
| 313 | +{ | |
| 314 | + /* check if release is forced, an verify cell state if it is not */ | |
| 315 | + if (!force && !ctx->cell_ready(ctx->adev)) | |
| 316 | + return -EBUSY; | |
| 317 | + | |
| 318 | + dma_free_attrs(&ctx->adev->auxdev.dev, ctx->dma_size, | |
| 319 | + ctx->dma_buffer, ctx->dma_addr, ctx->attrs); | |
| 320 | + kfree(ctx); | |
| 321 | + return 0; | |
| 322 | +} | |
| 323 | +EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_release, INTEL_IPU6); | |
| 324 | + | |
| 325 | +bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx) | |
| 326 | +{ | |
| 327 | + int state; | |
| 328 | + | |
| 329 | + state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr, | |
| 330 | + ctx->buttress_boot_offset, | |
| 331 | + SYSCOM_STATE_ID)); | |
| 332 | + | |
| 333 | + return state == SYSCOM_STATE_READY; | |
| 334 | +} | |
| 335 | +EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_ready, INTEL_IPU6); | |
| 336 | + | |
| 337 | +void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr) | |
| 338 | +{ | |
| 339 | + struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr]; | |
| 340 | + void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4; | |
| 341 | + unsigned int wr, rd; | |
| 342 | + unsigned int packets; | |
| 343 | + unsigned int index; | |
| 344 | + | |
| 345 | + wr = readl(q_dmem + FW_COM_WR_REG); | |
| 346 | + rd = readl(q_dmem + FW_COM_RD_REG); | |
| 347 | + | |
| 348 | + if (WARN_ON_ONCE(wr >= q->size || rd >= q->size)) | |
| 349 | + return NULL; | |
| 350 | + | |
| 351 | + if (wr < rd) | |
| 352 | + packets = rd - wr - 1; | |
| 353 | + else | |
| 354 | + packets = q->size - (wr - rd + 1); | |
| 355 | + | |
| 356 | + if (!packets) | |
| 357 | + return NULL; | |
| 358 | + | |
| 359 | + index = readl(q_dmem + FW_COM_WR_REG); | |
| 360 | + | |
| 361 | + return (void *)(q->host_address + index * q->token_size); | |
| 362 | +} | |
| 363 | +EXPORT_SYMBOL_NS_GPL(ipu6_send_get_token, INTEL_IPU6); | |
| 364 | + | |
| 365 | +void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr) | |
| 366 | +{ | |
| 367 | + struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr]; | |
| 368 | + void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4; | |
| 369 | + unsigned int wr = readl(q_dmem + FW_COM_WR_REG) + 1; | |
| 370 | + | |
| 371 | + if (wr >= q->size) | |
| 372 | + wr = 0; | |
| 373 | + | |
| 374 | + writel(wr, q_dmem + FW_COM_WR_REG); | |
| 375 | +} | |
| 376 | +EXPORT_SYMBOL_NS_GPL(ipu6_send_put_token, INTEL_IPU6); | |
| 377 | + | |
| 378 | +void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr) | |
| 379 | +{ | |
| 380 | + struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr]; | |
| 381 | + void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4; | |
| 382 | + unsigned int wr, rd; | |
| 383 | + unsigned int packets; | |
| 384 | + | |
| 385 | + wr = readl(q_dmem + FW_COM_WR_REG); | |
| 386 | + rd = readl(q_dmem + FW_COM_RD_REG); | |
| 387 | + | |
| 388 | + if (WARN_ON_ONCE(wr >= q->size || rd >= q->size)) | |
| 389 | + return NULL; | |
| 390 | + | |
| 391 | + if (wr < rd) | |
| 392 | + wr += q->size; | |
| 393 | + | |
| 394 | + packets = wr - rd; | |
| 395 | + if (!packets) | |
| 396 | + return NULL; | |
| 397 | + | |
| 398 | + return (void *)(q->host_address + rd * q->token_size); | |
| 399 | +} | |
| 400 | +EXPORT_SYMBOL_NS_GPL(ipu6_recv_get_token, INTEL_IPU6); | |
| 401 | + | |
| 402 | +void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr) | |
| 403 | +{ | |
| 404 | + struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr]; | |
| 405 | + void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4; | |
| 406 | + unsigned int rd = readl(q_dmem + FW_COM_RD_REG) + 1; | |
| 407 | + | |
| 408 | + if (rd >= q->size) | |
| 409 | + rd = 0; | |
| 410 | + | |
| 411 | + writel(rd, q_dmem + FW_COM_RD_REG); | |
| 412 | +} | |
| 413 | +EXPORT_SYMBOL_NS_GPL(ipu6_recv_put_token, INTEL_IPU6); | |
| @@ -0,0 +1,47 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2013--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_FW_COM_H | |
| 5 | +#define IPU6_FW_COM_H | |
| 6 | + | |
| 7 | +struct ipu6_fw_com_context; | |
| 8 | +struct ipu6_bus_device; | |
| 9 | + | |
| 10 | +struct ipu6_fw_syscom_queue_config { | |
| 11 | + unsigned int queue_size; /* tokens per queue */ | |
| 12 | + unsigned int token_size; /* bytes per token */ | |
| 13 | +}; | |
| 14 | + | |
| 15 | +#define SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET 0 | |
| 16 | + | |
| 17 | +struct ipu6_fw_com_cfg { | |
| 18 | + unsigned int num_input_queues; | |
| 19 | + unsigned int num_output_queues; | |
| 20 | + struct ipu6_fw_syscom_queue_config *input; | |
| 21 | + struct ipu6_fw_syscom_queue_config *output; | |
| 22 | + | |
| 23 | + unsigned int dmem_addr; | |
| 24 | + | |
| 25 | + /* firmware-specific configuration data */ | |
| 26 | + void *specific_addr; | |
| 27 | + unsigned int specific_size; | |
| 28 | + int (*cell_ready)(struct ipu6_bus_device *adev); | |
| 29 | + void (*cell_start)(struct ipu6_bus_device *adev); | |
| 30 | + | |
| 31 | + unsigned int buttress_boot_offset; | |
| 32 | +}; | |
| 33 | + | |
| 34 | +void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg, | |
| 35 | + struct ipu6_bus_device *adev, void __iomem *base); | |
| 36 | + | |
| 37 | +int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx); | |
| 38 | +bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx); | |
| 39 | +int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx); | |
| 40 | +int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force); | |
| 41 | + | |
| 42 | +void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr); | |
| 43 | +void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr); | |
| 44 | +void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr); | |
| 45 | +void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr); | |
| 46 | + | |
| 47 | +#endif | |
| @@ -0,0 +1,487 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/cacheflush.h> | |
| 7 | +#include <linux/delay.h> | |
| 8 | +#include <linux/device.h> | |
| 9 | +#include <linux/io.h> | |
| 10 | +#include <linux/spinlock.h> | |
| 11 | +#include <linux/types.h> | |
| 12 | + | |
| 13 | +#include "ipu6-bus.h" | |
| 14 | +#include "ipu6-fw-com.h" | |
| 15 | +#include "ipu6-isys.h" | |
| 16 | +#include "ipu6-platform-isys-csi2-reg.h" | |
| 17 | +#include "ipu6-platform-regs.h" | |
| 18 | + | |
| 19 | +static const char send_msg_types[N_IPU6_FW_ISYS_SEND_TYPE][32] = { | |
| 20 | + "STREAM_OPEN", | |
| 21 | + "STREAM_START", | |
| 22 | + "STREAM_START_AND_CAPTURE", | |
| 23 | + "STREAM_CAPTURE", | |
| 24 | + "STREAM_STOP", | |
| 25 | + "STREAM_FLUSH", | |
| 26 | + "STREAM_CLOSE" | |
| 27 | +}; | |
| 28 | + | |
| 29 | +static int handle_proxy_response(struct ipu6_isys *isys, unsigned int req_id) | |
| 30 | +{ | |
| 31 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 32 | + struct ipu6_fw_isys_proxy_resp_info_abi *resp; | |
| 33 | + int ret; | |
| 34 | + | |
| 35 | + resp = ipu6_recv_get_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES); | |
| 36 | + if (!resp) | |
| 37 | + return 1; | |
| 38 | + | |
| 39 | + dev_dbg(dev, "Proxy response: id %u, error %u, details %u\n", | |
| 40 | + resp->request_id, resp->error_info.error, | |
| 41 | + resp->error_info.error_details); | |
| 42 | + | |
| 43 | + ret = req_id == resp->request_id ? 0 : -EIO; | |
| 44 | + | |
| 45 | + ipu6_recv_put_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES); | |
| 46 | + | |
| 47 | + return ret; | |
| 48 | +} | |
| 49 | + | |
| 50 | +int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys, | |
| 51 | + unsigned int req_id, | |
| 52 | + unsigned int index, | |
| 53 | + unsigned int offset, u32 value) | |
| 54 | +{ | |
| 55 | + struct ipu6_fw_com_context *ctx = isys->fwcom; | |
| 56 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 57 | + struct ipu6_fw_proxy_send_queue_token *token; | |
| 58 | + unsigned int timeout = 1000; | |
| 59 | + int ret; | |
| 60 | + | |
| 61 | + dev_dbg(dev, | |
| 62 | + "proxy send: req_id 0x%x, index %d, offset 0x%x, value 0x%x\n", | |
| 63 | + req_id, index, offset, value); | |
| 64 | + | |
| 65 | + token = ipu6_send_get_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES); | |
| 66 | + if (!token) | |
| 67 | + return -EBUSY; | |
| 68 | + | |
| 69 | + token->request_id = req_id; | |
| 70 | + token->region_index = index; | |
| 71 | + token->offset = offset; | |
| 72 | + token->value = value; | |
| 73 | + ipu6_send_put_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES); | |
| 74 | + | |
| 75 | + do { | |
| 76 | + usleep_range(100, 110); | |
| 77 | + ret = handle_proxy_response(isys, req_id); | |
| 78 | + if (!ret) | |
| 79 | + break; | |
| 80 | + if (ret == -EIO) { | |
| 81 | + dev_err(dev, "Proxy respond with unexpected id\n"); | |
| 82 | + break; | |
| 83 | + } | |
| 84 | + timeout--; | |
| 85 | + } while (ret && timeout); | |
| 86 | + | |
| 87 | + if (!timeout) | |
| 88 | + dev_err(dev, "Proxy response timed out\n"); | |
| 89 | + | |
| 90 | + return ret; | |
| 91 | +} | |
| 92 | + | |
| 93 | +int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys, | |
| 94 | + const unsigned int stream_handle, | |
| 95 | + void *cpu_mapped_buf, | |
| 96 | + dma_addr_t dma_mapped_buf, | |
| 97 | + size_t size, u16 send_type) | |
| 98 | +{ | |
| 99 | + struct ipu6_fw_com_context *ctx = isys->fwcom; | |
| 100 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 101 | + struct ipu6_fw_send_queue_token *token; | |
| 102 | + | |
| 103 | + if (send_type >= N_IPU6_FW_ISYS_SEND_TYPE) | |
| 104 | + return -EINVAL; | |
| 105 | + | |
| 106 | + dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]); | |
| 107 | + | |
| 108 | + /* | |
| 109 | + * Time to flush cache in case we have some payload. Not all messages | |
| 110 | + * have that | |
| 111 | + */ | |
| 112 | + if (cpu_mapped_buf) | |
| 113 | + clflush_cache_range(cpu_mapped_buf, size); | |
| 114 | + | |
| 115 | + token = ipu6_send_get_token(ctx, | |
| 116 | + stream_handle + IPU6_BASE_MSG_SEND_QUEUES); | |
| 117 | + if (!token) | |
| 118 | + return -EBUSY; | |
| 119 | + | |
| 120 | + token->payload = dma_mapped_buf; | |
| 121 | + token->buf_handle = (unsigned long)cpu_mapped_buf; | |
| 122 | + token->send_type = send_type; | |
| 123 | + | |
| 124 | + ipu6_send_put_token(ctx, stream_handle + IPU6_BASE_MSG_SEND_QUEUES); | |
| 125 | + | |
| 126 | + return 0; | |
| 127 | +} | |
| 128 | + | |
| 129 | +int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys, | |
| 130 | + const unsigned int stream_handle, u16 send_type) | |
| 131 | +{ | |
| 132 | + return ipu6_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0, | |
| 133 | + send_type); | |
| 134 | +} | |
| 135 | + | |
| 136 | +int ipu6_fw_isys_close(struct ipu6_isys *isys) | |
| 137 | +{ | |
| 138 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 139 | + int retry = IPU6_ISYS_CLOSE_RETRY; | |
| 140 | + unsigned long flags; | |
| 141 | + void *fwcom; | |
| 142 | + int ret; | |
| 143 | + | |
| 144 | + /* | |
| 145 | + * Stop the isys fw. Actual close takes | |
| 146 | + * some time as the FW must stop its actions including code fetch | |
| 147 | + * to SP icache. | |
| 148 | + * spinlock to wait the interrupt handler to be finished | |
| 149 | + */ | |
| 150 | + spin_lock_irqsave(&isys->power_lock, flags); | |
| 151 | + ret = ipu6_fw_com_close(isys->fwcom); | |
| 152 | + fwcom = isys->fwcom; | |
| 153 | + isys->fwcom = NULL; | |
| 154 | + spin_unlock_irqrestore(&isys->power_lock, flags); | |
| 155 | + if (ret) | |
| 156 | + dev_err(dev, "Device close failure: %d\n", ret); | |
| 157 | + | |
| 158 | + /* release probably fails if the close failed. Let's try still */ | |
| 159 | + do { | |
| 160 | + usleep_range(400, 500); | |
| 161 | + ret = ipu6_fw_com_release(fwcom, 0); | |
| 162 | + retry--; | |
| 163 | + } while (ret && retry); | |
| 164 | + | |
| 165 | + if (ret) { | |
| 166 | + dev_err(dev, "Device release time out %d\n", ret); | |
| 167 | + spin_lock_irqsave(&isys->power_lock, flags); | |
| 168 | + isys->fwcom = fwcom; | |
| 169 | + spin_unlock_irqrestore(&isys->power_lock, flags); | |
| 170 | + } | |
| 171 | + | |
| 172 | + return ret; | |
| 173 | +} | |
| 174 | + | |
| 175 | +void ipu6_fw_isys_cleanup(struct ipu6_isys *isys) | |
| 176 | +{ | |
| 177 | + int ret; | |
| 178 | + | |
| 179 | + ret = ipu6_fw_com_release(isys->fwcom, 1); | |
| 180 | + if (ret < 0) | |
| 181 | + dev_warn(&isys->adev->auxdev.dev, | |
| 182 | + "Device busy, fw_com release failed."); | |
| 183 | + isys->fwcom = NULL; | |
| 184 | +} | |
| 185 | + | |
| 186 | +static void start_sp(struct ipu6_bus_device *adev) | |
| 187 | +{ | |
| 188 | + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); | |
| 189 | + void __iomem *spc_regs_base = isys->pdata->base + | |
| 190 | + isys->pdata->ipdata->hw_variant.spc_offset; | |
| 191 | + u32 val = IPU6_ISYS_SPC_STATUS_START | | |
| 192 | + IPU6_ISYS_SPC_STATUS_RUN | | |
| 193 | + IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE; | |
| 194 | + | |
| 195 | + val |= isys->icache_prefetch ? IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH : 0; | |
| 196 | + | |
| 197 | + writel(val, spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL); | |
| 198 | +} | |
| 199 | + | |
| 200 | +static int query_sp(struct ipu6_bus_device *adev) | |
| 201 | +{ | |
| 202 | + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); | |
| 203 | + void __iomem *spc_regs_base = isys->pdata->base + | |
| 204 | + isys->pdata->ipdata->hw_variant.spc_offset; | |
| 205 | + u32 val; | |
| 206 | + | |
| 207 | + val = readl(spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL); | |
| 208 | + /* return true when READY == 1, START == 0 */ | |
| 209 | + val &= IPU6_ISYS_SPC_STATUS_READY | IPU6_ISYS_SPC_STATUS_START; | |
| 210 | + | |
| 211 | + return val == IPU6_ISYS_SPC_STATUS_READY; | |
| 212 | +} | |
| 213 | + | |
| 214 | +static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys, | |
| 215 | + struct ipu6_fw_com_cfg *fwcom, | |
| 216 | + unsigned int num_streams) | |
| 217 | +{ | |
| 218 | + unsigned int max_send_queues, max_sram_blocks, max_devq_size; | |
| 219 | + struct ipu6_fw_syscom_queue_config *input_queue_cfg; | |
| 220 | + struct ipu6_fw_syscom_queue_config *output_queue_cfg; | |
| 221 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 222 | + int type_proxy = IPU6_FW_ISYS_QUEUE_TYPE_PROXY; | |
| 223 | + int type_dev = IPU6_FW_ISYS_QUEUE_TYPE_DEV; | |
| 224 | + int type_msg = IPU6_FW_ISYS_QUEUE_TYPE_MSG; | |
| 225 | + int base_dev_send = IPU6_BASE_DEV_SEND_QUEUES; | |
| 226 | + int base_msg_send = IPU6_BASE_MSG_SEND_QUEUES; | |
| 227 | + int base_msg_recv = IPU6_BASE_MSG_RECV_QUEUES; | |
| 228 | + struct ipu6_fw_isys_fw_config *isys_fw_cfg; | |
| 229 | + u32 num_in_message_queues; | |
| 230 | + unsigned int max_streams; | |
| 231 | + unsigned int size; | |
| 232 | + unsigned int i; | |
| 233 | + | |
| 234 | + max_streams = isys->pdata->ipdata->max_streams; | |
| 235 | + max_send_queues = isys->pdata->ipdata->max_send_queues; | |
| 236 | + max_sram_blocks = isys->pdata->ipdata->max_sram_blocks; | |
| 237 | + max_devq_size = isys->pdata->ipdata->max_devq_size; | |
| 238 | + num_in_message_queues = clamp(num_streams, 1U, max_streams); | |
| 239 | + isys_fw_cfg = devm_kzalloc(dev, sizeof(*isys_fw_cfg), GFP_KERNEL); | |
| 240 | + if (!isys_fw_cfg) | |
| 241 | + return -ENOMEM; | |
| 242 | + | |
| 243 | + isys_fw_cfg->num_send_queues[type_proxy] = IPU6_N_MAX_PROXY_SEND_QUEUES; | |
| 244 | + isys_fw_cfg->num_send_queues[type_dev] = IPU6_N_MAX_DEV_SEND_QUEUES; | |
| 245 | + isys_fw_cfg->num_send_queues[type_msg] = num_in_message_queues; | |
| 246 | + isys_fw_cfg->num_recv_queues[type_proxy] = IPU6_N_MAX_PROXY_RECV_QUEUES; | |
| 247 | + /* Common msg/dev return queue */ | |
| 248 | + isys_fw_cfg->num_recv_queues[type_dev] = 0; | |
| 249 | + isys_fw_cfg->num_recv_queues[type_msg] = 1; | |
| 250 | + | |
| 251 | + size = sizeof(*input_queue_cfg) * max_send_queues; | |
| 252 | + input_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL); | |
| 253 | + if (!input_queue_cfg) | |
| 254 | + return -ENOMEM; | |
| 255 | + | |
| 256 | + size = sizeof(*output_queue_cfg) * IPU6_N_MAX_RECV_QUEUES; | |
| 257 | + output_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL); | |
| 258 | + if (!output_queue_cfg) | |
| 259 | + return -ENOMEM; | |
| 260 | + | |
| 261 | + fwcom->input = input_queue_cfg; | |
| 262 | + fwcom->output = output_queue_cfg; | |
| 263 | + | |
| 264 | + fwcom->num_input_queues = isys_fw_cfg->num_send_queues[type_proxy] + | |
| 265 | + isys_fw_cfg->num_send_queues[type_dev] + | |
| 266 | + isys_fw_cfg->num_send_queues[type_msg]; | |
| 267 | + | |
| 268 | + fwcom->num_output_queues = isys_fw_cfg->num_recv_queues[type_proxy] + | |
| 269 | + isys_fw_cfg->num_recv_queues[type_dev] + | |
| 270 | + isys_fw_cfg->num_recv_queues[type_msg]; | |
| 271 | + | |
| 272 | + /* SRAM partitioning. Equal partitioning is set. */ | |
| 273 | + for (i = 0; i < max_sram_blocks; i++) { | |
| 274 | + if (i < num_in_message_queues) | |
| 275 | + isys_fw_cfg->buffer_partition.num_gda_pages[i] = | |
| 276 | + (IPU6_DEVICE_GDA_NR_PAGES * | |
| 277 | + IPU6_DEVICE_GDA_VIRT_FACTOR) / | |
| 278 | + num_in_message_queues; | |
| 279 | + else | |
| 280 | + isys_fw_cfg->buffer_partition.num_gda_pages[i] = 0; | |
| 281 | + } | |
| 282 | + | |
| 283 | + /* FW assumes proxy interface at fwcom queue 0 */ | |
| 284 | + for (i = 0; i < isys_fw_cfg->num_send_queues[type_proxy]; i++) { | |
| 285 | + input_queue_cfg[i].token_size = | |
| 286 | + sizeof(struct ipu6_fw_proxy_send_queue_token); | |
| 287 | + input_queue_cfg[i].queue_size = IPU6_ISYS_SIZE_PROXY_SEND_QUEUE; | |
| 288 | + } | |
| 289 | + | |
| 290 | + for (i = 0; i < isys_fw_cfg->num_send_queues[type_dev]; i++) { | |
| 291 | + input_queue_cfg[base_dev_send + i].token_size = | |
| 292 | + sizeof(struct ipu6_fw_send_queue_token); | |
| 293 | + input_queue_cfg[base_dev_send + i].queue_size = max_devq_size; | |
| 294 | + } | |
| 295 | + | |
| 296 | + for (i = 0; i < isys_fw_cfg->num_send_queues[type_msg]; i++) { | |
| 297 | + input_queue_cfg[base_msg_send + i].token_size = | |
| 298 | + sizeof(struct ipu6_fw_send_queue_token); | |
| 299 | + input_queue_cfg[base_msg_send + i].queue_size = | |
| 300 | + IPU6_ISYS_SIZE_SEND_QUEUE; | |
| 301 | + } | |
| 302 | + | |
| 303 | + for (i = 0; i < isys_fw_cfg->num_recv_queues[type_proxy]; i++) { | |
| 304 | + output_queue_cfg[i].token_size = | |
| 305 | + sizeof(struct ipu6_fw_proxy_resp_queue_token); | |
| 306 | + output_queue_cfg[i].queue_size = | |
| 307 | + IPU6_ISYS_SIZE_PROXY_RECV_QUEUE; | |
| 308 | + } | |
| 309 | + /* There is no recv DEV queue */ | |
| 310 | + for (i = 0; i < isys_fw_cfg->num_recv_queues[type_msg]; i++) { | |
| 311 | + output_queue_cfg[base_msg_recv + i].token_size = | |
| 312 | + sizeof(struct ipu6_fw_resp_queue_token); | |
| 313 | + output_queue_cfg[base_msg_recv + i].queue_size = | |
| 314 | + IPU6_ISYS_SIZE_RECV_QUEUE; | |
| 315 | + } | |
| 316 | + | |
| 317 | + fwcom->dmem_addr = isys->pdata->ipdata->hw_variant.dmem_offset; | |
| 318 | + fwcom->specific_addr = isys_fw_cfg; | |
| 319 | + fwcom->specific_size = sizeof(*isys_fw_cfg); | |
| 320 | + | |
| 321 | + return 0; | |
| 322 | +} | |
| 323 | + | |
| 324 | +int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams) | |
| 325 | +{ | |
| 326 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 327 | + int retry = IPU6_ISYS_OPEN_RETRY; | |
| 328 | + struct ipu6_fw_com_cfg fwcom = { | |
| 329 | + .cell_start = start_sp, | |
| 330 | + .cell_ready = query_sp, | |
| 331 | + .buttress_boot_offset = SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET, | |
| 332 | + }; | |
| 333 | + int ret; | |
| 334 | + | |
| 335 | + ipu6_isys_fwcom_cfg_init(isys, &fwcom, num_streams); | |
| 336 | + | |
| 337 | + isys->fwcom = ipu6_fw_com_prepare(&fwcom, isys->adev, | |
| 338 | + isys->pdata->base); | |
| 339 | + if (!isys->fwcom) { | |
| 340 | + dev_err(dev, "isys fw com prepare failed\n"); | |
| 341 | + return -EIO; | |
| 342 | + } | |
| 343 | + | |
| 344 | + ret = ipu6_fw_com_open(isys->fwcom); | |
| 345 | + if (ret) { | |
| 346 | + dev_err(dev, "isys fw com open failed %d\n", ret); | |
| 347 | + return ret; | |
| 348 | + } | |
| 349 | + | |
| 350 | + do { | |
| 351 | + usleep_range(400, 500); | |
| 352 | + if (ipu6_fw_com_ready(isys->fwcom)) | |
| 353 | + break; | |
| 354 | + retry--; | |
| 355 | + } while (retry > 0); | |
| 356 | + | |
| 357 | + if (!retry) { | |
| 358 | + dev_err(dev, "isys port open ready failed %d\n", ret); | |
| 359 | + ipu6_fw_isys_close(isys); | |
| 360 | + ret = -EIO; | |
| 361 | + } | |
| 362 | + | |
| 363 | + return ret; | |
| 364 | +} | |
| 365 | + | |
| 366 | +struct ipu6_fw_isys_resp_info_abi * | |
| 367 | +ipu6_fw_isys_get_resp(void *context, unsigned int queue) | |
| 368 | +{ | |
| 369 | + return ipu6_recv_get_token(context, queue); | |
| 370 | +} | |
| 371 | + | |
| 372 | +void ipu6_fw_isys_put_resp(void *context, unsigned int queue) | |
| 373 | +{ | |
| 374 | + ipu6_recv_put_token(context, queue); | |
| 375 | +} | |
| 376 | + | |
| 377 | +void ipu6_fw_isys_dump_stream_cfg(struct device *dev, | |
| 378 | + struct ipu6_fw_isys_stream_cfg_data_abi *cfg) | |
| 379 | +{ | |
| 380 | + unsigned int i; | |
| 381 | + | |
| 382 | + dev_dbg(dev, "-----------------------------------------------------\n"); | |
| 383 | + dev_dbg(dev, "IPU6_FW_ISYS_STREAM_CFG_DATA\n"); | |
| 384 | + | |
| 385 | + dev_dbg(dev, "compfmt = %d\n", cfg->vc); | |
| 386 | + dev_dbg(dev, "src = %d\n", cfg->src); | |
| 387 | + dev_dbg(dev, "vc = %d\n", cfg->vc); | |
| 388 | + dev_dbg(dev, "isl_use = %d\n", cfg->isl_use); | |
| 389 | + dev_dbg(dev, "sensor_type = %d\n", cfg->sensor_type); | |
| 390 | + | |
| 391 | + dev_dbg(dev, "send_irq_sof_discarded = %d\n", | |
| 392 | + cfg->send_irq_sof_discarded); | |
| 393 | + dev_dbg(dev, "send_irq_eof_discarded = %d\n", | |
| 394 | + cfg->send_irq_eof_discarded); | |
| 395 | + dev_dbg(dev, "send_resp_sof_discarded = %d\n", | |
| 396 | + cfg->send_resp_sof_discarded); | |
| 397 | + dev_dbg(dev, "send_resp_eof_discarded = %d\n", | |
| 398 | + cfg->send_resp_eof_discarded); | |
| 399 | + | |
| 400 | + dev_dbg(dev, "crop:\n"); | |
| 401 | + dev_dbg(dev, "\t.left_top = [%d, %d]\n", cfg->crop.left_offset, | |
| 402 | + cfg->crop.top_offset); | |
| 403 | + dev_dbg(dev, "\t.right_bottom = [%d, %d]\n", cfg->crop.right_offset, | |
| 404 | + cfg->crop.bottom_offset); | |
| 405 | + | |
| 406 | + dev_dbg(dev, "nof_input_pins = %d\n", cfg->nof_input_pins); | |
| 407 | + for (i = 0; i < cfg->nof_input_pins; i++) { | |
| 408 | + dev_dbg(dev, "input pin[%d]:\n", i); | |
| 409 | + dev_dbg(dev, "\t.dt = 0x%0x\n", cfg->input_pins[i].dt); | |
| 410 | + dev_dbg(dev, "\t.mipi_store_mode = %d\n", | |
| 411 | + cfg->input_pins[i].mipi_store_mode); | |
| 412 | + dev_dbg(dev, "\t.bits_per_pix = %d\n", | |
| 413 | + cfg->input_pins[i].bits_per_pix); | |
| 414 | + dev_dbg(dev, "\t.mapped_dt = 0x%0x\n", | |
| 415 | + cfg->input_pins[i].mapped_dt); | |
| 416 | + dev_dbg(dev, "\t.input_res = %dx%d\n", | |
| 417 | + cfg->input_pins[i].input_res.width, | |
| 418 | + cfg->input_pins[i].input_res.height); | |
| 419 | + dev_dbg(dev, "\t.mipi_decompression = %d\n", | |
| 420 | + cfg->input_pins[i].mipi_decompression); | |
| 421 | + dev_dbg(dev, "\t.capture_mode = %d\n", | |
| 422 | + cfg->input_pins[i].capture_mode); | |
| 423 | + } | |
| 424 | + | |
| 425 | + dev_dbg(dev, "nof_output_pins = %d\n", cfg->nof_output_pins); | |
| 426 | + for (i = 0; i < cfg->nof_output_pins; i++) { | |
| 427 | + dev_dbg(dev, "output_pin[%d]:\n", i); | |
| 428 | + dev_dbg(dev, "\t.input_pin_id = %d\n", | |
| 429 | + cfg->output_pins[i].input_pin_id); | |
| 430 | + dev_dbg(dev, "\t.output_res = %dx%d\n", | |
| 431 | + cfg->output_pins[i].output_res.width, | |
| 432 | + cfg->output_pins[i].output_res.height); | |
| 433 | + dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride); | |
| 434 | + dev_dbg(dev, "\t.pt = %d\n", cfg->output_pins[i].pt); | |
| 435 | + dev_dbg(dev, "\t.payload_buf_size = %d\n", | |
| 436 | + cfg->output_pins[i].payload_buf_size); | |
| 437 | + dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft); | |
| 438 | + dev_dbg(dev, "\t.watermark_in_lines = %d\n", | |
| 439 | + cfg->output_pins[i].watermark_in_lines); | |
| 440 | + dev_dbg(dev, "\t.send_irq = %d\n", | |
| 441 | + cfg->output_pins[i].send_irq); | |
| 442 | + dev_dbg(dev, "\t.reserve_compression = %d\n", | |
| 443 | + cfg->output_pins[i].reserve_compression); | |
| 444 | + dev_dbg(dev, "\t.snoopable = %d\n", | |
| 445 | + cfg->output_pins[i].snoopable); | |
| 446 | + dev_dbg(dev, "\t.error_handling_enable = %d\n", | |
| 447 | + cfg->output_pins[i].error_handling_enable); | |
| 448 | + dev_dbg(dev, "\t.sensor_type = %d\n", | |
| 449 | + cfg->output_pins[i].sensor_type); | |
| 450 | + } | |
| 451 | + dev_dbg(dev, "-----------------------------------------------------\n"); | |
| 452 | +} | |
| 453 | + | |
| 454 | +void | |
| 455 | +ipu6_fw_isys_dump_frame_buff_set(struct device *dev, | |
| 456 | + struct ipu6_fw_isys_frame_buff_set_abi *buf, | |
| 457 | + unsigned int outputs) | |
| 458 | +{ | |
| 459 | + unsigned int i; | |
| 460 | + | |
| 461 | + dev_dbg(dev, "-----------------------------------------------------\n"); | |
| 462 | + dev_dbg(dev, "IPU6_FW_ISYS_FRAME_BUFF_SET\n"); | |
| 463 | + | |
| 464 | + for (i = 0; i < outputs; i++) { | |
| 465 | + dev_dbg(dev, "output_pin[%d]:\n", i); | |
| 466 | + dev_dbg(dev, "\t.out_buf_id = %llu\n", | |
| 467 | + buf->output_pins[i].out_buf_id); | |
| 468 | + dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr); | |
| 469 | + dev_dbg(dev, "\t.compress = %d\n", | |
| 470 | + buf->output_pins[i].compress); | |
| 471 | + } | |
| 472 | + | |
| 473 | + dev_dbg(dev, "send_irq_sof = 0x%x\n", buf->send_irq_sof); | |
| 474 | + dev_dbg(dev, "send_irq_eof = 0x%x\n", buf->send_irq_eof); | |
| 475 | + dev_dbg(dev, "send_resp_sof = 0x%x\n", buf->send_resp_sof); | |
| 476 | + dev_dbg(dev, "send_resp_eof = 0x%x\n", buf->send_resp_eof); | |
| 477 | + dev_dbg(dev, "send_irq_capture_ack = 0x%x\n", | |
| 478 | + buf->send_irq_capture_ack); | |
| 479 | + dev_dbg(dev, "send_irq_capture_done = 0x%x\n", | |
| 480 | + buf->send_irq_capture_done); | |
| 481 | + dev_dbg(dev, "send_resp_capture_ack = 0x%x\n", | |
| 482 | + buf->send_resp_capture_ack); | |
| 483 | + dev_dbg(dev, "send_resp_capture_done = 0x%x\n", | |
| 484 | + buf->send_resp_capture_done); | |
| 485 | + | |
| 486 | + dev_dbg(dev, "-----------------------------------------------------\n"); | |
| 487 | +} | |
| @@ -0,0 +1,596 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2013--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_FW_ISYS_H | |
| 5 | +#define IPU6_FW_ISYS_H | |
| 6 | + | |
| 7 | +#include <linux/types.h> | |
| 8 | + | |
| 9 | +struct device; | |
| 10 | +struct ipu6_isys; | |
| 11 | + | |
| 12 | +/* Max number of Input/Output Pins */ | |
| 13 | +#define IPU6_MAX_IPINS 4 | |
| 14 | + | |
| 15 | +#define IPU6_MAX_OPINS ((IPU6_MAX_IPINS) + 1) | |
| 16 | + | |
| 17 | +#define IPU6_STREAM_ID_MAX 16 | |
| 18 | +#define IPU6_NONSECURE_STREAM_ID_MAX 12 | |
| 19 | +#define IPU6_DEV_SEND_QUEUE_SIZE (IPU6_STREAM_ID_MAX) | |
| 20 | +#define IPU6_NOF_SRAM_BLOCKS_MAX (IPU6_STREAM_ID_MAX) | |
| 21 | +#define IPU6_N_MAX_MSG_SEND_QUEUES (IPU6_STREAM_ID_MAX) | |
| 22 | +#define IPU6SE_STREAM_ID_MAX 8 | |
| 23 | +#define IPU6SE_NONSECURE_STREAM_ID_MAX 4 | |
| 24 | +#define IPU6SE_DEV_SEND_QUEUE_SIZE (IPU6SE_STREAM_ID_MAX) | |
| 25 | +#define IPU6SE_NOF_SRAM_BLOCKS_MAX (IPU6SE_STREAM_ID_MAX) | |
| 26 | +#define IPU6SE_N_MAX_MSG_SEND_QUEUES (IPU6SE_STREAM_ID_MAX) | |
| 27 | + | |
| 28 | +/* Single return queue for all streams/commands type */ | |
| 29 | +#define IPU6_N_MAX_MSG_RECV_QUEUES 1 | |
| 30 | +/* Single device queue for high priority commands (bypass in-order queue) */ | |
| 31 | +#define IPU6_N_MAX_DEV_SEND_QUEUES 1 | |
| 32 | +/* Single dedicated send queue for proxy interface */ | |
| 33 | +#define IPU6_N_MAX_PROXY_SEND_QUEUES 1 | |
| 34 | +/* Single dedicated recv queue for proxy interface */ | |
| 35 | +#define IPU6_N_MAX_PROXY_RECV_QUEUES 1 | |
| 36 | +/* Send queues layout */ | |
| 37 | +#define IPU6_BASE_PROXY_SEND_QUEUES 0 | |
| 38 | +#define IPU6_BASE_DEV_SEND_QUEUES \ | |
| 39 | + (IPU6_BASE_PROXY_SEND_QUEUES + IPU6_N_MAX_PROXY_SEND_QUEUES) | |
| 40 | +#define IPU6_BASE_MSG_SEND_QUEUES \ | |
| 41 | + (IPU6_BASE_DEV_SEND_QUEUES + IPU6_N_MAX_DEV_SEND_QUEUES) | |
| 42 | +/* Recv queues layout */ | |
| 43 | +#define IPU6_BASE_PROXY_RECV_QUEUES 0 | |
| 44 | +#define IPU6_BASE_MSG_RECV_QUEUES \ | |
| 45 | + (IPU6_BASE_PROXY_RECV_QUEUES + IPU6_N_MAX_PROXY_RECV_QUEUES) | |
| 46 | +#define IPU6_N_MAX_RECV_QUEUES \ | |
| 47 | + (IPU6_BASE_MSG_RECV_QUEUES + IPU6_N_MAX_MSG_RECV_QUEUES) | |
| 48 | + | |
| 49 | +#define IPU6_N_MAX_SEND_QUEUES \ | |
| 50 | + (IPU6_BASE_MSG_SEND_QUEUES + IPU6_N_MAX_MSG_SEND_QUEUES) | |
| 51 | +#define IPU6SE_N_MAX_SEND_QUEUES \ | |
| 52 | + (IPU6_BASE_MSG_SEND_QUEUES + IPU6SE_N_MAX_MSG_SEND_QUEUES) | |
| 53 | + | |
| 54 | +/* Max number of planes for frame formats supported by the FW */ | |
| 55 | +#define IPU6_PIN_PLANES_MAX 4 | |
| 56 | + | |
| 57 | +#define IPU6_FW_ISYS_SENSOR_TYPE_START 14 | |
| 58 | +#define IPU6_FW_ISYS_SENSOR_TYPE_END 19 | |
| 59 | +#define IPU6SE_FW_ISYS_SENSOR_TYPE_START 6 | |
| 60 | +#define IPU6SE_FW_ISYS_SENSOR_TYPE_END 11 | |
| 61 | +/* | |
| 62 | + * Device close takes some time from last ack message to actual stopping | |
| 63 | + * of the SP processor. As long as the SP processor runs we can't proceed with | |
| 64 | + * clean up of resources. | |
| 65 | + */ | |
| 66 | +#define IPU6_ISYS_OPEN_RETRY 2000 | |
| 67 | +#define IPU6_ISYS_CLOSE_RETRY 2000 | |
| 68 | +#define IPU6_FW_CALL_TIMEOUT_JIFFIES msecs_to_jiffies(2000) | |
| 69 | + | |
| 70 | +enum ipu6_fw_isys_resp_type { | |
| 71 | + IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE = 0, | |
| 72 | + IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK, | |
| 73 | + IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, | |
| 74 | + IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, | |
| 75 | + IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, | |
| 76 | + IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, | |
| 77 | + IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, | |
| 78 | + IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY, | |
| 79 | + IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_WATERMARK, | |
| 80 | + IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF, | |
| 81 | + IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF, | |
| 82 | + IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, | |
| 83 | + IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, | |
| 84 | + IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_SKIPPED, | |
| 85 | + IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED, | |
| 86 | + IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED, | |
| 87 | + IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED, | |
| 88 | + IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY, | |
| 89 | + N_IPU6_FW_ISYS_RESP_TYPE | |
| 90 | +}; | |
| 91 | + | |
| 92 | +enum ipu6_fw_isys_send_type { | |
| 93 | + IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN = 0, | |
| 94 | + IPU6_FW_ISYS_SEND_TYPE_STREAM_START, | |
| 95 | + IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE, | |
| 96 | + IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE, | |
| 97 | + IPU6_FW_ISYS_SEND_TYPE_STREAM_STOP, | |
| 98 | + IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH, | |
| 99 | + IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE, | |
| 100 | + N_IPU6_FW_ISYS_SEND_TYPE | |
| 101 | +}; | |
| 102 | + | |
| 103 | +enum ipu6_fw_isys_queue_type { | |
| 104 | + IPU6_FW_ISYS_QUEUE_TYPE_PROXY = 0, | |
| 105 | + IPU6_FW_ISYS_QUEUE_TYPE_DEV, | |
| 106 | + IPU6_FW_ISYS_QUEUE_TYPE_MSG, | |
| 107 | + N_IPU6_FW_ISYS_QUEUE_TYPE | |
| 108 | +}; | |
| 109 | + | |
| 110 | +enum ipu6_fw_isys_stream_source { | |
| 111 | + IPU6_FW_ISYS_STREAM_SRC_PORT_0 = 0, | |
| 112 | + IPU6_FW_ISYS_STREAM_SRC_PORT_1, | |
| 113 | + IPU6_FW_ISYS_STREAM_SRC_PORT_2, | |
| 114 | + IPU6_FW_ISYS_STREAM_SRC_PORT_3, | |
| 115 | + IPU6_FW_ISYS_STREAM_SRC_PORT_4, | |
| 116 | + IPU6_FW_ISYS_STREAM_SRC_PORT_5, | |
| 117 | + IPU6_FW_ISYS_STREAM_SRC_PORT_6, | |
| 118 | + IPU6_FW_ISYS_STREAM_SRC_PORT_7, | |
| 119 | + IPU6_FW_ISYS_STREAM_SRC_PORT_8, | |
| 120 | + IPU6_FW_ISYS_STREAM_SRC_PORT_9, | |
| 121 | + IPU6_FW_ISYS_STREAM_SRC_PORT_10, | |
| 122 | + IPU6_FW_ISYS_STREAM_SRC_PORT_11, | |
| 123 | + IPU6_FW_ISYS_STREAM_SRC_PORT_12, | |
| 124 | + IPU6_FW_ISYS_STREAM_SRC_PORT_13, | |
| 125 | + IPU6_FW_ISYS_STREAM_SRC_PORT_14, | |
| 126 | + IPU6_FW_ISYS_STREAM_SRC_PORT_15, | |
| 127 | + IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_0, | |
| 128 | + IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_1, | |
| 129 | + IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_2, | |
| 130 | + IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_3, | |
| 131 | + IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_4, | |
| 132 | + IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_5, | |
| 133 | + IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_6, | |
| 134 | + IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_7, | |
| 135 | + IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_8, | |
| 136 | + IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_9, | |
| 137 | + N_IPU6_FW_ISYS_STREAM_SRC | |
| 138 | +}; | |
| 139 | + | |
| 140 | +#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 IPU6_FW_ISYS_STREAM_SRC_PORT_0 | |
| 141 | +#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT1 IPU6_FW_ISYS_STREAM_SRC_PORT_1 | |
| 142 | +#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT2 IPU6_FW_ISYS_STREAM_SRC_PORT_2 | |
| 143 | +#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT3 IPU6_FW_ISYS_STREAM_SRC_PORT_3 | |
| 144 | + | |
| 145 | +#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTA IPU6_FW_ISYS_STREAM_SRC_PORT_4 | |
| 146 | +#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTB IPU6_FW_ISYS_STREAM_SRC_PORT_5 | |
| 147 | +#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT0 \ | |
| 148 | + IPU6_FW_ISYS_STREAM_SRC_PORT_6 | |
| 149 | +#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT1 \ | |
| 150 | + IPU6_FW_ISYS_STREAM_SRC_PORT_7 | |
| 151 | +#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT2 \ | |
| 152 | + IPU6_FW_ISYS_STREAM_SRC_PORT_8 | |
| 153 | +#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT3 \ | |
| 154 | + IPU6_FW_ISYS_STREAM_SRC_PORT_9 | |
| 155 | + | |
| 156 | +#define IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_PORT0 IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_0 | |
| 157 | +#define IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_PORT1 IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_1 | |
| 158 | + | |
| 159 | +/* | |
| 160 | + * enum ipu6_fw_isys_mipi_vc: MIPI csi2 spec | |
| 161 | + * supports up to 4 virtual per physical channel | |
| 162 | + */ | |
| 163 | +enum ipu6_fw_isys_mipi_vc { | |
| 164 | + IPU6_FW_ISYS_MIPI_VC_0 = 0, | |
| 165 | + IPU6_FW_ISYS_MIPI_VC_1, | |
| 166 | + IPU6_FW_ISYS_MIPI_VC_2, | |
| 167 | + IPU6_FW_ISYS_MIPI_VC_3, | |
| 168 | + N_IPU6_FW_ISYS_MIPI_VC | |
| 169 | +}; | |
| 170 | + | |
| 171 | +enum ipu6_fw_isys_frame_format_type { | |
| 172 | + IPU6_FW_ISYS_FRAME_FORMAT_NV11 = 0, /* 12 bit YUV 411, Y, UV plane */ | |
| 173 | + IPU6_FW_ISYS_FRAME_FORMAT_NV12, /* 12 bit YUV 420, Y, UV plane */ | |
| 174 | + IPU6_FW_ISYS_FRAME_FORMAT_NV12_16, /* 16 bit YUV 420, Y, UV plane */ | |
| 175 | + /* 12 bit YUV 420, Intel proprietary tiled format */ | |
| 176 | + IPU6_FW_ISYS_FRAME_FORMAT_NV12_TILEY, | |
| 177 | + | |
| 178 | + IPU6_FW_ISYS_FRAME_FORMAT_NV16, /* 16 bit YUV 422, Y, UV plane */ | |
| 179 | + IPU6_FW_ISYS_FRAME_FORMAT_NV21, /* 12 bit YUV 420, Y, VU plane */ | |
| 180 | + IPU6_FW_ISYS_FRAME_FORMAT_NV61, /* 16 bit YUV 422, Y, VU plane */ | |
| 181 | + IPU6_FW_ISYS_FRAME_FORMAT_YV12, /* 12 bit YUV 420, Y, V, U plane */ | |
| 182 | + IPU6_FW_ISYS_FRAME_FORMAT_YV16, /* 16 bit YUV 422, Y, V, U plane */ | |
| 183 | + IPU6_FW_ISYS_FRAME_FORMAT_YUV420, /* 12 bit YUV 420, Y, U, V plane */ | |
| 184 | + IPU6_FW_ISYS_FRAME_FORMAT_YUV420_10, /* yuv420, 10 bits per subpixel */ | |
| 185 | + IPU6_FW_ISYS_FRAME_FORMAT_YUV420_12, /* yuv420, 12 bits per subpixel */ | |
| 186 | + IPU6_FW_ISYS_FRAME_FORMAT_YUV420_14, /* yuv420, 14 bits per subpixel */ | |
| 187 | + IPU6_FW_ISYS_FRAME_FORMAT_YUV420_16, /* yuv420, 16 bits per subpixel */ | |
| 188 | + IPU6_FW_ISYS_FRAME_FORMAT_YUV422, /* 16 bit YUV 422, Y, U, V plane */ | |
| 189 | + IPU6_FW_ISYS_FRAME_FORMAT_YUV422_16, /* yuv422, 16 bits per subpixel */ | |
| 190 | + IPU6_FW_ISYS_FRAME_FORMAT_UYVY, /* 16 bit YUV 422, UYVY interleaved */ | |
| 191 | + IPU6_FW_ISYS_FRAME_FORMAT_YUYV, /* 16 bit YUV 422, YUYV interleaved */ | |
| 192 | + IPU6_FW_ISYS_FRAME_FORMAT_YUV444, /* 24 bit YUV 444, Y, U, V plane */ | |
| 193 | + /* Internal format, 2 y lines followed by a uvinterleaved line */ | |
| 194 | + IPU6_FW_ISYS_FRAME_FORMAT_YUV_LINE, | |
| 195 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW8, /* RAW8, 1 plane */ | |
| 196 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW10, /* RAW10, 1 plane */ | |
| 197 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW12, /* RAW12, 1 plane */ | |
| 198 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW14, /* RAW14, 1 plane */ | |
| 199 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW16, /* RAW16, 1 plane */ | |
| 200 | + /** | |
| 201 | + * 16 bit RGB, 1 plane. Each 3 sub pixels are packed into one 16 bit | |
| 202 | + * value, 5 bits for R, 6 bits for G and 5 bits for B. | |
| 203 | + */ | |
| 204 | + IPU6_FW_ISYS_FRAME_FORMAT_RGB565, | |
| 205 | + IPU6_FW_ISYS_FRAME_FORMAT_PLANAR_RGB888, /* 24 bit RGB, 3 planes */ | |
| 206 | + IPU6_FW_ISYS_FRAME_FORMAT_RGBA888, /* 32 bit RGBA, 1 plane, A=Alpha */ | |
| 207 | + IPU6_FW_ISYS_FRAME_FORMAT_QPLANE6, /* Internal, for advanced ISP */ | |
| 208 | + IPU6_FW_ISYS_FRAME_FORMAT_BINARY_8, /* byte stream, used for jpeg. */ | |
| 209 | + N_IPU6_FW_ISYS_FRAME_FORMAT | |
| 210 | +}; | |
| 211 | + | |
| 212 | +enum ipu6_fw_isys_pin_type { | |
| 213 | + /* captured as MIPI packets */ | |
| 214 | + IPU6_FW_ISYS_PIN_TYPE_MIPI = 0, | |
| 215 | + /* captured through the SoC path */ | |
| 216 | + IPU6_FW_ISYS_PIN_TYPE_RAW_SOC = 3, | |
| 217 | +}; | |
| 218 | + | |
| 219 | +/* | |
| 220 | + * enum ipu6_fw_isys_mipi_store_mode. Describes if long MIPI packets reach | |
| 221 | + * MIPI SRAM with the long packet header or | |
| 222 | + * if not, then only option is to capture it with pin type MIPI. | |
| 223 | + */ | |
| 224 | +enum ipu6_fw_isys_mipi_store_mode { | |
| 225 | + IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL = 0, | |
| 226 | + IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER, | |
| 227 | + N_IPU6_FW_ISYS_MIPI_STORE_MODE | |
| 228 | +}; | |
| 229 | + | |
| 230 | +enum ipu6_fw_isys_capture_mode { | |
| 231 | + IPU6_FW_ISYS_CAPTURE_MODE_REGULAR = 0, | |
| 232 | + IPU6_FW_ISYS_CAPTURE_MODE_BURST, | |
| 233 | + N_IPU6_FW_ISYS_CAPTURE_MODE, | |
| 234 | +}; | |
| 235 | + | |
| 236 | +enum ipu6_fw_isys_sensor_mode { | |
| 237 | + IPU6_FW_ISYS_SENSOR_MODE_NORMAL = 0, | |
| 238 | + IPU6_FW_ISYS_SENSOR_MODE_TOBII, | |
| 239 | + N_IPU6_FW_ISYS_SENSOR_MODE, | |
| 240 | +}; | |
| 241 | + | |
| 242 | +enum ipu6_fw_isys_error { | |
| 243 | + IPU6_FW_ISYS_ERROR_NONE = 0, | |
| 244 | + IPU6_FW_ISYS_ERROR_FW_INTERNAL_CONSISTENCY, | |
| 245 | + IPU6_FW_ISYS_ERROR_HW_CONSISTENCY, | |
| 246 | + IPU6_FW_ISYS_ERROR_DRIVER_INVALID_COMMAND_SEQUENCE, | |
| 247 | + IPU6_FW_ISYS_ERROR_DRIVER_INVALID_DEVICE_CONFIGURATION, | |
| 248 | + IPU6_FW_ISYS_ERROR_DRIVER_INVALID_STREAM_CONFIGURATION, | |
| 249 | + IPU6_FW_ISYS_ERROR_DRIVER_INVALID_FRAME_CONFIGURATION, | |
| 250 | + IPU6_FW_ISYS_ERROR_INSUFFICIENT_RESOURCES, | |
| 251 | + IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO, | |
| 252 | + IPU6_FW_ISYS_ERROR_HW_REPORTED_SIG2CIO, | |
| 253 | + IPU6_FW_ISYS_ERROR_SENSOR_FW_SYNC, | |
| 254 | + IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION, | |
| 255 | + IPU6_FW_ISYS_ERROR_RESPONSE_QUEUE_FULL, | |
| 256 | + N_IPU6_FW_ISYS_ERROR | |
| 257 | +}; | |
| 258 | + | |
| 259 | +enum ipu6_fw_proxy_error { | |
| 260 | + IPU6_FW_PROXY_ERROR_NONE = 0, | |
| 261 | + IPU6_FW_PROXY_ERROR_INVALID_WRITE_REGION, | |
| 262 | + IPU6_FW_PROXY_ERROR_INVALID_WRITE_OFFSET, | |
| 263 | + N_IPU6_FW_PROXY_ERROR | |
| 264 | +}; | |
| 265 | + | |
| 266 | +/* firmware ABI structure below are aligned in firmware, no need pack */ | |
| 267 | +struct ipu6_fw_isys_buffer_partition_abi { | |
| 268 | + u32 num_gda_pages[IPU6_STREAM_ID_MAX]; | |
| 269 | +}; | |
| 270 | + | |
| 271 | +struct ipu6_fw_isys_fw_config { | |
| 272 | + struct ipu6_fw_isys_buffer_partition_abi buffer_partition; | |
| 273 | + u32 num_send_queues[N_IPU6_FW_ISYS_QUEUE_TYPE]; | |
| 274 | + u32 num_recv_queues[N_IPU6_FW_ISYS_QUEUE_TYPE]; | |
| 275 | +}; | |
| 276 | + | |
| 277 | +/* | |
| 278 | + * struct ipu6_fw_isys_resolution_abi: Generic resolution structure. | |
| 279 | + */ | |
| 280 | +struct ipu6_fw_isys_resolution_abi { | |
| 281 | + u32 width; | |
| 282 | + u32 height; | |
| 283 | +}; | |
| 284 | + | |
| 285 | +/** | |
| 286 | + * struct ipu6_fw_isys_output_pin_payload_abi - ISYS output pin buffer | |
| 287 | + * @out_buf_id: Points to output pin buffer - buffer identifier | |
| 288 | + * @addr: Points to output pin buffer - CSS Virtual Address | |
| 289 | + * @compress: Request frame compression (1), or not (0) | |
| 290 | + */ | |
| 291 | +struct ipu6_fw_isys_output_pin_payload_abi { | |
| 292 | + u64 out_buf_id; | |
| 293 | + u32 addr; | |
| 294 | + u32 compress; | |
| 295 | +}; | |
| 296 | + | |
| 297 | +/** | |
| 298 | + * struct ipu6_fw_isys_output_pin_info_abi - ISYS output pin info | |
| 299 | + * @output_res: output pin resolution | |
| 300 | + * @stride: output stride in Bytes (not valid for statistics) | |
| 301 | + * @watermark_in_lines: pin watermark level in lines | |
| 302 | + * @payload_buf_size: minimum size in Bytes of all buffers that will be | |
| 303 | + * supplied for capture on this pin | |
| 304 | + * @ts_offsets: ts_offsets | |
| 305 | + * @s2m_pixel_soc_pixel_remapping: pixel soc remapping (see the definition of | |
| 306 | + * S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING) | |
| 307 | + * @csi_be_soc_pixel_remapping: see s2m_pixel_soc_pixel_remapping | |
| 308 | + * @send_irq: assert if pin event should trigger irq | |
| 309 | + * @input_pin_id: related input pin id | |
| 310 | + * @pt: pin type -real format "enum ipu6_fw_isys_pin_type" | |
| 311 | + * @ft: frame format type -real format "enum ipu6_fw_isys_frame_format_type" | |
| 312 | + * @reserved: a reserved field | |
| 313 | + * @reserve_compression: reserve compression resources for pin | |
| 314 | + * @snoopable: snoopable | |
| 315 | + * @error_handling_enable: enable error handling | |
| 316 | + * @sensor_type: sensor_type | |
| 317 | + */ | |
| 318 | +struct ipu6_fw_isys_output_pin_info_abi { | |
| 319 | + struct ipu6_fw_isys_resolution_abi output_res; | |
| 320 | + u32 stride; | |
| 321 | + u32 watermark_in_lines; | |
| 322 | + u32 payload_buf_size; | |
| 323 | + u32 ts_offsets[IPU6_PIN_PLANES_MAX]; | |
| 324 | + u32 s2m_pixel_soc_pixel_remapping; | |
| 325 | + u32 csi_be_soc_pixel_remapping; | |
| 326 | + u8 send_irq; | |
| 327 | + u8 input_pin_id; | |
| 328 | + u8 pt; | |
| 329 | + u8 ft; | |
| 330 | + u8 reserved; | |
| 331 | + u8 reserve_compression; | |
| 332 | + u8 snoopable; | |
| 333 | + u8 error_handling_enable; | |
| 334 | + u32 sensor_type; | |
| 335 | +}; | |
| 336 | + | |
| 337 | +/** | |
| 338 | + * struct ipu6_fw_isys_input_pin_info_abi - ISYS input pin info | |
| 339 | + * @input_res: input resolution | |
| 340 | + * @dt: mipi data type ((enum ipu6_fw_isys_mipi_data_type) | |
| 341 | + * @mipi_store_mode: defines if legacy long packet header will be stored or | |
| 342 | + * discarded if discarded, output pin type for this | |
| 343 | + * input pin can only be MIPI | |
| 344 | + * (enum ipu6_fw_isys_mipi_store_mode) | |
| 345 | + * @bits_per_pix: native bits per pixel | |
| 346 | + * @mapped_dt: actual data type from sensor | |
| 347 | + * @mipi_decompression: defines which compression will be in mipi backend | |
| 348 | + * @crop_first_and_last_lines: Control whether to crop the first and last line | |
| 349 | + * of the input image. Crop done by HW device. | |
| 350 | + * @capture_mode: mode of capture, regular or burst, default value is regular | |
| 351 | + * @reserved: a reserved field | |
| 352 | + */ | |
| 353 | +struct ipu6_fw_isys_input_pin_info_abi { | |
| 354 | + struct ipu6_fw_isys_resolution_abi input_res; | |
| 355 | + u8 dt; | |
| 356 | + u8 mipi_store_mode; | |
| 357 | + u8 bits_per_pix; | |
| 358 | + u8 mapped_dt; | |
| 359 | + u8 mipi_decompression; | |
| 360 | + u8 crop_first_and_last_lines; | |
| 361 | + u8 capture_mode; | |
| 362 | + u8 reserved; | |
| 363 | +}; | |
| 364 | + | |
| 365 | +/** | |
| 366 | + * struct ipu6_fw_isys_cropping_abi - ISYS cropping coordinates | |
| 367 | + * @top_offset: Top offset | |
| 368 | + * @left_offset: Left offset | |
| 369 | + * @bottom_offset: Bottom offset | |
| 370 | + * @right_offset: Right offset | |
| 371 | + */ | |
| 372 | +struct ipu6_fw_isys_cropping_abi { | |
| 373 | + s32 top_offset; | |
| 374 | + s32 left_offset; | |
| 375 | + s32 bottom_offset; | |
| 376 | + s32 right_offset; | |
| 377 | +}; | |
| 378 | + | |
| 379 | +/** | |
| 380 | + * struct ipu6_fw_isys_stream_cfg_data_abi - ISYS stream configuration data | |
| 381 | + * ISYS stream configuration data structure | |
| 382 | + * @crop: for extended use and is not used in FW currently | |
| 383 | + * @input_pins: input pin descriptors | |
| 384 | + * @output_pins: output pin descriptors | |
| 385 | + * @compfmt: de-compression setting for User Defined Data | |
| 386 | + * @nof_input_pins: number of input pins | |
| 387 | + * @nof_output_pins: number of output pins | |
| 388 | + * @send_irq_sof_discarded: send irq on discarded frame sof response | |
| 389 | + * - if '1' it will override the send_resp_sof_discarded | |
| 390 | + * and send the response | |
| 391 | + * - if '0' the send_resp_sof_discarded will determine | |
| 392 | + * whether to send the response | |
| 393 | + * @send_irq_eof_discarded: send irq on discarded frame eof response | |
| 394 | + * - if '1' it will override the send_resp_eof_discarded | |
| 395 | + * and send the response | |
| 396 | + * - if '0' the send_resp_eof_discarded will determine | |
| 397 | + * whether to send the response | |
| 398 | + * @send_resp_sof_discarded: send response for discarded frame sof detected, | |
| 399 | + * used only when send_irq_sof_discarded is '0' | |
| 400 | + * @send_resp_eof_discarded: send response for discarded frame eof detected, | |
| 401 | + * used only when send_irq_eof_discarded is '0' | |
| 402 | + * @src: Stream source index e.g. MIPI_generator_0, CSI2-rx_1 | |
| 403 | + * @vc: MIPI Virtual Channel (up to 4 virtual per physical channel) | |
| 404 | + * @isl_use: indicates whether stream requires ISL and how | |
| 405 | + * @sensor_type: type of connected sensor, tobii or others, default is 0 | |
| 406 | + * @reserved: a reserved field | |
| 407 | + * @reserved2: a reserved field | |
| 408 | + */ | |
| 409 | +struct ipu6_fw_isys_stream_cfg_data_abi { | |
| 410 | + struct ipu6_fw_isys_cropping_abi crop; | |
| 411 | + struct ipu6_fw_isys_input_pin_info_abi input_pins[IPU6_MAX_IPINS]; | |
| 412 | + struct ipu6_fw_isys_output_pin_info_abi output_pins[IPU6_MAX_OPINS]; | |
| 413 | + u32 compfmt; | |
| 414 | + u8 nof_input_pins; | |
| 415 | + u8 nof_output_pins; | |
| 416 | + u8 send_irq_sof_discarded; | |
| 417 | + u8 send_irq_eof_discarded; | |
| 418 | + u8 send_resp_sof_discarded; | |
| 419 | + u8 send_resp_eof_discarded; | |
| 420 | + u8 src; | |
| 421 | + u8 vc; | |
| 422 | + u8 isl_use; | |
| 423 | + u8 sensor_type; | |
| 424 | + u8 reserved; | |
| 425 | + u8 reserved2; | |
| 426 | +}; | |
| 427 | + | |
| 428 | +/** | |
| 429 | + * struct ipu6_fw_isys_frame_buff_set_abi - ISYS frame buffer set (request) | |
| 430 | + * @output_pins: output pin addresses | |
| 431 | + * @send_irq_sof: send irq on frame sof response | |
| 432 | + * - if '1' it will override the send_resp_sof and | |
| 433 | + * send the response | |
| 434 | + * - if '0' the send_resp_sof will determine whether to | |
| 435 | + * send the response | |
| 436 | + * @send_irq_eof: send irq on frame eof response | |
| 437 | + * - if '1' it will override the send_resp_eof and | |
| 438 | + * send the response | |
| 439 | + * - if '0' the send_resp_eof will determine whether to | |
| 440 | + * send the response | |
| 441 | + * @send_irq_capture_ack: send irq on capture ack | |
| 442 | + * @send_irq_capture_done: send irq on capture done | |
| 443 | + * @send_resp_sof: send response for frame sof detected, | |
| 444 | + * used only when send_irq_sof is '0' | |
| 445 | + * @send_resp_eof: send response for frame eof detected, | |
| 446 | + * used only when send_irq_eof is '0' | |
| 447 | + * @send_resp_capture_ack: send response for capture ack event | |
| 448 | + * @send_resp_capture_done: send response for capture done event | |
| 449 | + * @reserved: a reserved field | |
| 450 | + */ | |
| 451 | +struct ipu6_fw_isys_frame_buff_set_abi { | |
| 452 | + struct ipu6_fw_isys_output_pin_payload_abi output_pins[IPU6_MAX_OPINS]; | |
| 453 | + u8 send_irq_sof; | |
| 454 | + u8 send_irq_eof; | |
| 455 | + u8 send_irq_capture_ack; | |
| 456 | + u8 send_irq_capture_done; | |
| 457 | + u8 send_resp_sof; | |
| 458 | + u8 send_resp_eof; | |
| 459 | + u8 send_resp_capture_ack; | |
| 460 | + u8 send_resp_capture_done; | |
| 461 | + u8 reserved[8]; | |
| 462 | +}; | |
| 463 | + | |
| 464 | +/** | |
| 465 | + * struct ipu6_fw_isys_error_info_abi - ISYS error information | |
| 466 | + * @error: error code if something went wrong | |
| 467 | + * @error_details: depending on error code, it may contain additional error info | |
| 468 | + */ | |
| 469 | +struct ipu6_fw_isys_error_info_abi { | |
| 470 | + u32 error; | |
| 471 | + u32 error_details; | |
| 472 | +}; | |
| 473 | + | |
| 474 | +/** | |
| 475 | + * struct ipu6_fw_isys_resp_info_abi - ISYS firmware response | |
| 476 | + * @buf_id: buffer ID | |
| 477 | + * @pin: this var is only valid for pin event related responses, | |
| 478 | + * contains pin addresses | |
| 479 | + * @error_info: error information from the FW | |
| 480 | + * @timestamp: Time information for event if available | |
| 481 | + * @stream_handle: stream id the response corresponds to | |
| 482 | + * @type: response type (enum ipu6_fw_isys_resp_type) | |
| 483 | + * @pin_id: pin id that the pin payload corresponds to | |
| 484 | + * @reserved: a reserved field | |
| 485 | + * @reserved2: a reserved field | |
| 486 | + */ | |
| 487 | +struct ipu6_fw_isys_resp_info_abi { | |
| 488 | + u64 buf_id; | |
| 489 | + struct ipu6_fw_isys_output_pin_payload_abi pin; | |
| 490 | + struct ipu6_fw_isys_error_info_abi error_info; | |
| 491 | + u32 timestamp[2]; | |
| 492 | + u8 stream_handle; | |
| 493 | + u8 type; | |
| 494 | + u8 pin_id; | |
| 495 | + u8 reserved; | |
| 496 | + u32 reserved2; | |
| 497 | +}; | |
| 498 | + | |
| 499 | +/** | |
| 500 | + * struct ipu6_fw_isys_proxy_error_info_abi - ISYS proxy error | |
| 501 | + * @error: error code if something went wrong | |
| 502 | + * @error_details: depending on error code, it may contain additional error info | |
| 503 | + */ | |
| 504 | +struct ipu6_fw_isys_proxy_error_info_abi { | |
| 505 | + u32 error; | |
| 506 | + u32 error_details; | |
| 507 | +}; | |
| 508 | + | |
| 509 | +struct ipu6_fw_isys_proxy_resp_info_abi { | |
| 510 | + u32 request_id; | |
| 511 | + struct ipu6_fw_isys_proxy_error_info_abi error_info; | |
| 512 | +}; | |
| 513 | + | |
| 514 | +/** | |
| 515 | + * struct ipu6_fw_proxy_write_queue_token - ISYS proxy write queue token | |
| 516 | + * @request_id: update id for the specific proxy write request | |
| 517 | + * @region_index: Region id for the proxy write request | |
| 518 | + * @offset: Offset of the write request according to the base address | |
| 519 | + * of the region | |
| 520 | + * @value: Value that is requested to be written with the proxy write request | |
| 521 | + */ | |
| 522 | +struct ipu6_fw_proxy_write_queue_token { | |
| 523 | + u32 request_id; | |
| 524 | + u32 region_index; | |
| 525 | + u32 offset; | |
| 526 | + u32 value; | |
| 527 | +}; | |
| 528 | + | |
| 529 | +/** | |
| 530 | + * struct ipu6_fw_resp_queue_token - ISYS response queue token | |
| 531 | + * @resp_info: response info | |
| 532 | + */ | |
| 533 | +struct ipu6_fw_resp_queue_token { | |
| 534 | + struct ipu6_fw_isys_resp_info_abi resp_info; | |
| 535 | +}; | |
| 536 | + | |
| 537 | +/** | |
| 538 | + * struct ipu6_fw_send_queue_token - ISYS send queue token | |
| 539 | + * @buf_handle: buffer handle | |
| 540 | + * @payload: payload | |
| 541 | + * @send_type: send_type | |
| 542 | + * @stream_id: stream_id | |
| 543 | + */ | |
| 544 | +struct ipu6_fw_send_queue_token { | |
| 545 | + u64 buf_handle; | |
| 546 | + u32 payload; | |
| 547 | + u16 send_type; | |
| 548 | + u16 stream_id; | |
| 549 | +}; | |
| 550 | + | |
| 551 | +/** | |
| 552 | + * struct ipu6_fw_proxy_resp_queue_token - ISYS proxy response queue token | |
| 553 | + * @proxy_resp_info: proxy response info | |
| 554 | + */ | |
| 555 | +struct ipu6_fw_proxy_resp_queue_token { | |
| 556 | + struct ipu6_fw_isys_proxy_resp_info_abi proxy_resp_info; | |
| 557 | +}; | |
| 558 | + | |
| 559 | +/** | |
| 560 | + * struct ipu6_fw_proxy_send_queue_token - SYS proxy send queue token | |
| 561 | + * @request_id: request_id | |
| 562 | + * @region_index: region_index | |
| 563 | + * @offset: offset | |
| 564 | + * @value: value | |
| 565 | + */ | |
| 566 | +struct ipu6_fw_proxy_send_queue_token { | |
| 567 | + u32 request_id; | |
| 568 | + u32 region_index; | |
| 569 | + u32 offset; | |
| 570 | + u32 value; | |
| 571 | +}; | |
| 572 | + | |
| 573 | +void | |
| 574 | +ipu6_fw_isys_dump_stream_cfg(struct device *dev, | |
| 575 | + struct ipu6_fw_isys_stream_cfg_data_abi *cfg); | |
| 576 | +void | |
| 577 | +ipu6_fw_isys_dump_frame_buff_set(struct device *dev, | |
| 578 | + struct ipu6_fw_isys_frame_buff_set_abi *buf, | |
| 579 | + unsigned int outputs); | |
| 580 | +int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams); | |
| 581 | +int ipu6_fw_isys_close(struct ipu6_isys *isys); | |
| 582 | +int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys, | |
| 583 | + const unsigned int stream_handle, u16 send_type); | |
| 584 | +int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys, | |
| 585 | + const unsigned int stream_handle, | |
| 586 | + void *cpu_mapped_buf, dma_addr_t dma_mapped_buf, | |
| 587 | + size_t size, u16 send_type); | |
| 588 | +int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys, | |
| 589 | + unsigned int req_id, | |
| 590 | + unsigned int index, | |
| 591 | + unsigned int offset, u32 value); | |
| 592 | +void ipu6_fw_isys_cleanup(struct ipu6_isys *isys); | |
| 593 | +struct ipu6_fw_isys_resp_info_abi * | |
| 594 | +ipu6_fw_isys_get_resp(void *context, unsigned int queue); | |
| 595 | +void ipu6_fw_isys_put_resp(void *context, unsigned int queue); | |
| 596 | +#endif | |
| @@ -0,0 +1,663 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/atomic.h> | |
| 7 | +#include <linux/bitfield.h> | |
| 8 | +#include <linux/bits.h> | |
| 9 | +#include <linux/delay.h> | |
| 10 | +#include <linux/device.h> | |
| 11 | +#include <linux/err.h> | |
| 12 | +#include <linux/io.h> | |
| 13 | +#include <linux/minmax.h> | |
| 14 | +#include <linux/sprintf.h> | |
| 15 | + | |
| 16 | +#include <media/media-entity.h> | |
| 17 | +#include <media/v4l2-ctrls.h> | |
| 18 | +#include <media/v4l2-device.h> | |
| 19 | +#include <media/v4l2-event.h> | |
| 20 | +#include <media/v4l2-subdev.h> | |
| 21 | + | |
| 22 | +#include "ipu6-bus.h" | |
| 23 | +#include "ipu6-isys.h" | |
| 24 | +#include "ipu6-isys-csi2.h" | |
| 25 | +#include "ipu6-isys-subdev.h" | |
| 26 | +#include "ipu6-platform-isys-csi2-reg.h" | |
| 27 | + | |
| 28 | +static const u32 csi2_supported_codes[] = { | |
| 29 | + MEDIA_BUS_FMT_RGB565_1X16, | |
| 30 | + MEDIA_BUS_FMT_RGB888_1X24, | |
| 31 | + MEDIA_BUS_FMT_UYVY8_1X16, | |
| 32 | + MEDIA_BUS_FMT_YUYV8_1X16, | |
| 33 | + MEDIA_BUS_FMT_SBGGR10_1X10, | |
| 34 | + MEDIA_BUS_FMT_SGBRG10_1X10, | |
| 35 | + MEDIA_BUS_FMT_SGRBG10_1X10, | |
| 36 | + MEDIA_BUS_FMT_SRGGB10_1X10, | |
| 37 | + MEDIA_BUS_FMT_SBGGR12_1X12, | |
| 38 | + MEDIA_BUS_FMT_SGBRG12_1X12, | |
| 39 | + MEDIA_BUS_FMT_SGRBG12_1X12, | |
| 40 | + MEDIA_BUS_FMT_SRGGB12_1X12, | |
| 41 | + MEDIA_BUS_FMT_SBGGR8_1X8, | |
| 42 | + MEDIA_BUS_FMT_SGBRG8_1X8, | |
| 43 | + MEDIA_BUS_FMT_SGRBG8_1X8, | |
| 44 | + MEDIA_BUS_FMT_SRGGB8_1X8, | |
| 45 | + MEDIA_BUS_FMT_META_8, | |
| 46 | + MEDIA_BUS_FMT_META_10, | |
| 47 | + MEDIA_BUS_FMT_META_12, | |
| 48 | + MEDIA_BUS_FMT_META_16, | |
| 49 | + MEDIA_BUS_FMT_META_24, | |
| 50 | + 0 | |
| 51 | +}; | |
| 52 | + | |
| 53 | +/* | |
| 54 | + * Strings corresponding to CSI-2 receiver errors are here. | |
| 55 | + * Corresponding macros are defined in the header file. | |
| 56 | + */ | |
| 57 | +static const struct ipu6_csi2_error dphy_rx_errors[] = { | |
| 58 | + { "Single packet header error corrected", true }, | |
| 59 | + { "Multiple packet header errors detected", true }, | |
| 60 | + { "Payload checksum (CRC) error", true }, | |
| 61 | + { "Transfer FIFO overflow", false }, | |
| 62 | + { "Reserved short packet data type detected", true }, | |
| 63 | + { "Reserved long packet data type detected", true }, | |
| 64 | + { "Incomplete long packet detected", false }, | |
| 65 | + { "Frame sync error", false }, | |
| 66 | + { "Line sync error", false }, | |
| 67 | + { "DPHY recoverable synchronization error", true }, | |
| 68 | + { "DPHY fatal error", false }, | |
| 69 | + { "DPHY elastic FIFO overflow", false }, | |
| 70 | + { "Inter-frame short packet discarded", true }, | |
| 71 | + { "Inter-frame long packet discarded", true }, | |
| 72 | + { "MIPI pktgen overflow", false }, | |
| 73 | + { "MIPI pktgen data loss", false }, | |
| 74 | + { "FIFO overflow", false }, | |
| 75 | + { "Lane deskew", false }, | |
| 76 | + { "SOT sync error", false }, | |
| 77 | + { "HSIDLE detected", false } | |
| 78 | +}; | |
| 79 | + | |
| 80 | +s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2) | |
| 81 | +{ | |
| 82 | + struct media_pad *src_pad; | |
| 83 | + struct v4l2_subdev *ext_sd; | |
| 84 | + struct device *dev; | |
| 85 | + | |
| 86 | + if (!csi2) | |
| 87 | + return -EINVAL; | |
| 88 | + | |
| 89 | + dev = &csi2->isys->adev->auxdev.dev; | |
| 90 | + src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); | |
| 91 | + if (IS_ERR(src_pad)) { | |
| 92 | + dev_err(dev, "can't get source pad of %s (%ld)\n", | |
| 93 | + csi2->asd.sd.name, PTR_ERR(src_pad)); | |
| 94 | + return PTR_ERR(src_pad); | |
| 95 | + } | |
| 96 | + | |
| 97 | + ext_sd = media_entity_to_v4l2_subdev(src_pad->entity); | |
| 98 | + if (WARN(!ext_sd, "Failed to get subdev for %s\n", csi2->asd.sd.name)) | |
| 99 | + return -ENODEV; | |
| 100 | + | |
| 101 | + return v4l2_get_link_freq(ext_sd->ctrl_handler, 0, 0); | |
| 102 | +} | |
| 103 | + | |
| 104 | +static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, | |
| 105 | + struct v4l2_event_subscription *sub) | |
| 106 | +{ | |
| 107 | + struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); | |
| 108 | + struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); | |
| 109 | + struct device *dev = &csi2->isys->adev->auxdev.dev; | |
| 110 | + | |
| 111 | + dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n", | |
| 112 | + sub->type, sub->id); | |
| 113 | + | |
| 114 | + switch (sub->type) { | |
| 115 | + case V4L2_EVENT_FRAME_SYNC: | |
| 116 | + return v4l2_event_subscribe(fh, sub, 10, NULL); | |
| 117 | + case V4L2_EVENT_CTRL: | |
| 118 | + return v4l2_ctrl_subscribe_event(fh, sub); | |
| 119 | + default: | |
| 120 | + return -EINVAL; | |
| 121 | + } | |
| 122 | +} | |
| 123 | + | |
| 124 | +static const struct v4l2_subdev_core_ops csi2_sd_core_ops = { | |
| 125 | + .subscribe_event = csi2_subscribe_event, | |
| 126 | + .unsubscribe_event = v4l2_event_subdev_unsubscribe, | |
| 127 | +}; | |
| 128 | + | |
| 129 | +/* | |
| 130 | + * The input system CSI2+ receiver has several | |
| 131 | + * parameters affecting the receiver timings. These depend | |
| 132 | + * on the MIPI bus frequency F in Hz (sensor transmitter rate) | |
| 133 | + * as follows: | |
| 134 | + * register value = (A/1e9 + B * UI) / COUNT_ACC | |
| 135 | + * where | |
| 136 | + * UI = 1 / (2 * F) in seconds | |
| 137 | + * COUNT_ACC = counter accuracy in seconds | |
| 138 | + * COUNT_ACC = 0.125 ns = 1 / 8 ns, ACCINV = 8. | |
| 139 | + * | |
| 140 | + * A and B are coefficients from the table below, | |
| 141 | + * depending whether the register minimum or maximum value is | |
| 142 | + * calculated. | |
| 143 | + * Minimum Maximum | |
| 144 | + * Clock lane A B A B | |
| 145 | + * reg_rx_csi_dly_cnt_termen_clane 0 0 38 0 | |
| 146 | + * reg_rx_csi_dly_cnt_settle_clane 95 -8 300 -16 | |
| 147 | + * Data lanes | |
| 148 | + * reg_rx_csi_dly_cnt_termen_dlane0 0 0 35 4 | |
| 149 | + * reg_rx_csi_dly_cnt_settle_dlane0 85 -2 145 -6 | |
| 150 | + * reg_rx_csi_dly_cnt_termen_dlane1 0 0 35 4 | |
| 151 | + * reg_rx_csi_dly_cnt_settle_dlane1 85 -2 145 -6 | |
| 152 | + * reg_rx_csi_dly_cnt_termen_dlane2 0 0 35 4 | |
| 153 | + * reg_rx_csi_dly_cnt_settle_dlane2 85 -2 145 -6 | |
| 154 | + * reg_rx_csi_dly_cnt_termen_dlane3 0 0 35 4 | |
| 155 | + * reg_rx_csi_dly_cnt_settle_dlane3 85 -2 145 -6 | |
| 156 | + * | |
| 157 | + * We use the minimum values of both A and B. | |
| 158 | + */ | |
| 159 | + | |
| 160 | +#define DIV_SHIFT 8 | |
| 161 | +#define CSI2_ACCINV 8 | |
| 162 | + | |
| 163 | +static u32 calc_timing(s32 a, s32 b, s64 link_freq, s32 accinv) | |
| 164 | +{ | |
| 165 | + return accinv * a + (accinv * b * (500000000 >> DIV_SHIFT) | |
| 166 | + / (s32)(link_freq >> DIV_SHIFT)); | |
| 167 | +} | |
| 168 | + | |
| 169 | +static int | |
| 170 | +ipu6_isys_csi2_calc_timing(struct ipu6_isys_csi2 *csi2, | |
| 171 | + struct ipu6_isys_csi2_timing *timing, s32 accinv) | |
| 172 | +{ | |
| 173 | + struct device *dev = &csi2->isys->adev->auxdev.dev; | |
| 174 | + s64 link_freq; | |
| 175 | + | |
| 176 | + link_freq = ipu6_isys_csi2_get_link_freq(csi2); | |
| 177 | + if (link_freq < 0) | |
| 178 | + return link_freq; | |
| 179 | + | |
| 180 | + timing->ctermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A, | |
| 181 | + CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B, | |
| 182 | + link_freq, accinv); | |
| 183 | + timing->csettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A, | |
| 184 | + CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B, | |
| 185 | + link_freq, accinv); | |
| 186 | + timing->dtermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A, | |
| 187 | + CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B, | |
| 188 | + link_freq, accinv); | |
| 189 | + timing->dsettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A, | |
| 190 | + CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B, | |
| 191 | + link_freq, accinv); | |
| 192 | + | |
| 193 | + dev_dbg(dev, "ctermen %u csettle %u dtermen %u dsettle %u\n", | |
| 194 | + timing->ctermen, timing->csettle, | |
| 195 | + timing->dtermen, timing->dsettle); | |
| 196 | + | |
| 197 | + return 0; | |
| 198 | +} | |
| 199 | + | |
| 200 | +void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2) | |
| 201 | +{ | |
| 202 | + u32 irq = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + | |
| 203 | + CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET); | |
| 204 | + struct ipu6_isys *isys = csi2->isys; | |
| 205 | + u32 mask; | |
| 206 | + | |
| 207 | + mask = isys->pdata->ipdata->csi2.irq_mask; | |
| 208 | + writel(irq & mask, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + | |
| 209 | + CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); | |
| 210 | + csi2->receiver_errors |= irq & mask; | |
| 211 | +} | |
| 212 | + | |
| 213 | +void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2) | |
| 214 | +{ | |
| 215 | + struct device *dev = &csi2->isys->adev->auxdev.dev; | |
| 216 | + const struct ipu6_csi2_error *errors; | |
| 217 | + u32 status; | |
| 218 | + u32 i; | |
| 219 | + | |
| 220 | + /* register errors once more in case of interrupts are disabled */ | |
| 221 | + ipu6_isys_register_errors(csi2); | |
| 222 | + status = csi2->receiver_errors; | |
| 223 | + csi2->receiver_errors = 0; | |
| 224 | + errors = dphy_rx_errors; | |
| 225 | + | |
| 226 | + for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) { | |
| 227 | + if (status & BIT(i)) | |
| 228 | + dev_err_ratelimited(dev, "csi2-%i error: %s\n", | |
| 229 | + csi2->port, errors[i].error_string); | |
| 230 | + } | |
| 231 | +} | |
| 232 | + | |
| 233 | +static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd, | |
| 234 | + const struct ipu6_isys_csi2_timing *timing, | |
| 235 | + unsigned int nlanes, int enable) | |
| 236 | +{ | |
| 237 | + struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); | |
| 238 | + struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); | |
| 239 | + struct ipu6_isys *isys = csi2->isys; | |
| 240 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 241 | + struct ipu6_isys_csi2_config cfg; | |
| 242 | + unsigned int nports; | |
| 243 | + int ret = 0; | |
| 244 | + u32 mask = 0; | |
| 245 | + u32 i; | |
| 246 | + | |
| 247 | + dev_dbg(dev, "stream %s CSI2-%u with %u lanes\n", enable ? "on" : "off", | |
| 248 | + csi2->port, nlanes); | |
| 249 | + | |
| 250 | + cfg.port = csi2->port; | |
| 251 | + cfg.nlanes = nlanes; | |
| 252 | + | |
| 253 | + mask = isys->pdata->ipdata->csi2.irq_mask; | |
| 254 | + nports = isys->pdata->ipdata->csi2.nports; | |
| 255 | + | |
| 256 | + if (!enable) { | |
| 257 | + writel(0, csi2->base + CSI_REG_CSI_FE_ENABLE); | |
| 258 | + writel(0, csi2->base + CSI_REG_PPI2CSI_ENABLE); | |
| 259 | + | |
| 260 | + writel(0, | |
| 261 | + csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + | |
| 262 | + CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET); | |
| 263 | + writel(mask, | |
| 264 | + csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + | |
| 265 | + CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); | |
| 266 | + writel(0, | |
| 267 | + csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + | |
| 268 | + CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET); | |
| 269 | + writel(0xffffffff, | |
| 270 | + csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + | |
| 271 | + CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); | |
| 272 | + | |
| 273 | + isys->phy_set_power(isys, &cfg, timing, false); | |
| 274 | + | |
| 275 | + writel(0, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT | |
| 276 | + (isys->pdata->ipdata->csi2.fw_access_port_ofs, | |
| 277 | + csi2->port)); | |
| 278 | + writel(0, isys->pdata->base + | |
| 279 | + CSI_REG_HUB_DRV_ACCESS_PORT(csi2->port)); | |
| 280 | + | |
| 281 | + return ret; | |
| 282 | + } | |
| 283 | + | |
| 284 | + /* reset port reset */ | |
| 285 | + writel(0x1, csi2->base + CSI_REG_PORT_GPREG_SRST); | |
| 286 | + usleep_range(100, 200); | |
| 287 | + writel(0x0, csi2->base + CSI_REG_PORT_GPREG_SRST); | |
| 288 | + | |
| 289 | + /* enable port clock */ | |
| 290 | + for (i = 0; i < nports; i++) { | |
| 291 | + writel(1, isys->pdata->base + CSI_REG_HUB_DRV_ACCESS_PORT(i)); | |
| 292 | + writel(1, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT | |
| 293 | + (isys->pdata->ipdata->csi2.fw_access_port_ofs, i)); | |
| 294 | + } | |
| 295 | + | |
| 296 | + /* enable all error related irq */ | |
| 297 | + writel(mask, | |
| 298 | + csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + | |
| 299 | + CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET); | |
| 300 | + writel(mask, | |
| 301 | + csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + | |
| 302 | + CSI_PORT_REG_BASE_IRQ_MASK_OFFSET); | |
| 303 | + writel(mask, | |
| 304 | + csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + | |
| 305 | + CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); | |
| 306 | + writel(mask, | |
| 307 | + csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + | |
| 308 | + CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET); | |
| 309 | + writel(mask, | |
| 310 | + csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + | |
| 311 | + CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET); | |
| 312 | + | |
| 313 | + /* | |
| 314 | + * Using event from firmware instead of irq to handle CSI2 sync event | |
| 315 | + * which can reduce system wakeups. If CSI2 sync irq enabled, we need | |
| 316 | + * disable the firmware CSI2 sync event to avoid duplicate handling. | |
| 317 | + */ | |
| 318 | + writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + | |
| 319 | + CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET); | |
| 320 | + writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + | |
| 321 | + CSI_PORT_REG_BASE_IRQ_MASK_OFFSET); | |
| 322 | + writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + | |
| 323 | + CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); | |
| 324 | + writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + | |
| 325 | + CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET); | |
| 326 | + writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + | |
| 327 | + CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET); | |
| 328 | + | |
| 329 | + /* configure to enable FE and PPI2CSI */ | |
| 330 | + writel(0, csi2->base + CSI_REG_CSI_FE_MODE); | |
| 331 | + writel(CSI_SENSOR_INPUT, csi2->base + CSI_REG_CSI_FE_MUX_CTRL); | |
| 332 | + writel(CSI_CNTR_SENSOR_LINE_ID | CSI_CNTR_SENSOR_FRAME_ID, | |
| 333 | + csi2->base + CSI_REG_CSI_FE_SYNC_CNTR_SEL); | |
| 334 | + writel(FIELD_PREP(PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK, nlanes - 1), | |
| 335 | + csi2->base + CSI_REG_PPI2CSI_CONFIG_PPI_INTF); | |
| 336 | + | |
| 337 | + writel(1, csi2->base + CSI_REG_PPI2CSI_ENABLE); | |
| 338 | + writel(1, csi2->base + CSI_REG_CSI_FE_ENABLE); | |
| 339 | + | |
| 340 | + ret = isys->phy_set_power(isys, &cfg, timing, true); | |
| 341 | + if (ret) | |
| 342 | + dev_err(dev, "csi-%d phy power up failed %d\n", csi2->port, | |
| 343 | + ret); | |
| 344 | + | |
| 345 | + return ret; | |
| 346 | +} | |
| 347 | + | |
| 348 | +static int set_stream(struct v4l2_subdev *sd, int enable) | |
| 349 | +{ | |
| 350 | + struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); | |
| 351 | + struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); | |
| 352 | + struct device *dev = &csi2->isys->adev->auxdev.dev; | |
| 353 | + struct ipu6_isys_csi2_timing timing = { }; | |
| 354 | + unsigned int nlanes; | |
| 355 | + int ret; | |
| 356 | + | |
| 357 | + dev_dbg(dev, "csi2 stream %s callback\n", enable ? "on" : "off"); | |
| 358 | + | |
| 359 | + if (!enable) { | |
| 360 | + csi2->stream_count--; | |
| 361 | + if (csi2->stream_count) | |
| 362 | + return 0; | |
| 363 | + | |
| 364 | + ipu6_isys_csi2_set_stream(sd, &timing, 0, enable); | |
| 365 | + return 0; | |
| 366 | + } | |
| 367 | + | |
| 368 | + if (csi2->stream_count) { | |
| 369 | + csi2->stream_count++; | |
| 370 | + return 0; | |
| 371 | + } | |
| 372 | + | |
| 373 | + nlanes = csi2->nlanes; | |
| 374 | + | |
| 375 | + ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV); | |
| 376 | + if (ret) | |
| 377 | + return ret; | |
| 378 | + | |
| 379 | + ret = ipu6_isys_csi2_set_stream(sd, &timing, nlanes, enable); | |
| 380 | + if (ret) | |
| 381 | + return ret; | |
| 382 | + | |
| 383 | + csi2->stream_count++; | |
| 384 | + | |
| 385 | + return 0; | |
| 386 | +} | |
| 387 | + | |
| 388 | +static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd, | |
| 389 | + struct v4l2_subdev_state *state, | |
| 390 | + struct v4l2_subdev_selection *sel) | |
| 391 | +{ | |
| 392 | + struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); | |
| 393 | + struct device *dev = &asd->isys->adev->auxdev.dev; | |
| 394 | + struct v4l2_mbus_framefmt *sink_ffmt; | |
| 395 | + struct v4l2_mbus_framefmt *src_ffmt; | |
| 396 | + struct v4l2_rect *crop; | |
| 397 | + | |
| 398 | + if (sel->pad == CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) | |
| 399 | + return -EINVAL; | |
| 400 | + | |
| 401 | + sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, | |
| 402 | + sel->pad, | |
| 403 | + sel->stream); | |
| 404 | + if (!sink_ffmt) | |
| 405 | + return -EINVAL; | |
| 406 | + | |
| 407 | + src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream); | |
| 408 | + if (!src_ffmt) | |
| 409 | + return -EINVAL; | |
| 410 | + | |
| 411 | + crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); | |
| 412 | + if (!crop) | |
| 413 | + return -EINVAL; | |
| 414 | + | |
| 415 | + /* Only vertical cropping is supported */ | |
| 416 | + sel->r.left = 0; | |
| 417 | + sel->r.width = sink_ffmt->width; | |
| 418 | + /* Non-bayer formats can't be single line cropped */ | |
| 419 | + if (!ipu6_isys_is_bayer_format(sink_ffmt->code)) | |
| 420 | + sel->r.top &= ~1; | |
| 421 | + sel->r.height = clamp(sel->r.height & ~1, IPU6_ISYS_MIN_HEIGHT, | |
| 422 | + sink_ffmt->height - sel->r.top); | |
| 423 | + *crop = sel->r; | |
| 424 | + | |
| 425 | + /* update source pad format */ | |
| 426 | + src_ffmt->width = sel->r.width; | |
| 427 | + src_ffmt->height = sel->r.height; | |
| 428 | + if (ipu6_isys_is_bayer_format(sink_ffmt->code)) | |
| 429 | + src_ffmt->code = ipu6_isys_convert_bayer_order(sink_ffmt->code, | |
| 430 | + sel->r.left, | |
| 431 | + sel->r.top); | |
| 432 | + dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n", | |
| 433 | + sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height, | |
| 434 | + src_ffmt->code); | |
| 435 | + | |
| 436 | + return 0; | |
| 437 | +} | |
| 438 | + | |
| 439 | +static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd, | |
| 440 | + struct v4l2_subdev_state *state, | |
| 441 | + struct v4l2_subdev_selection *sel) | |
| 442 | +{ | |
| 443 | + struct v4l2_mbus_framefmt *sink_ffmt; | |
| 444 | + struct v4l2_rect *crop; | |
| 445 | + int ret = 0; | |
| 446 | + | |
| 447 | + if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK) | |
| 448 | + return -EINVAL; | |
| 449 | + | |
| 450 | + sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, | |
| 451 | + sel->pad, | |
| 452 | + sel->stream); | |
| 453 | + if (!sink_ffmt) | |
| 454 | + return -EINVAL; | |
| 455 | + | |
| 456 | + crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); | |
| 457 | + if (!crop) | |
| 458 | + return -EINVAL; | |
| 459 | + | |
| 460 | + switch (sel->target) { | |
| 461 | + case V4L2_SEL_TGT_CROP_DEFAULT: | |
| 462 | + case V4L2_SEL_TGT_CROP_BOUNDS: | |
| 463 | + sel->r.left = 0; | |
| 464 | + sel->r.top = 0; | |
| 465 | + sel->r.width = sink_ffmt->width; | |
| 466 | + sel->r.height = sink_ffmt->height; | |
| 467 | + break; | |
| 468 | + case V4L2_SEL_TGT_CROP: | |
| 469 | + sel->r = *crop; | |
| 470 | + break; | |
| 471 | + default: | |
| 472 | + ret = -EINVAL; | |
| 473 | + } | |
| 474 | + | |
| 475 | + return ret; | |
| 476 | +} | |
| 477 | + | |
| 478 | +static const struct v4l2_subdev_video_ops csi2_sd_video_ops = { | |
| 479 | + .s_stream = set_stream, | |
| 480 | +}; | |
| 481 | + | |
| 482 | +static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { | |
| 483 | + .get_fmt = v4l2_subdev_get_fmt, | |
| 484 | + .set_fmt = ipu6_isys_subdev_set_fmt, | |
| 485 | + .get_selection = ipu6_isys_csi2_get_sel, | |
| 486 | + .set_selection = ipu6_isys_csi2_set_sel, | |
| 487 | + .enum_mbus_code = ipu6_isys_subdev_enum_mbus_code, | |
| 488 | + .set_routing = ipu6_isys_subdev_set_routing, | |
| 489 | +}; | |
| 490 | + | |
| 491 | +static const struct v4l2_subdev_ops csi2_sd_ops = { | |
| 492 | + .core = &csi2_sd_core_ops, | |
| 493 | + .video = &csi2_sd_video_ops, | |
| 494 | + .pad = &csi2_sd_pad_ops, | |
| 495 | +}; | |
| 496 | + | |
| 497 | +static const struct media_entity_operations csi2_entity_ops = { | |
| 498 | + .link_validate = v4l2_subdev_link_validate, | |
| 499 | + .has_pad_interdep = v4l2_subdev_has_pad_interdep, | |
| 500 | +}; | |
| 501 | + | |
| 502 | +void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2) | |
| 503 | +{ | |
| 504 | + if (!csi2->isys) | |
| 505 | + return; | |
| 506 | + | |
| 507 | + v4l2_device_unregister_subdev(&csi2->asd.sd); | |
| 508 | + v4l2_subdev_cleanup(&csi2->asd.sd); | |
| 509 | + ipu6_isys_subdev_cleanup(&csi2->asd); | |
| 510 | + csi2->isys = NULL; | |
| 511 | +} | |
| 512 | + | |
| 513 | +int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2, | |
| 514 | + struct ipu6_isys *isys, | |
| 515 | + void __iomem *base, unsigned int index) | |
| 516 | +{ | |
| 517 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 518 | + int ret; | |
| 519 | + | |
| 520 | + csi2->isys = isys; | |
| 521 | + csi2->base = base; | |
| 522 | + csi2->port = index; | |
| 523 | + | |
| 524 | + csi2->asd.sd.entity.ops = &csi2_entity_ops; | |
| 525 | + csi2->asd.isys = isys; | |
| 526 | + ret = ipu6_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0, | |
| 527 | + NR_OF_CSI2_SINK_PADS, NR_OF_CSI2_SRC_PADS); | |
| 528 | + if (ret) | |
| 529 | + goto fail; | |
| 530 | + | |
| 531 | + csi2->asd.source = IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 + index; | |
| 532 | + csi2->asd.supported_codes = csi2_supported_codes; | |
| 533 | + snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name), | |
| 534 | + IPU6_ISYS_ENTITY_PREFIX " CSI2 %u", index); | |
| 535 | + v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd); | |
| 536 | + ret = v4l2_subdev_init_finalize(&csi2->asd.sd); | |
| 537 | + if (ret) { | |
| 538 | + dev_err(dev, "failed to init v4l2 subdev\n"); | |
| 539 | + goto fail; | |
| 540 | + } | |
| 541 | + | |
| 542 | + ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd); | |
| 543 | + if (ret) { | |
| 544 | + dev_err(dev, "failed to register v4l2 subdev\n"); | |
| 545 | + goto fail; | |
| 546 | + } | |
| 547 | + | |
| 548 | + return 0; | |
| 549 | + | |
| 550 | +fail: | |
| 551 | + ipu6_isys_csi2_cleanup(csi2); | |
| 552 | + | |
| 553 | + return ret; | |
| 554 | +} | |
| 555 | + | |
| 556 | +void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream) | |
| 557 | +{ | |
| 558 | + struct video_device *vdev = stream->asd->sd.devnode; | |
| 559 | + struct device *dev = &stream->isys->adev->auxdev.dev; | |
| 560 | + struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd); | |
| 561 | + struct v4l2_event ev = { | |
| 562 | + .type = V4L2_EVENT_FRAME_SYNC, | |
| 563 | + }; | |
| 564 | + | |
| 565 | + ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); | |
| 566 | + v4l2_event_queue(vdev, &ev); | |
| 567 | + | |
| 568 | + dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n", | |
| 569 | + csi2->port, ev.u.frame_sync.frame_sequence, stream->vc); | |
| 570 | +} | |
| 571 | + | |
| 572 | +void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream) | |
| 573 | +{ | |
| 574 | + struct device *dev = &stream->isys->adev->auxdev.dev; | |
| 575 | + struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd); | |
| 576 | + u32 frame_sequence = atomic_read(&stream->sequence); | |
| 577 | + | |
| 578 | + dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n", | |
| 579 | + csi2->port, frame_sequence); | |
| 580 | +} | |
| 581 | + | |
| 582 | +int ipu6_isys_csi2_get_remote_desc(u32 source_stream, | |
| 583 | + struct ipu6_isys_csi2 *csi2, | |
| 584 | + struct media_entity *source_entity, | |
| 585 | + struct v4l2_mbus_frame_desc_entry *entry) | |
| 586 | +{ | |
| 587 | + struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; | |
| 588 | + struct device *dev = &csi2->isys->adev->auxdev.dev; | |
| 589 | + struct v4l2_mbus_frame_desc desc; | |
| 590 | + struct v4l2_subdev *source; | |
| 591 | + struct media_pad *pad; | |
| 592 | + unsigned int i; | |
| 593 | + int ret; | |
| 594 | + | |
| 595 | + source = media_entity_to_v4l2_subdev(source_entity); | |
| 596 | + if (!source) | |
| 597 | + return -EPIPE; | |
| 598 | + | |
| 599 | + pad = media_pad_remote_pad_first(&csi2->asd.pad[CSI2_PAD_SINK]); | |
| 600 | + if (!pad) | |
| 601 | + return -EPIPE; | |
| 602 | + | |
| 603 | + ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc); | |
| 604 | + if (ret) | |
| 605 | + return ret; | |
| 606 | + | |
| 607 | + if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { | |
| 608 | + dev_err(dev, "Unsupported frame descriptor type\n"); | |
| 609 | + return -EINVAL; | |
| 610 | + } | |
| 611 | + | |
| 612 | + for (i = 0; i < desc.num_entries; i++) { | |
| 613 | + if (source_stream == desc.entry[i].stream) { | |
| 614 | + desc_entry = &desc.entry[i]; | |
| 615 | + break; | |
| 616 | + } | |
| 617 | + } | |
| 618 | + | |
| 619 | + if (!desc_entry) { | |
| 620 | + dev_err(dev, "Failed to find stream %u from remote subdev\n", | |
| 621 | + source_stream); | |
| 622 | + return -EINVAL; | |
| 623 | + } | |
| 624 | + | |
| 625 | + if (desc_entry->bus.csi2.vc >= NR_OF_CSI2_VC) { | |
| 626 | + dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc); | |
| 627 | + return -EINVAL; | |
| 628 | + } | |
| 629 | + | |
| 630 | + *entry = *desc_entry; | |
| 631 | + | |
| 632 | + return 0; | |
| 633 | +} | |
| 634 | + | |
| 635 | +void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status) | |
| 636 | +{ | |
| 637 | + struct ipu6_isys_stream *stream = av->stream; | |
| 638 | + struct v4l2_subdev *sd = &stream->asd->sd; | |
| 639 | + struct v4l2_subdev_state *state; | |
| 640 | + struct media_pad *r_pad; | |
| 641 | + unsigned int i; | |
| 642 | + u32 r_stream; | |
| 643 | + | |
| 644 | + r_pad = media_pad_remote_pad_first(&av->pad); | |
| 645 | + r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index); | |
| 646 | + | |
| 647 | + state = v4l2_subdev_lock_and_get_active_state(sd); | |
| 648 | + | |
| 649 | + for (i = 0; i < state->stream_configs.num_configs; i++) { | |
| 650 | + struct v4l2_subdev_stream_config *cfg = | |
| 651 | + &state->stream_configs.configs[i]; | |
| 652 | + | |
| 653 | + if (cfg->pad == r_pad->index && r_stream == cfg->stream) { | |
| 654 | + dev_dbg(&av->isys->adev->auxdev.dev, | |
| 655 | + "%s: pad:%u, stream:%u, status:%u\n", | |
| 656 | + sd->entity.name, r_pad->index, r_stream, | |
| 657 | + status); | |
| 658 | + cfg->enabled = status; | |
| 659 | + } | |
| 660 | + } | |
| 661 | + | |
| 662 | + v4l2_subdev_unlock_state(state); | |
| 663 | +} | |
| @@ -0,0 +1,82 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2013--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_ISYS_CSI2_H | |
| 5 | +#define IPU6_ISYS_CSI2_H | |
| 6 | + | |
| 7 | +#include <linux/container_of.h> | |
| 8 | + | |
| 9 | +#include "ipu6-isys-subdev.h" | |
| 10 | +#include "ipu6-isys-video.h" | |
| 11 | + | |
| 12 | +struct media_entity; | |
| 13 | +struct v4l2_mbus_frame_desc_entry; | |
| 14 | + | |
| 15 | +struct ipu6_isys_video; | |
| 16 | +struct ipu6_isys; | |
| 17 | +struct ipu6_isys_csi2_pdata; | |
| 18 | +struct ipu6_isys_stream; | |
| 19 | + | |
| 20 | +#define NR_OF_CSI2_VC 16 | |
| 21 | +#define INVALID_VC_ID -1 | |
| 22 | +#define NR_OF_CSI2_SINK_PADS 1 | |
| 23 | +#define CSI2_PAD_SINK 0 | |
| 24 | +#define NR_OF_CSI2_SRC_PADS 8 | |
| 25 | +#define CSI2_PAD_SRC 1 | |
| 26 | +#define NR_OF_CSI2_PADS (NR_OF_CSI2_SINK_PADS + NR_OF_CSI2_SRC_PADS) | |
| 27 | + | |
| 28 | +#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A 0 | |
| 29 | +#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B 0 | |
| 30 | +#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A 95 | |
| 31 | +#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B -8 | |
| 32 | + | |
| 33 | +#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A 0 | |
| 34 | +#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B 0 | |
| 35 | +#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A 85 | |
| 36 | +#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B -2 | |
| 37 | + | |
| 38 | +struct ipu6_isys_csi2 { | |
| 39 | + struct ipu6_isys_subdev asd; | |
| 40 | + struct ipu6_isys_csi2_pdata *pdata; | |
| 41 | + struct ipu6_isys *isys; | |
| 42 | + struct ipu6_isys_video av[NR_OF_CSI2_SRC_PADS]; | |
| 43 | + | |
| 44 | + void __iomem *base; | |
| 45 | + u32 receiver_errors; | |
| 46 | + unsigned int nlanes; | |
| 47 | + unsigned int port; | |
| 48 | + unsigned int stream_count; | |
| 49 | +}; | |
| 50 | + | |
| 51 | +struct ipu6_isys_csi2_timing { | |
| 52 | + u32 ctermen; | |
| 53 | + u32 csettle; | |
| 54 | + u32 dtermen; | |
| 55 | + u32 dsettle; | |
| 56 | +}; | |
| 57 | + | |
| 58 | +struct ipu6_csi2_error { | |
| 59 | + const char *error_string; | |
| 60 | + bool is_info_only; | |
| 61 | +}; | |
| 62 | + | |
| 63 | +#define ipu6_isys_subdev_to_csi2(__sd) \ | |
| 64 | + container_of(__sd, struct ipu6_isys_csi2, asd) | |
| 65 | + | |
| 66 | +#define to_ipu6_isys_csi2(__asd) container_of(__asd, struct ipu6_isys_csi2, asd) | |
| 67 | + | |
| 68 | +s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2); | |
| 69 | +int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2, struct ipu6_isys *isys, | |
| 70 | + void __iomem *base, unsigned int index); | |
| 71 | +void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2); | |
| 72 | +void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream); | |
| 73 | +void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream); | |
| 74 | +void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2); | |
| 75 | +void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2); | |
| 76 | +int ipu6_isys_csi2_get_remote_desc(u32 source_stream, | |
| 77 | + struct ipu6_isys_csi2 *csi2, | |
| 78 | + struct media_entity *source_entity, | |
| 79 | + struct v4l2_mbus_frame_desc_entry *entry); | |
| 80 | +void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status); | |
| 81 | + | |
| 82 | +#endif /* IPU6_ISYS_CSI2_H */ | |
| @@ -0,0 +1,536 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/bitfield.h> | |
| 7 | +#include <linux/bits.h> | |
| 8 | +#include <linux/delay.h> | |
| 9 | +#include <linux/device.h> | |
| 10 | +#include <linux/iopoll.h> | |
| 11 | +#include <linux/math64.h> | |
| 12 | + | |
| 13 | +#include "ipu6-bus.h" | |
| 14 | +#include "ipu6-isys.h" | |
| 15 | +#include "ipu6-platform-isys-csi2-reg.h" | |
| 16 | + | |
| 17 | +#define IPU6_DWC_DPHY_BASE(i) (0x238038 + 0x34 * (i)) | |
| 18 | +#define IPU6_DWC_DPHY_RSTZ 0x00 | |
| 19 | +#define IPU6_DWC_DPHY_SHUTDOWNZ 0x04 | |
| 20 | +#define IPU6_DWC_DPHY_HSFREQRANGE 0x08 | |
| 21 | +#define IPU6_DWC_DPHY_CFGCLKFREQRANGE 0x0c | |
| 22 | +#define IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE 0x10 | |
| 23 | +#define IPU6_DWC_DPHY_TEST_IFC_REQ 0x14 | |
| 24 | +#define IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION 0x18 | |
| 25 | +#define IPU6_DWC_DPHY_DFT_CTRL0 0x28 | |
| 26 | +#define IPU6_DWC_DPHY_DFT_CTRL1 0x2c | |
| 27 | +#define IPU6_DWC_DPHY_DFT_CTRL2 0x30 | |
| 28 | + | |
| 29 | +/* | |
| 30 | + * test IFC request definition: | |
| 31 | + * - req: 0 for read, 1 for write | |
| 32 | + * - 12 bits address | |
| 33 | + * - 8bits data (will ignore for read) | |
| 34 | + * --24----16------4-----0 | |
| 35 | + * --|-data-|-addr-|-req-| | |
| 36 | + */ | |
| 37 | +#define IFC_REQ(req, addr, data) (FIELD_PREP(GENMASK(23, 16), data) | \ | |
| 38 | + FIELD_PREP(GENMASK(15, 4), addr) | \ | |
| 39 | + FIELD_PREP(GENMASK(1, 0), req)) | |
| 40 | + | |
| 41 | +#define TEST_IFC_REQ_READ 0 | |
| 42 | +#define TEST_IFC_REQ_WRITE 1 | |
| 43 | +#define TEST_IFC_REQ_RESET 2 | |
| 44 | + | |
| 45 | +#define TEST_IFC_ACCESS_MODE_FSM 0 | |
| 46 | +#define TEST_IFC_ACCESS_MODE_IFC_CTL 1 | |
| 47 | + | |
| 48 | +enum phy_fsm_state { | |
| 49 | + PHY_FSM_STATE_POWERON = 0, | |
| 50 | + PHY_FSM_STATE_BGPON = 1, | |
| 51 | + PHY_FSM_STATE_CAL_TYPE = 2, | |
| 52 | + PHY_FSM_STATE_BURNIN_CAL = 3, | |
| 53 | + PHY_FSM_STATE_TERMCAL = 4, | |
| 54 | + PHY_FSM_STATE_OFFSETCAL = 5, | |
| 55 | + PHY_FSM_STATE_OFFSET_LANE = 6, | |
| 56 | + PHY_FSM_STATE_IDLE = 7, | |
| 57 | + PHY_FSM_STATE_ULP = 8, | |
| 58 | + PHY_FSM_STATE_DDLTUNNING = 9, | |
| 59 | + PHY_FSM_STATE_SKEW_BACKWARD = 10, | |
| 60 | + PHY_FSM_STATE_INVALID, | |
| 61 | +}; | |
| 62 | + | |
| 63 | +static void dwc_dphy_write(struct ipu6_isys *isys, u32 phy_id, u32 addr, | |
| 64 | + u32 data) | |
| 65 | +{ | |
| 66 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 67 | + void __iomem *isys_base = isys->pdata->base; | |
| 68 | + void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id); | |
| 69 | + | |
| 70 | + dev_dbg(dev, "write: reg 0x%lx = data 0x%x", base + addr - isys_base, | |
| 71 | + data); | |
| 72 | + writel(data, base + addr); | |
| 73 | +} | |
| 74 | + | |
| 75 | +static u32 dwc_dphy_read(struct ipu6_isys *isys, u32 phy_id, u32 addr) | |
| 76 | +{ | |
| 77 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 78 | + void __iomem *isys_base = isys->pdata->base; | |
| 79 | + void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id); | |
| 80 | + u32 data; | |
| 81 | + | |
| 82 | + data = readl(base + addr); | |
| 83 | + dev_dbg(dev, "read: reg 0x%lx = data 0x%x", base + addr - isys_base, | |
| 84 | + data); | |
| 85 | + | |
| 86 | + return data; | |
| 87 | +} | |
| 88 | + | |
| 89 | +static void dwc_dphy_write_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr, | |
| 90 | + u32 data, u8 shift, u8 width) | |
| 91 | +{ | |
| 92 | + u32 temp; | |
| 93 | + u32 mask; | |
| 94 | + | |
| 95 | + mask = (1 << width) - 1; | |
| 96 | + temp = dwc_dphy_read(isys, phy_id, addr); | |
| 97 | + temp &= ~(mask << shift); | |
| 98 | + temp |= (data & mask) << shift; | |
| 99 | + dwc_dphy_write(isys, phy_id, addr, temp); | |
| 100 | +} | |
| 101 | + | |
| 102 | +static u32 __maybe_unused dwc_dphy_read_mask(struct ipu6_isys *isys, u32 phy_id, | |
| 103 | + u32 addr, u8 shift, u8 width) | |
| 104 | +{ | |
| 105 | + u32 val; | |
| 106 | + | |
| 107 | + val = dwc_dphy_read(isys, phy_id, addr) >> shift; | |
| 108 | + return val & ((1 << width) - 1); | |
| 109 | +} | |
| 110 | + | |
| 111 | +#define DWC_DPHY_TIMEOUT (5 * USEC_PER_SEC) | |
| 112 | +static int dwc_dphy_ifc_read(struct ipu6_isys *isys, u32 phy_id, u32 addr, | |
| 113 | + u32 *val) | |
| 114 | +{ | |
| 115 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 116 | + void __iomem *isys_base = isys->pdata->base; | |
| 117 | + void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id); | |
| 118 | + void __iomem *reg; | |
| 119 | + u32 completion; | |
| 120 | + int ret; | |
| 121 | + | |
| 122 | + dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ, | |
| 123 | + IFC_REQ(TEST_IFC_REQ_READ, addr, 0)); | |
| 124 | + reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION; | |
| 125 | + ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)), | |
| 126 | + 10, DWC_DPHY_TIMEOUT); | |
| 127 | + if (ret) { | |
| 128 | + dev_err(dev, "DWC ifc request read timeout\n"); | |
| 129 | + return ret; | |
| 130 | + } | |
| 131 | + | |
| 132 | + *val = completion >> 8 & 0xff; | |
| 133 | + *val = FIELD_GET(GENMASK(15, 8), completion); | |
| 134 | + dev_dbg(dev, "DWC ifc read 0x%x = 0x%x", addr, *val); | |
| 135 | + | |
| 136 | + return 0; | |
| 137 | +} | |
| 138 | + | |
| 139 | +static int dwc_dphy_ifc_write(struct ipu6_isys *isys, u32 phy_id, u32 addr, | |
| 140 | + u32 data) | |
| 141 | +{ | |
| 142 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 143 | + void __iomem *isys_base = isys->pdata->base; | |
| 144 | + void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id); | |
| 145 | + void __iomem *reg; | |
| 146 | + u32 completion; | |
| 147 | + int ret; | |
| 148 | + | |
| 149 | + dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ, | |
| 150 | + IFC_REQ(TEST_IFC_REQ_WRITE, addr, data)); | |
| 151 | + completion = readl(base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION); | |
| 152 | + reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION; | |
| 153 | + ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)), | |
| 154 | + 10, DWC_DPHY_TIMEOUT); | |
| 155 | + if (ret) | |
| 156 | + dev_err(dev, "DWC ifc request write timeout\n"); | |
| 157 | + | |
| 158 | + return ret; | |
| 159 | +} | |
| 160 | + | |
| 161 | +static void dwc_dphy_ifc_write_mask(struct ipu6_isys *isys, u32 phy_id, | |
| 162 | + u32 addr, u32 data, u8 shift, u8 width) | |
| 163 | +{ | |
| 164 | + u32 temp, mask; | |
| 165 | + int ret; | |
| 166 | + | |
| 167 | + ret = dwc_dphy_ifc_read(isys, phy_id, addr, &temp); | |
| 168 | + if (ret) | |
| 169 | + return; | |
| 170 | + | |
| 171 | + mask = (1 << width) - 1; | |
| 172 | + temp &= ~(mask << shift); | |
| 173 | + temp |= (data & mask) << shift; | |
| 174 | + dwc_dphy_ifc_write(isys, phy_id, addr, temp); | |
| 175 | +} | |
| 176 | + | |
| 177 | +static u32 dwc_dphy_ifc_read_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr, | |
| 178 | + u8 shift, u8 width) | |
| 179 | +{ | |
| 180 | + int ret; | |
| 181 | + u32 val; | |
| 182 | + | |
| 183 | + ret = dwc_dphy_ifc_read(isys, phy_id, addr, &val); | |
| 184 | + if (ret) | |
| 185 | + return 0; | |
| 186 | + | |
| 187 | + return ((val >> shift) & ((1 << width) - 1)); | |
| 188 | +} | |
| 189 | + | |
| 190 | +static int dwc_dphy_pwr_up(struct ipu6_isys *isys, u32 phy_id) | |
| 191 | +{ | |
| 192 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 193 | + u32 fsm_state; | |
| 194 | + int ret; | |
| 195 | + | |
| 196 | + dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 1); | |
| 197 | + usleep_range(10, 20); | |
| 198 | + dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 1); | |
| 199 | + | |
| 200 | + ret = read_poll_timeout(dwc_dphy_ifc_read_mask, fsm_state, | |
| 201 | + (fsm_state == PHY_FSM_STATE_IDLE || | |
| 202 | + fsm_state == PHY_FSM_STATE_ULP), | |
| 203 | + 100, DWC_DPHY_TIMEOUT, false, isys, | |
| 204 | + phy_id, 0x1e, 0, 4); | |
| 205 | + | |
| 206 | + if (ret) | |
| 207 | + dev_err(dev, "Dphy %d power up failed, state 0x%x", phy_id, | |
| 208 | + fsm_state); | |
| 209 | + | |
| 210 | + return ret; | |
| 211 | +} | |
| 212 | + | |
| 213 | +struct dwc_dphy_freq_range { | |
| 214 | + u8 hsfreq; | |
| 215 | + u16 min; | |
| 216 | + u16 max; | |
| 217 | + u16 default_mbps; | |
| 218 | + u16 osc_freq_target; | |
| 219 | +}; | |
| 220 | + | |
| 221 | +#define DPHY_FREQ_RANGE_NUM (63) | |
| 222 | +#define DPHY_FREQ_RANGE_INVALID_INDEX (0xff) | |
| 223 | +static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = { | |
| 224 | + {0x00, 80, 97, 80, 335}, | |
| 225 | + {0x10, 80, 107, 90, 335}, | |
| 226 | + {0x20, 84, 118, 100, 335}, | |
| 227 | + {0x30, 93, 128, 110, 335}, | |
| 228 | + {0x01, 103, 139, 120, 335}, | |
| 229 | + {0x11, 112, 149, 130, 335}, | |
| 230 | + {0x21, 122, 160, 140, 335}, | |
| 231 | + {0x31, 131, 170, 150, 335}, | |
| 232 | + {0x02, 141, 181, 160, 335}, | |
| 233 | + {0x12, 150, 191, 170, 335}, | |
| 234 | + {0x22, 160, 202, 180, 335}, | |
| 235 | + {0x32, 169, 212, 190, 335}, | |
| 236 | + {0x03, 183, 228, 205, 335}, | |
| 237 | + {0x13, 198, 244, 220, 335}, | |
| 238 | + {0x23, 212, 259, 235, 335}, | |
| 239 | + {0x33, 226, 275, 250, 335}, | |
| 240 | + {0x04, 250, 301, 275, 335}, | |
| 241 | + {0x14, 274, 328, 300, 335}, | |
| 242 | + {0x25, 297, 354, 325, 335}, | |
| 243 | + {0x35, 321, 380, 350, 335}, | |
| 244 | + {0x05, 369, 433, 400, 335}, | |
| 245 | + {0x16, 416, 485, 450, 335}, | |
| 246 | + {0x26, 464, 538, 500, 335}, | |
| 247 | + {0x37, 511, 590, 550, 335}, | |
| 248 | + {0x07, 559, 643, 600, 335}, | |
| 249 | + {0x18, 606, 695, 650, 335}, | |
| 250 | + {0x28, 654, 748, 700, 335}, | |
| 251 | + {0x39, 701, 800, 750, 335}, | |
| 252 | + {0x09, 749, 853, 800, 335}, | |
| 253 | + {0x19, 796, 905, 850, 335}, | |
| 254 | + {0x29, 844, 958, 900, 335}, | |
| 255 | + {0x3a, 891, 1010, 950, 335}, | |
| 256 | + {0x0a, 939, 1063, 1000, 335}, | |
| 257 | + {0x1a, 986, 1115, 1050, 335}, | |
| 258 | + {0x2a, 1034, 1168, 1100, 335}, | |
| 259 | + {0x3b, 1081, 1220, 1150, 335}, | |
| 260 | + {0x0b, 1129, 1273, 1200, 335}, | |
| 261 | + {0x1b, 1176, 1325, 1250, 335}, | |
| 262 | + {0x2b, 1224, 1378, 1300, 335}, | |
| 263 | + {0x3c, 1271, 1430, 1350, 335}, | |
| 264 | + {0x0c, 1319, 1483, 1400, 335}, | |
| 265 | + {0x1c, 1366, 1535, 1450, 335}, | |
| 266 | + {0x2c, 1414, 1588, 1500, 335}, | |
| 267 | + {0x3d, 1461, 1640, 1550, 208}, | |
| 268 | + {0x0d, 1509, 1693, 1600, 214}, | |
| 269 | + {0x1d, 1556, 1745, 1650, 221}, | |
| 270 | + {0x2e, 1604, 1798, 1700, 228}, | |
| 271 | + {0x3e, 1651, 1850, 1750, 234}, | |
| 272 | + {0x0e, 1699, 1903, 1800, 241}, | |
| 273 | + {0x1e, 1746, 1955, 1850, 248}, | |
| 274 | + {0x2f, 1794, 2008, 1900, 255}, | |
| 275 | + {0x3f, 1841, 2060, 1950, 261}, | |
| 276 | + {0x0f, 1889, 2113, 2000, 268}, | |
| 277 | + {0x40, 1936, 2165, 2050, 275}, | |
| 278 | + {0x41, 1984, 2218, 2100, 281}, | |
| 279 | + {0x42, 2031, 2270, 2150, 288}, | |
| 280 | + {0x43, 2079, 2323, 2200, 294}, | |
| 281 | + {0x44, 2126, 2375, 2250, 302}, | |
| 282 | + {0x45, 2174, 2428, 2300, 308}, | |
| 283 | + {0x46, 2221, 2480, 2350, 315}, | |
| 284 | + {0x47, 2269, 2500, 2400, 321}, | |
| 285 | + {0x48, 2316, 2500, 2450, 328}, | |
| 286 | + {0x49, 2364, 2500, 2500, 335} | |
| 287 | +}; | |
| 288 | + | |
| 289 | +static u16 get_hsfreq_by_mbps(u32 mbps) | |
| 290 | +{ | |
| 291 | + unsigned int i = DPHY_FREQ_RANGE_NUM; | |
| 292 | + | |
| 293 | + while (i--) { | |
| 294 | + if (freqranges[i].default_mbps == mbps || | |
| 295 | + (mbps >= freqranges[i].min && mbps <= freqranges[i].max)) | |
| 296 | + return i; | |
| 297 | + } | |
| 298 | + | |
| 299 | + return DPHY_FREQ_RANGE_INVALID_INDEX; | |
| 300 | +} | |
| 301 | + | |
| 302 | +static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys, | |
| 303 | + u32 phy_id, u32 mbps) | |
| 304 | +{ | |
| 305 | + struct ipu6_bus_device *adev = isys->adev; | |
| 306 | + struct device *dev = &adev->auxdev.dev; | |
| 307 | + struct ipu6_device *isp = adev->isp; | |
| 308 | + u32 cfg_clk_freqrange; | |
| 309 | + u32 osc_freq_target; | |
| 310 | + u32 index; | |
| 311 | + | |
| 312 | + dev_dbg(dev, "config Dphy %u with %u mbps", phy_id, mbps); | |
| 313 | + | |
| 314 | + index = get_hsfreq_by_mbps(mbps); | |
| 315 | + if (index == DPHY_FREQ_RANGE_INVALID_INDEX) { | |
| 316 | + dev_err(dev, "link freq not found for mbps %u", mbps); | |
| 317 | + return -EINVAL; | |
| 318 | + } | |
| 319 | + | |
| 320 | + dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_HSFREQRANGE, | |
| 321 | + freqranges[index].hsfreq, 0, 7); | |
| 322 | + | |
| 323 | + /* Force termination Calibration */ | |
| 324 | + if (isys->phy_termcal_val) { | |
| 325 | + dwc_dphy_ifc_write_mask(isys, phy_id, 0x20a, 0x1, 0, 1); | |
| 326 | + dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, 0x3, 0, 2); | |
| 327 | + dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, | |
| 328 | + isys->phy_termcal_val, 4, 4); | |
| 329 | + } | |
| 330 | + | |
| 331 | + /* | |
| 332 | + * Enable override to configure the DDL target oscillation | |
| 333 | + * frequency on bit 0 of register 0xe4 | |
| 334 | + */ | |
| 335 | + dwc_dphy_ifc_write_mask(isys, phy_id, 0xe4, 0x1, 0, 1); | |
| 336 | + /* | |
| 337 | + * configure registers 0xe2, 0xe3 with the | |
| 338 | + * appropriate DDL target oscillation frequency | |
| 339 | + * 0x1cc(460) | |
| 340 | + */ | |
| 341 | + osc_freq_target = freqranges[index].osc_freq_target; | |
| 342 | + dwc_dphy_ifc_write_mask(isys, phy_id, 0xe2, | |
| 343 | + osc_freq_target & 0xff, 0, 8); | |
| 344 | + dwc_dphy_ifc_write_mask(isys, phy_id, 0xe3, | |
| 345 | + (osc_freq_target >> 8) & 0xf, 0, 4); | |
| 346 | + | |
| 347 | + if (mbps < 1500) { | |
| 348 | + /* deskew_polarity_rw, for < 1.5Gbps */ | |
| 349 | + dwc_dphy_ifc_write_mask(isys, phy_id, 0x8, 0x1, 5, 1); | |
| 350 | + } | |
| 351 | + | |
| 352 | + /* | |
| 353 | + * Set cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4] | |
| 354 | + * (38.4 - 17) * 4 = ~85 (0x55) | |
| 355 | + */ | |
| 356 | + cfg_clk_freqrange = (isp->buttress.ref_clk - 170) * 4 / 10; | |
| 357 | + dev_dbg(dev, "ref_clk = %u clk_freqrange = %u", | |
| 358 | + isp->buttress.ref_clk, cfg_clk_freqrange); | |
| 359 | + dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_CFGCLKFREQRANGE, | |
| 360 | + cfg_clk_freqrange, 0, 8); | |
| 361 | + | |
| 362 | + dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 4, 1); | |
| 363 | + dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 8, 1); | |
| 364 | + | |
| 365 | + return 0; | |
| 366 | +} | |
| 367 | + | |
| 368 | +static void ipu6_isys_dwc_phy_aggr_setup(struct ipu6_isys *isys, u32 master, | |
| 369 | + u32 slave, u32 mbps) | |
| 370 | +{ | |
| 371 | + /* Config mastermacro */ | |
| 372 | + dwc_dphy_ifc_write_mask(isys, master, 0x133, 0x1, 0, 1); | |
| 373 | + dwc_dphy_ifc_write_mask(isys, slave, 0x133, 0x0, 0, 1); | |
| 374 | + | |
| 375 | + /* Config master PHY clk lane to drive long channel clk */ | |
| 376 | + dwc_dphy_ifc_write_mask(isys, master, 0x307, 0x1, 2, 1); | |
| 377 | + dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x0, 2, 1); | |
| 378 | + | |
| 379 | + /* Config both PHYs data lanes to get clk from long channel */ | |
| 380 | + dwc_dphy_ifc_write_mask(isys, master, 0x508, 0x1, 5, 1); | |
| 381 | + dwc_dphy_ifc_write_mask(isys, slave, 0x508, 0x1, 5, 1); | |
| 382 | + dwc_dphy_ifc_write_mask(isys, master, 0x708, 0x1, 5, 1); | |
| 383 | + dwc_dphy_ifc_write_mask(isys, slave, 0x708, 0x1, 5, 1); | |
| 384 | + | |
| 385 | + /* Config slave PHY clk lane to bypass long channel clk to DDR clk */ | |
| 386 | + dwc_dphy_ifc_write_mask(isys, master, 0x308, 0x0, 3, 1); | |
| 387 | + dwc_dphy_ifc_write_mask(isys, slave, 0x308, 0x1, 3, 1); | |
| 388 | + | |
| 389 | + /* Override slave PHY clk lane enable (DPHYRXCLK_CLL_demux module) */ | |
| 390 | + dwc_dphy_ifc_write_mask(isys, slave, 0xe0, 0x3, 0, 2); | |
| 391 | + | |
| 392 | + /* Override slave PHY DDR clk lane enable (DPHYHSRX_div124 module) */ | |
| 393 | + dwc_dphy_ifc_write_mask(isys, slave, 0xe1, 0x1, 1, 1); | |
| 394 | + dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x1, 3, 1); | |
| 395 | + | |
| 396 | + /* Turn off slave PHY LP-RX clk lane */ | |
| 397 | + dwc_dphy_ifc_write_mask(isys, slave, 0x304, 0x1, 7, 1); | |
| 398 | + dwc_dphy_ifc_write_mask(isys, slave, 0x305, 0xa, 0, 5); | |
| 399 | +} | |
| 400 | + | |
| 401 | +#define PHY_E 4 | |
| 402 | +static int ipu6_isys_dwc_phy_powerup_ack(struct ipu6_isys *isys, u32 phy_id) | |
| 403 | +{ | |
| 404 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 405 | + u32 rescal_done; | |
| 406 | + int ret; | |
| 407 | + | |
| 408 | + ret = dwc_dphy_pwr_up(isys, phy_id); | |
| 409 | + if (ret != 0) { | |
| 410 | + dev_err(dev, "Dphy %u power up failed(%d)", phy_id, ret); | |
| 411 | + return ret; | |
| 412 | + } | |
| 413 | + | |
| 414 | + /* reset forcerxmode */ | |
| 415 | + dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 4, 1); | |
| 416 | + dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 8, 1); | |
| 417 | + | |
| 418 | + dev_dbg(dev, "Dphy %u is ready!", phy_id); | |
| 419 | + | |
| 420 | + if (phy_id != PHY_E || isys->phy_termcal_val) | |
| 421 | + return 0; | |
| 422 | + | |
| 423 | + usleep_range(100, 200); | |
| 424 | + rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1); | |
| 425 | + if (rescal_done) { | |
| 426 | + isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id, | |
| 427 | + 0x220, 2, 4); | |
| 428 | + dev_dbg(dev, "termcal done with value = %u", | |
| 429 | + isys->phy_termcal_val); | |
| 430 | + } | |
| 431 | + | |
| 432 | + return 0; | |
| 433 | +} | |
| 434 | + | |
| 435 | +static void ipu6_isys_dwc_phy_reset(struct ipu6_isys *isys, u32 phy_id) | |
| 436 | +{ | |
| 437 | + dev_dbg(&isys->adev->auxdev.dev, "Reset phy %u", phy_id); | |
| 438 | + | |
| 439 | + dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 0); | |
| 440 | + dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 0); | |
| 441 | + dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE, | |
| 442 | + TEST_IFC_ACCESS_MODE_FSM); | |
| 443 | + dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ, | |
| 444 | + TEST_IFC_REQ_RESET); | |
| 445 | +} | |
| 446 | + | |
| 447 | +int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys, | |
| 448 | + struct ipu6_isys_csi2_config *cfg, | |
| 449 | + const struct ipu6_isys_csi2_timing *timing, | |
| 450 | + bool on) | |
| 451 | +{ | |
| 452 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 453 | + void __iomem *isys_base = isys->pdata->base; | |
| 454 | + u32 phy_id, primary, secondary; | |
| 455 | + u32 nlanes, port, mbps; | |
| 456 | + s64 link_freq; | |
| 457 | + int ret; | |
| 458 | + | |
| 459 | + port = cfg->port; | |
| 460 | + | |
| 461 | + if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) { | |
| 462 | + dev_warn(dev, "invalid port ID %d\n", port); | |
| 463 | + return -EINVAL; | |
| 464 | + } | |
| 465 | + | |
| 466 | + nlanes = cfg->nlanes; | |
| 467 | + /* only port 0, 2 and 4 support 4 lanes */ | |
| 468 | + if (nlanes == 4 && port % 2) { | |
| 469 | + dev_err(dev, "invalid csi-port %u with %u lanes\n", port, | |
| 470 | + nlanes); | |
| 471 | + return -EINVAL; | |
| 472 | + } | |
| 473 | + | |
| 474 | + link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[port]); | |
| 475 | + if (link_freq < 0) { | |
| 476 | + dev_err(dev, "get link freq failed(%lld).\n", link_freq); | |
| 477 | + return link_freq; | |
| 478 | + } | |
| 479 | + | |
| 480 | + mbps = div_u64(link_freq, 500000); | |
| 481 | + | |
| 482 | + phy_id = port; | |
| 483 | + primary = port & ~1; | |
| 484 | + secondary = primary + 1; | |
| 485 | + if (on) { | |
| 486 | + if (nlanes == 4) { | |
| 487 | + dev_dbg(dev, "config phy %u and %u in aggr mode\n", | |
| 488 | + primary, secondary); | |
| 489 | + | |
| 490 | + ipu6_isys_dwc_phy_reset(isys, primary); | |
| 491 | + ipu6_isys_dwc_phy_reset(isys, secondary); | |
| 492 | + ipu6_isys_dwc_phy_aggr_setup(isys, primary, | |
| 493 | + secondary, mbps); | |
| 494 | + | |
| 495 | + ret = ipu6_isys_dwc_phy_config(isys, primary, mbps); | |
| 496 | + if (ret) | |
| 497 | + return ret; | |
| 498 | + ret = ipu6_isys_dwc_phy_config(isys, secondary, mbps); | |
| 499 | + if (ret) | |
| 500 | + return ret; | |
| 501 | + | |
| 502 | + ret = ipu6_isys_dwc_phy_powerup_ack(isys, primary); | |
| 503 | + if (ret) | |
| 504 | + return ret; | |
| 505 | + | |
| 506 | + ret = ipu6_isys_dwc_phy_powerup_ack(isys, secondary); | |
| 507 | + return ret; | |
| 508 | + } | |
| 509 | + | |
| 510 | + dev_dbg(dev, "config phy %u with %u lanes in non-aggr mode\n", | |
| 511 | + phy_id, nlanes); | |
| 512 | + | |
| 513 | + ipu6_isys_dwc_phy_reset(isys, phy_id); | |
| 514 | + ret = ipu6_isys_dwc_phy_config(isys, phy_id, mbps); | |
| 515 | + if (ret) | |
| 516 | + return ret; | |
| 517 | + | |
| 518 | + ret = ipu6_isys_dwc_phy_powerup_ack(isys, phy_id); | |
| 519 | + return ret; | |
| 520 | + } | |
| 521 | + | |
| 522 | + if (nlanes == 4) { | |
| 523 | + dev_dbg(dev, "Power down phy %u and phy %u for port %u\n", | |
| 524 | + primary, secondary, port); | |
| 525 | + ipu6_isys_dwc_phy_reset(isys, secondary); | |
| 526 | + ipu6_isys_dwc_phy_reset(isys, primary); | |
| 527 | + | |
| 528 | + return 0; | |
| 529 | + } | |
| 530 | + | |
| 531 | + dev_dbg(dev, "Powerdown phy %u with %u lanes\n", phy_id, nlanes); | |
| 532 | + | |
| 533 | + ipu6_isys_dwc_phy_reset(isys, phy_id); | |
| 534 | + | |
| 535 | + return 0; | |
| 536 | +} | |
| @@ -0,0 +1,242 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/bitfield.h> | |
| 7 | +#include <linux/bits.h> | |
| 8 | +#include <linux/device.h> | |
| 9 | +#include <linux/io.h> | |
| 10 | + | |
| 11 | +#include "ipu6-bus.h" | |
| 12 | +#include "ipu6-isys.h" | |
| 13 | +#include "ipu6-isys-csi2.h" | |
| 14 | +#include "ipu6-platform-isys-csi2-reg.h" | |
| 15 | + | |
| 16 | +/* only use BB0, BB2, BB4, and BB6 on PHY0 */ | |
| 17 | +#define IPU6SE_ISYS_PHY_BB_NUM 4 | |
| 18 | +#define IPU6SE_ISYS_PHY_0_BASE 0x10000 | |
| 19 | + | |
| 20 | +#define PHY_CPHY_DLL_OVRD(x) (0x100 + 0x100 * (x)) | |
| 21 | +#define PHY_CPHY_RX_CONTROL1(x) (0x110 + 0x100 * (x)) | |
| 22 | +#define PHY_DPHY_CFG(x) (0x148 + 0x100 * (x)) | |
| 23 | +#define PHY_BB_AFE_CONFIG(x) (0x174 + 0x100 * (x)) | |
| 24 | + | |
| 25 | +/* | |
| 26 | + * use port_cfg to configure that which data lanes used | |
| 27 | + * +---------+ +------+ +-----+ | |
| 28 | + * | port0 x4<-----| | | | | |
| 29 | + * | | | port | | | | |
| 30 | + * | port1 x2<-----| | | | | |
| 31 | + * | | | <-| PHY | | |
| 32 | + * | port2 x4<-----| | | | | |
| 33 | + * | | |config| | | | |
| 34 | + * | port3 x2<-----| | | | | |
| 35 | + * +---------+ +------+ +-----+ | |
| 36 | + */ | |
| 37 | +static const unsigned int csi2_port_cfg[][3] = { | |
| 38 | + {0, 0, 0x1f}, /* no link */ | |
| 39 | + {4, 0, 0x10}, /* x4 + x4 config */ | |
| 40 | + {2, 0, 0x12}, /* x2 + x2 config */ | |
| 41 | + {1, 0, 0x13}, /* x1 + x1 config */ | |
| 42 | + {2, 1, 0x15}, /* x2x1 + x2x1 config */ | |
| 43 | + {1, 1, 0x16}, /* x1x1 + x1x1 config */ | |
| 44 | + {2, 2, 0x18}, /* x2x2 + x2x2 config */ | |
| 45 | + {1, 2, 0x19} /* x1x2 + x1x2 config */ | |
| 46 | +}; | |
| 47 | + | |
| 48 | +/* port, nlanes, bbindex, portcfg */ | |
| 49 | +static const unsigned int phy_port_cfg[][4] = { | |
| 50 | + /* sip0 */ | |
| 51 | + {0, 1, 0, 0x15}, | |
| 52 | + {0, 2, 0, 0x15}, | |
| 53 | + {0, 4, 0, 0x15}, | |
| 54 | + {0, 4, 2, 0x22}, | |
| 55 | + /* sip1 */ | |
| 56 | + {2, 1, 4, 0x15}, | |
| 57 | + {2, 2, 4, 0x15}, | |
| 58 | + {2, 4, 4, 0x15}, | |
| 59 | + {2, 4, 6, 0x22} | |
| 60 | +}; | |
| 61 | + | |
| 62 | +static void ipu6_isys_csi2_phy_config_by_port(struct ipu6_isys *isys, | |
| 63 | + unsigned int port, | |
| 64 | + unsigned int nlanes) | |
| 65 | +{ | |
| 66 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 67 | + void __iomem *base = isys->adev->isp->base; | |
| 68 | + unsigned int bbnum; | |
| 69 | + u32 val, reg, i; | |
| 70 | + | |
| 71 | + dev_dbg(dev, "port %u with %u lanes", port, nlanes); | |
| 72 | + | |
| 73 | + /* only support <1.5Gbps */ | |
| 74 | + for (i = 0; i < IPU6SE_ISYS_PHY_BB_NUM; i++) { | |
| 75 | + /* cphy_dll_ovrd.crcdc_fsm_dlane0 = 13 */ | |
| 76 | + reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_DLL_OVRD(i); | |
| 77 | + val = readl(base + reg); | |
| 78 | + val |= FIELD_PREP(GENMASK(6, 1), 13); | |
| 79 | + writel(val, base + reg); | |
| 80 | + | |
| 81 | + /* cphy_rx_control1.en_crc1 = 1 */ | |
| 82 | + reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_RX_CONTROL1(i); | |
| 83 | + val = readl(base + reg); | |
| 84 | + val |= BIT(31); | |
| 85 | + writel(val, base + reg); | |
| 86 | + | |
| 87 | + /* dphy_cfg.reserved = 1, .lden_from_dll_ovrd_0 = 1 */ | |
| 88 | + reg = IPU6SE_ISYS_PHY_0_BASE + PHY_DPHY_CFG(i); | |
| 89 | + val = readl(base + reg); | |
| 90 | + val |= BIT(25) | BIT(26); | |
| 91 | + writel(val, base + reg); | |
| 92 | + | |
| 93 | + /* cphy_dll_ovrd.lden_crcdc_fsm_dlane0 = 1 */ | |
| 94 | + reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_DLL_OVRD(i); | |
| 95 | + val = readl(base + reg); | |
| 96 | + val |= BIT(0); | |
| 97 | + writel(val, base + reg); | |
| 98 | + } | |
| 99 | + | |
| 100 | + /* Front end config, use minimal channel loss */ | |
| 101 | + for (i = 0; i < ARRAY_SIZE(phy_port_cfg); i++) { | |
| 102 | + if (phy_port_cfg[i][0] == port && | |
| 103 | + phy_port_cfg[i][1] == nlanes) { | |
| 104 | + bbnum = phy_port_cfg[i][2] / 2; | |
| 105 | + reg = IPU6SE_ISYS_PHY_0_BASE + PHY_BB_AFE_CONFIG(bbnum); | |
| 106 | + val = readl(base + reg); | |
| 107 | + val |= phy_port_cfg[i][3]; | |
| 108 | + writel(val, base + reg); | |
| 109 | + } | |
| 110 | + } | |
| 111 | +} | |
| 112 | + | |
| 113 | +static void ipu6_isys_csi2_rx_control(struct ipu6_isys *isys) | |
| 114 | +{ | |
| 115 | + void __iomem *base = isys->adev->isp->base; | |
| 116 | + u32 val, reg; | |
| 117 | + | |
| 118 | + reg = CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL; | |
| 119 | + val = readl(base + reg); | |
| 120 | + val |= BIT(0); | |
| 121 | + writel(val, base + CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL); | |
| 122 | + | |
| 123 | + reg = CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL; | |
| 124 | + val = readl(base + reg); | |
| 125 | + val |= BIT(0); | |
| 126 | + writel(val, base + CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL); | |
| 127 | + | |
| 128 | + reg = CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL; | |
| 129 | + val = readl(base + reg); | |
| 130 | + val |= BIT(0); | |
| 131 | + writel(val, base + CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL); | |
| 132 | + | |
| 133 | + reg = CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL; | |
| 134 | + val = readl(base + reg); | |
| 135 | + val |= BIT(0); | |
| 136 | + writel(val, base + CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL); | |
| 137 | +} | |
| 138 | + | |
| 139 | +static int ipu6_isys_csi2_set_port_cfg(struct ipu6_isys *isys, | |
| 140 | + unsigned int port, unsigned int nlanes) | |
| 141 | +{ | |
| 142 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 143 | + unsigned int sip = port / 2; | |
| 144 | + unsigned int index; | |
| 145 | + | |
| 146 | + switch (nlanes) { | |
| 147 | + case 1: | |
| 148 | + index = 5; | |
| 149 | + break; | |
| 150 | + case 2: | |
| 151 | + index = 6; | |
| 152 | + break; | |
| 153 | + case 4: | |
| 154 | + index = 1; | |
| 155 | + break; | |
| 156 | + default: | |
| 157 | + dev_err(dev, "lanes nr %u is unsupported\n", nlanes); | |
| 158 | + return -EINVAL; | |
| 159 | + } | |
| 160 | + | |
| 161 | + dev_dbg(dev, "port config for port %u with %u lanes\n", port, nlanes); | |
| 162 | + | |
| 163 | + writel(csi2_port_cfg[index][2], | |
| 164 | + isys->pdata->base + CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip)); | |
| 165 | + | |
| 166 | + return 0; | |
| 167 | +} | |
| 168 | + | |
| 169 | +static void | |
| 170 | +ipu6_isys_csi2_set_timing(struct ipu6_isys *isys, | |
| 171 | + const struct ipu6_isys_csi2_timing *timing, | |
| 172 | + unsigned int port, unsigned int nlanes) | |
| 173 | +{ | |
| 174 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 175 | + void __iomem *reg; | |
| 176 | + u32 port_base; | |
| 177 | + u32 i; | |
| 178 | + | |
| 179 | + port_base = (port % 2) ? CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port) : | |
| 180 | + CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port); | |
| 181 | + | |
| 182 | + dev_dbg(dev, "set timing for port %u with %u lanes\n", port, nlanes); | |
| 183 | + | |
| 184 | + reg = isys->pdata->base + port_base; | |
| 185 | + reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE; | |
| 186 | + | |
| 187 | + writel(timing->ctermen, reg); | |
| 188 | + | |
| 189 | + reg = isys->pdata->base + port_base; | |
| 190 | + reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE; | |
| 191 | + writel(timing->csettle, reg); | |
| 192 | + | |
| 193 | + for (i = 0; i < nlanes; i++) { | |
| 194 | + reg = isys->pdata->base + port_base; | |
| 195 | + reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(i); | |
| 196 | + writel(timing->dtermen, reg); | |
| 197 | + | |
| 198 | + reg = isys->pdata->base + port_base; | |
| 199 | + reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(i); | |
| 200 | + writel(timing->dsettle, reg); | |
| 201 | + } | |
| 202 | +} | |
| 203 | + | |
| 204 | +#define DPHY_TIMER_INCR 0x28 | |
| 205 | +int ipu6_isys_jsl_phy_set_power(struct ipu6_isys *isys, | |
| 206 | + struct ipu6_isys_csi2_config *cfg, | |
| 207 | + const struct ipu6_isys_csi2_timing *timing, | |
| 208 | + bool on) | |
| 209 | +{ | |
| 210 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 211 | + void __iomem *isys_base = isys->pdata->base; | |
| 212 | + int ret = 0; | |
| 213 | + u32 nlanes; | |
| 214 | + u32 port; | |
| 215 | + | |
| 216 | + if (!on) | |
| 217 | + return 0; | |
| 218 | + | |
| 219 | + port = cfg->port; | |
| 220 | + nlanes = cfg->nlanes; | |
| 221 | + | |
| 222 | + if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) { | |
| 223 | + dev_warn(dev, "invalid port ID %d\n", port); | |
| 224 | + return -EINVAL; | |
| 225 | + } | |
| 226 | + | |
| 227 | + ipu6_isys_csi2_phy_config_by_port(isys, port, nlanes); | |
| 228 | + | |
| 229 | + writel(DPHY_TIMER_INCR, | |
| 230 | + isys->pdata->base + CSI2_HUB_GPREG_DPHY_TIMER_INCR); | |
| 231 | + | |
| 232 | + /* set port cfg and rx timing */ | |
| 233 | + ipu6_isys_csi2_set_timing(isys, timing, port, nlanes); | |
| 234 | + | |
| 235 | + ret = ipu6_isys_csi2_set_port_cfg(isys, port, nlanes); | |
| 236 | + if (ret) | |
| 237 | + return ret; | |
| 238 | + | |
| 239 | + ipu6_isys_csi2_rx_control(isys); | |
| 240 | + | |
| 241 | + return 0; | |
| 242 | +} | |
| @@ -0,0 +1,720 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/bits.h> | |
| 7 | +#include <linux/container_of.h> | |
| 8 | +#include <linux/device.h> | |
| 9 | +#include <linux/iopoll.h> | |
| 10 | +#include <linux/list.h> | |
| 11 | +#include <linux/refcount.h> | |
| 12 | +#include <linux/time64.h> | |
| 13 | + | |
| 14 | +#include <media/v4l2-async.h> | |
| 15 | + | |
| 16 | +#include "ipu6.h" | |
| 17 | +#include "ipu6-bus.h" | |
| 18 | +#include "ipu6-isys.h" | |
| 19 | +#include "ipu6-isys-csi2.h" | |
| 20 | +#include "ipu6-platform-isys-csi2-reg.h" | |
| 21 | + | |
| 22 | +#define CSI_REG_HUB_GPREG_PHY_CTL(id) (CSI_REG_BASE + 0x18008 + (id) * 0x8) | |
| 23 | +#define CSI_REG_HUB_GPREG_PHY_CTL_RESET BIT(4) | |
| 24 | +#define CSI_REG_HUB_GPREG_PHY_CTL_PWR_EN BIT(0) | |
| 25 | +#define CSI_REG_HUB_GPREG_PHY_STATUS(id) (CSI_REG_BASE + 0x1800c + (id) * 0x8) | |
| 26 | +#define CSI_REG_HUB_GPREG_PHY_POWER_ACK BIT(0) | |
| 27 | +#define CSI_REG_HUB_GPREG_PHY_READY BIT(4) | |
| 28 | + | |
| 29 | +#define MCD_PHY_POWER_STATUS_TIMEOUT (200 * USEC_PER_MSEC) | |
| 30 | + | |
| 31 | +/* | |
| 32 | + * bridge to phy in buttress reg map, each phy has 16 kbytes | |
| 33 | + * only 2 phys for TGL U and Y | |
| 34 | + */ | |
| 35 | +#define IPU6_ISYS_MCD_PHY_BASE(i) (0x10000 + (i) * 0x4000) | |
| 36 | + | |
| 37 | +/* | |
| 38 | + * There are 2 MCD DPHY instances on TGL and 1 MCD DPHY instance on ADL. | |
| 39 | + * Each MCD PHY has 12-lanes which has 8 data lanes and 4 clock lanes. | |
| 40 | + * CSI port 1, 3 (5, 7) can support max 2 data lanes. | |
| 41 | + * CSI port 0, 2 (4, 6) can support max 4 data lanes. | |
| 42 | + * PHY configurations are PPI based instead of port. | |
| 43 | + * Left: | |
| 44 | + * +---------+---------+---------+---------+--------+---------+----------+ | |
| 45 | + * | | | | | | | | | |
| 46 | + * | PPI | PPI5 | PPI4 | PPI3 | PPI2 | PPI1 | PPI0 | | |
| 47 | + * +---------+---------+---------+---------+--------+---------+----------+ | |
| 48 | + * | | | | | | | | | |
| 49 | + * | x4 | unused | D3 | D2 | C0 | D0 | D1 | | |
| 50 | + * |---------+---------+---------+---------+--------+---------+----------+ | |
| 51 | + * | | | | | | | | | |
| 52 | + * | x2x2 | C1 | D0 | D1 | C0 | D0 | D1 | | |
| 53 | + * ----------+---------+---------+---------+--------+---------+----------+ | |
| 54 | + * | | | | | | | | | |
| 55 | + * | x2x1 | C1 | D0 | unused | C0 | D0 | D1 | | |
| 56 | + * +---------+---------+---------+---------+--------+---------+----------+ | |
| 57 | + * | | | | | | | | | |
| 58 | + * | x1x1 | C1 | D0 | unused | C0 | D0 | unused | | |
| 59 | + * +---------+---------+---------+---------+--------+---------+----------+ | |
| 60 | + * | | | | | | | | | |
| 61 | + * | x1x2 | C1 | D0 | D1 | C0 | D0 | unused | | |
| 62 | + * +---------+---------+---------+---------+--------+---------+----------+ | |
| 63 | + * | |
| 64 | + * Right: | |
| 65 | + * +---------+---------+---------+---------+--------+---------+----------+ | |
| 66 | + * | | | | | | | | | |
| 67 | + * | PPI | PPI6 | PPI7 | PPI8 | PPI9 | PPI10 | PPI11 | | |
| 68 | + * +---------+---------+---------+---------+--------+---------+----------+ | |
| 69 | + * | | | | | | | | | |
| 70 | + * | x4 | D1 | D0 | C2 | D2 | D3 | unused | | |
| 71 | + * |---------+---------+---------+---------+--------+---------+----------+ | |
| 72 | + * | | | | | | | | | |
| 73 | + * | x2x2 | D1 | D0 | C2 | D1 | D0 | C3 | | |
| 74 | + * ----------+---------+---------+---------+--------+---------+----------+ | |
| 75 | + * | | | | | | | | | |
| 76 | + * | x2x1 | D1 | D0 | C2 | unused | D0 | C3 | | |
| 77 | + * +---------+---------+---------+---------+--------+---------+----------+ | |
| 78 | + * | | | | | | | | | |
| 79 | + * | x1x1 | unused | D0 | C2 | unused | D0 | C3 | | |
| 80 | + * +---------+---------+---------+---------+--------+---------+----------+ | |
| 81 | + * | | | | | | | | | |
| 82 | + * | x1x2 | unused | D0 | C2 | D1 | D0 | C3 | | |
| 83 | + * +---------+---------+---------+---------+--------+---------+----------+ | |
| 84 | + * | |
| 85 | + * ppi mapping per phy : | |
| 86 | + * | |
| 87 | + * x4 + x4: | |
| 88 | + * Left : port0 - PPI range {0, 1, 2, 3, 4} | |
| 89 | + * Right: port2 - PPI range {6, 7, 8, 9, 10} | |
| 90 | + * | |
| 91 | + * x4 + x2x2: | |
| 92 | + * Left: port0 - PPI range {0, 1, 2, 3, 4} | |
| 93 | + * Right: port2 - PPI range {6, 7, 8}, port3 - PPI range {9, 10, 11} | |
| 94 | + * | |
| 95 | + * x2x2 + x4: | |
| 96 | + * Left: port0 - PPI range {0, 1, 2}, port1 - PPI range {3, 4, 5} | |
| 97 | + * Right: port2 - PPI range {6, 7, 8, 9, 10} | |
| 98 | + * | |
| 99 | + * x2x2 + x2x2: | |
| 100 | + * Left : port0 - PPI range {0, 1, 2}, port1 - PPI range {3, 4, 5} | |
| 101 | + * Right: port2 - PPI range {6, 7, 8}, port3 - PPI range {9, 10, 11} | |
| 102 | + */ | |
| 103 | + | |
| 104 | +struct phy_reg { | |
| 105 | + u32 reg; | |
| 106 | + u32 val; | |
| 107 | +}; | |
| 108 | + | |
| 109 | +static const struct phy_reg common_init_regs[] = { | |
| 110 | + /* for TGL-U, use 0x80000000 */ | |
| 111 | + {0x00000040, 0x80000000}, | |
| 112 | + {0x00000044, 0x00a80880}, | |
| 113 | + {0x00000044, 0x00b80880}, | |
| 114 | + {0x00000010, 0x0000078c}, | |
| 115 | + {0x00000344, 0x2f4401e2}, | |
| 116 | + {0x00000544, 0x924401e2}, | |
| 117 | + {0x00000744, 0x594401e2}, | |
| 118 | + {0x00000944, 0x624401e2}, | |
| 119 | + {0x00000b44, 0xfc4401e2}, | |
| 120 | + {0x00000d44, 0xc54401e2}, | |
| 121 | + {0x00000f44, 0x034401e2}, | |
| 122 | + {0x00001144, 0x8f4401e2}, | |
| 123 | + {0x00001344, 0x754401e2}, | |
| 124 | + {0x00001544, 0xe94401e2}, | |
| 125 | + {0x00001744, 0xcb4401e2}, | |
| 126 | + {0x00001944, 0xfa4401e2} | |
| 127 | +}; | |
| 128 | + | |
| 129 | +static const struct phy_reg x1_port0_config_regs[] = { | |
| 130 | + {0x00000694, 0xc80060fa}, | |
| 131 | + {0x00000680, 0x3d4f78ea}, | |
| 132 | + {0x00000690, 0x10a0140b}, | |
| 133 | + {0x000006a8, 0xdf04010a}, | |
| 134 | + {0x00000700, 0x57050060}, | |
| 135 | + {0x00000710, 0x0030001c}, | |
| 136 | + {0x00000738, 0x5f004444}, | |
| 137 | + {0x0000073c, 0x78464204}, | |
| 138 | + {0x00000748, 0x7821f940}, | |
| 139 | + {0x0000074c, 0xb2000433}, | |
| 140 | + {0x00000494, 0xfe6030fa}, | |
| 141 | + {0x00000480, 0x29ef5ed0}, | |
| 142 | + {0x00000490, 0x10a0540b}, | |
| 143 | + {0x000004a8, 0x7a01010a}, | |
| 144 | + {0x00000500, 0xef053460}, | |
| 145 | + {0x00000510, 0xe030101c}, | |
| 146 | + {0x00000538, 0xdf808444}, | |
| 147 | + {0x0000053c, 0xc8422204}, | |
| 148 | + {0x00000540, 0x0180088c}, | |
| 149 | + {0x00000574, 0x00000000}, | |
| 150 | + {0x00000000, 0x00000000} | |
| 151 | +}; | |
| 152 | + | |
| 153 | +static const struct phy_reg x1_port1_config_regs[] = { | |
| 154 | + {0x00000c94, 0xc80060fa}, | |
| 155 | + {0x00000c80, 0xcf47abea}, | |
| 156 | + {0x00000c90, 0x10a0840b}, | |
| 157 | + {0x00000ca8, 0xdf04010a}, | |
| 158 | + {0x00000d00, 0x57050060}, | |
| 159 | + {0x00000d10, 0x0030001c}, | |
| 160 | + {0x00000d38, 0x5f004444}, | |
| 161 | + {0x00000d3c, 0x78464204}, | |
| 162 | + {0x00000d48, 0x7821f940}, | |
| 163 | + {0x00000d4c, 0xb2000433}, | |
| 164 | + {0x00000a94, 0xc91030fa}, | |
| 165 | + {0x00000a80, 0x5a166ed0}, | |
| 166 | + {0x00000a90, 0x10a0540b}, | |
| 167 | + {0x00000aa8, 0x5d060100}, | |
| 168 | + {0x00000b00, 0xef053460}, | |
| 169 | + {0x00000b10, 0xa030101c}, | |
| 170 | + {0x00000b38, 0xdf808444}, | |
| 171 | + {0x00000b3c, 0xc8422204}, | |
| 172 | + {0x00000b40, 0x0180088c}, | |
| 173 | + {0x00000b74, 0x00000000}, | |
| 174 | + {0x00000000, 0x00000000} | |
| 175 | +}; | |
| 176 | + | |
| 177 | +static const struct phy_reg x1_port2_config_regs[] = { | |
| 178 | + {0x00001294, 0x28f000fa}, | |
| 179 | + {0x00001280, 0x08130cea}, | |
| 180 | + {0x00001290, 0x10a0140b}, | |
| 181 | + {0x000012a8, 0xd704010a}, | |
| 182 | + {0x00001300, 0x8d050060}, | |
| 183 | + {0x00001310, 0x0030001c}, | |
| 184 | + {0x00001338, 0xdf008444}, | |
| 185 | + {0x0000133c, 0x78422204}, | |
| 186 | + {0x00001348, 0x7821f940}, | |
| 187 | + {0x0000134c, 0x5a000433}, | |
| 188 | + {0x00001094, 0x2d20b0fa}, | |
| 189 | + {0x00001080, 0xade75dd0}, | |
| 190 | + {0x00001090, 0x10a0540b}, | |
| 191 | + {0x000010a8, 0xb101010a}, | |
| 192 | + {0x00001100, 0x33053460}, | |
| 193 | + {0x00001110, 0x0030101c}, | |
| 194 | + {0x00001138, 0xdf808444}, | |
| 195 | + {0x0000113c, 0xc8422204}, | |
| 196 | + {0x00001140, 0x8180088c}, | |
| 197 | + {0x00001174, 0x00000000}, | |
| 198 | + {0x00000000, 0x00000000} | |
| 199 | +}; | |
| 200 | + | |
| 201 | +static const struct phy_reg x1_port3_config_regs[] = { | |
| 202 | + {0x00001894, 0xc80060fa}, | |
| 203 | + {0x00001880, 0x0f90fd6a}, | |
| 204 | + {0x00001890, 0x10a0840b}, | |
| 205 | + {0x000018a8, 0xdf04010a}, | |
| 206 | + {0x00001900, 0x57050060}, | |
| 207 | + {0x00001910, 0x0030001c}, | |
| 208 | + {0x00001938, 0x5f004444}, | |
| 209 | + {0x0000193c, 0x78464204}, | |
| 210 | + {0x00001948, 0x7821f940}, | |
| 211 | + {0x0000194c, 0xb2000433}, | |
| 212 | + {0x00001694, 0x3050d0fa}, | |
| 213 | + {0x00001680, 0x0ef6d050}, | |
| 214 | + {0x00001690, 0x10a0540b}, | |
| 215 | + {0x000016a8, 0xe301010a}, | |
| 216 | + {0x00001700, 0x69053460}, | |
| 217 | + {0x00001710, 0xa030101c}, | |
| 218 | + {0x00001738, 0xdf808444}, | |
| 219 | + {0x0000173c, 0xc8422204}, | |
| 220 | + {0x00001740, 0x0180088c}, | |
| 221 | + {0x00001774, 0x00000000}, | |
| 222 | + {0x00000000, 0x00000000} | |
| 223 | +}; | |
| 224 | + | |
| 225 | +static const struct phy_reg x2_port0_config_regs[] = { | |
| 226 | + {0x00000694, 0xc80060fa}, | |
| 227 | + {0x00000680, 0x3d4f78ea}, | |
| 228 | + {0x00000690, 0x10a0140b}, | |
| 229 | + {0x000006a8, 0xdf04010a}, | |
| 230 | + {0x00000700, 0x57050060}, | |
| 231 | + {0x00000710, 0x0030001c}, | |
| 232 | + {0x00000738, 0x5f004444}, | |
| 233 | + {0x0000073c, 0x78464204}, | |
| 234 | + {0x00000748, 0x7821f940}, | |
| 235 | + {0x0000074c, 0xb2000433}, | |
| 236 | + {0x00000494, 0xc80060fa}, | |
| 237 | + {0x00000480, 0x29ef5ed8}, | |
| 238 | + {0x00000490, 0x10a0540b}, | |
| 239 | + {0x000004a8, 0x7a01010a}, | |
| 240 | + {0x00000500, 0xef053460}, | |
| 241 | + {0x00000510, 0xe030101c}, | |
| 242 | + {0x00000538, 0xdf808444}, | |
| 243 | + {0x0000053c, 0xc8422204}, | |
| 244 | + {0x00000540, 0x0180088c}, | |
| 245 | + {0x00000574, 0x00000000}, | |
| 246 | + {0x00000294, 0xc80060fa}, | |
| 247 | + {0x00000280, 0xcb45b950}, | |
| 248 | + {0x00000290, 0x10a0540b}, | |
| 249 | + {0x000002a8, 0x8c01010a}, | |
| 250 | + {0x00000300, 0xef053460}, | |
| 251 | + {0x00000310, 0x8030101c}, | |
| 252 | + {0x00000338, 0x41808444}, | |
| 253 | + {0x0000033c, 0x32422204}, | |
| 254 | + {0x00000340, 0x0180088c}, | |
| 255 | + {0x00000374, 0x00000000}, | |
| 256 | + {0x00000000, 0x00000000} | |
| 257 | +}; | |
| 258 | + | |
| 259 | +static const struct phy_reg x2_port1_config_regs[] = { | |
| 260 | + {0x00000c94, 0xc80060fa}, | |
| 261 | + {0x00000c80, 0xcf47abea}, | |
| 262 | + {0x00000c90, 0x10a0840b}, | |
| 263 | + {0x00000ca8, 0xdf04010a}, | |
| 264 | + {0x00000d00, 0x57050060}, | |
| 265 | + {0x00000d10, 0x0030001c}, | |
| 266 | + {0x00000d38, 0x5f004444}, | |
| 267 | + {0x00000d3c, 0x78464204}, | |
| 268 | + {0x00000d48, 0x7821f940}, | |
| 269 | + {0x00000d4c, 0xb2000433}, | |
| 270 | + {0x00000a94, 0xc80060fa}, | |
| 271 | + {0x00000a80, 0x5a166ed8}, | |
| 272 | + {0x00000a90, 0x10a0540b}, | |
| 273 | + {0x00000aa8, 0x7a01010a}, | |
| 274 | + {0x00000b00, 0xef053460}, | |
| 275 | + {0x00000b10, 0xa030101c}, | |
| 276 | + {0x00000b38, 0xdf808444}, | |
| 277 | + {0x00000b3c, 0xc8422204}, | |
| 278 | + {0x00000b40, 0x0180088c}, | |
| 279 | + {0x00000b74, 0x00000000}, | |
| 280 | + {0x00000894, 0xc80060fa}, | |
| 281 | + {0x00000880, 0x4d4f21d0}, | |
| 282 | + {0x00000890, 0x10a0540b}, | |
| 283 | + {0x000008a8, 0x5601010a}, | |
| 284 | + {0x00000900, 0xef053460}, | |
| 285 | + {0x00000910, 0x8030101c}, | |
| 286 | + {0x00000938, 0xdf808444}, | |
| 287 | + {0x0000093c, 0xc8422204}, | |
| 288 | + {0x00000940, 0x0180088c}, | |
| 289 | + {0x00000974, 0x00000000}, | |
| 290 | + {0x00000000, 0x00000000} | |
| 291 | +}; | |
| 292 | + | |
| 293 | +static const struct phy_reg x2_port2_config_regs[] = { | |
| 294 | + {0x00001294, 0xc80060fa}, | |
| 295 | + {0x00001280, 0x08130cea}, | |
| 296 | + {0x00001290, 0x10a0140b}, | |
| 297 | + {0x000012a8, 0xd704010a}, | |
| 298 | + {0x00001300, 0x8d050060}, | |
| 299 | + {0x00001310, 0x0030001c}, | |
| 300 | + {0x00001338, 0xdf008444}, | |
| 301 | + {0x0000133c, 0x78422204}, | |
| 302 | + {0x00001348, 0x7821f940}, | |
| 303 | + {0x0000134c, 0x5a000433}, | |
| 304 | + {0x00001094, 0xc80060fa}, | |
| 305 | + {0x00001080, 0xade75dd8}, | |
| 306 | + {0x00001090, 0x10a0540b}, | |
| 307 | + {0x000010a8, 0xb101010a}, | |
| 308 | + {0x00001100, 0x33053460}, | |
| 309 | + {0x00001110, 0x0030101c}, | |
| 310 | + {0x00001138, 0xdf808444}, | |
| 311 | + {0x0000113c, 0xc8422204}, | |
| 312 | + {0x00001140, 0x8180088c}, | |
| 313 | + {0x00001174, 0x00000000}, | |
| 314 | + {0x00000e94, 0xc80060fa}, | |
| 315 | + {0x00000e80, 0x0fbf16d0}, | |
| 316 | + {0x00000e90, 0x10a0540b}, | |
| 317 | + {0x00000ea8, 0x7a01010a}, | |
| 318 | + {0x00000f00, 0xf5053460}, | |
| 319 | + {0x00000f10, 0xc030101c}, | |
| 320 | + {0x00000f38, 0xdf808444}, | |
| 321 | + {0x00000f3c, 0xc8422204}, | |
| 322 | + {0x00000f40, 0x8180088c}, | |
| 323 | + {0x00000000, 0x00000000} | |
| 324 | +}; | |
| 325 | + | |
| 326 | +static const struct phy_reg x2_port3_config_regs[] = { | |
| 327 | + {0x00001894, 0xc80060fa}, | |
| 328 | + {0x00001880, 0x0f90fd6a}, | |
| 329 | + {0x00001890, 0x10a0840b}, | |
| 330 | + {0x000018a8, 0xdf04010a}, | |
| 331 | + {0x00001900, 0x57050060}, | |
| 332 | + {0x00001910, 0x0030001c}, | |
| 333 | + {0x00001938, 0x5f004444}, | |
| 334 | + {0x0000193c, 0x78464204}, | |
| 335 | + {0x00001948, 0x7821f940}, | |
| 336 | + {0x0000194c, 0xb2000433}, | |
| 337 | + {0x00001694, 0xc80060fa}, | |
| 338 | + {0x00001680, 0x0ef6d058}, | |
| 339 | + {0x00001690, 0x10a0540b}, | |
| 340 | + {0x000016a8, 0x7a01010a}, | |
| 341 | + {0x00001700, 0x69053460}, | |
| 342 | + {0x00001710, 0xa030101c}, | |
| 343 | + {0x00001738, 0xdf808444}, | |
| 344 | + {0x0000173c, 0xc8422204}, | |
| 345 | + {0x00001740, 0x0180088c}, | |
| 346 | + {0x00001774, 0x00000000}, | |
| 347 | + {0x00001494, 0xc80060fa}, | |
| 348 | + {0x00001480, 0xf9d34bd0}, | |
| 349 | + {0x00001490, 0x10a0540b}, | |
| 350 | + {0x000014a8, 0x7a01010a}, | |
| 351 | + {0x00001500, 0x1b053460}, | |
| 352 | + {0x00001510, 0x0030101c}, | |
| 353 | + {0x00001538, 0xdf808444}, | |
| 354 | + {0x0000153c, 0xc8422204}, | |
| 355 | + {0x00001540, 0x8180088c}, | |
| 356 | + {0x00001574, 0x00000000}, | |
| 357 | + {0x00000000, 0x00000000} | |
| 358 | +}; | |
| 359 | + | |
| 360 | +static const struct phy_reg x4_port0_config_regs[] = { | |
| 361 | + {0x00000694, 0xc80060fa}, | |
| 362 | + {0x00000680, 0x3d4f78fa}, | |
| 363 | + {0x00000690, 0x10a0140b}, | |
| 364 | + {0x000006a8, 0xdf04010a}, | |
| 365 | + {0x00000700, 0x57050060}, | |
| 366 | + {0x00000710, 0x0030001c}, | |
| 367 | + {0x00000738, 0x5f004444}, | |
| 368 | + {0x0000073c, 0x78464204}, | |
| 369 | + {0x00000748, 0x7821f940}, | |
| 370 | + {0x0000074c, 0xb2000433}, | |
| 371 | + {0x00000494, 0xfe6030fa}, | |
| 372 | + {0x00000480, 0x29ef5ed8}, | |
| 373 | + {0x00000490, 0x10a0540b}, | |
| 374 | + {0x000004a8, 0x7a01010a}, | |
| 375 | + {0x00000500, 0xef053460}, | |
| 376 | + {0x00000510, 0xe030101c}, | |
| 377 | + {0x00000538, 0xdf808444}, | |
| 378 | + {0x0000053c, 0xc8422204}, | |
| 379 | + {0x00000540, 0x0180088c}, | |
| 380 | + {0x00000574, 0x00000004}, | |
| 381 | + {0x00000294, 0x23e030fa}, | |
| 382 | + {0x00000280, 0xcb45b950}, | |
| 383 | + {0x00000290, 0x10a0540b}, | |
| 384 | + {0x000002a8, 0x8c01010a}, | |
| 385 | + {0x00000300, 0xef053460}, | |
| 386 | + {0x00000310, 0x8030101c}, | |
| 387 | + {0x00000338, 0x41808444}, | |
| 388 | + {0x0000033c, 0x32422204}, | |
| 389 | + {0x00000340, 0x0180088c}, | |
| 390 | + {0x00000374, 0x00000004}, | |
| 391 | + {0x00000894, 0x5620b0fa}, | |
| 392 | + {0x00000880, 0x4d4f21dc}, | |
| 393 | + {0x00000890, 0x10a0540b}, | |
| 394 | + {0x000008a8, 0x5601010a}, | |
| 395 | + {0x00000900, 0xef053460}, | |
| 396 | + {0x00000910, 0x8030101c}, | |
| 397 | + {0x00000938, 0xdf808444}, | |
| 398 | + {0x0000093c, 0xc8422204}, | |
| 399 | + {0x00000940, 0x0180088c}, | |
| 400 | + {0x00000974, 0x00000004}, | |
| 401 | + {0x00000a94, 0xc91030fa}, | |
| 402 | + {0x00000a80, 0x5a166ecc}, | |
| 403 | + {0x00000a90, 0x10a0540b}, | |
| 404 | + {0x00000aa8, 0x5d01010a}, | |
| 405 | + {0x00000b00, 0xef053460}, | |
| 406 | + {0x00000b10, 0xa030101c}, | |
| 407 | + {0x00000b38, 0xdf808444}, | |
| 408 | + {0x00000b3c, 0xc8422204}, | |
| 409 | + {0x00000b40, 0x0180088c}, | |
| 410 | + {0x00000b74, 0x00000004}, | |
| 411 | + {0x00000000, 0x00000000} | |
| 412 | +}; | |
| 413 | + | |
| 414 | +static const struct phy_reg x4_port1_config_regs[] = { | |
| 415 | + {0x00000000, 0x00000000} | |
| 416 | +}; | |
| 417 | + | |
| 418 | +static const struct phy_reg x4_port2_config_regs[] = { | |
| 419 | + {0x00001294, 0x28f000fa}, | |
| 420 | + {0x00001280, 0x08130cfa}, | |
| 421 | + {0x00001290, 0x10c0140b}, | |
| 422 | + {0x000012a8, 0xd704010a}, | |
| 423 | + {0x00001300, 0x8d050060}, | |
| 424 | + {0x00001310, 0x0030001c}, | |
| 425 | + {0x00001338, 0xdf008444}, | |
| 426 | + {0x0000133c, 0x78422204}, | |
| 427 | + {0x00001348, 0x7821f940}, | |
| 428 | + {0x0000134c, 0x5a000433}, | |
| 429 | + {0x00001094, 0x2d20b0fa}, | |
| 430 | + {0x00001080, 0xade75dd8}, | |
| 431 | + {0x00001090, 0x10a0540b}, | |
| 432 | + {0x000010a8, 0xb101010a}, | |
| 433 | + {0x00001100, 0x33053460}, | |
| 434 | + {0x00001110, 0x0030101c}, | |
| 435 | + {0x00001138, 0xdf808444}, | |
| 436 | + {0x0000113c, 0xc8422204}, | |
| 437 | + {0x00001140, 0x8180088c}, | |
| 438 | + {0x00001174, 0x00000004}, | |
| 439 | + {0x00000e94, 0xd308d0fa}, | |
| 440 | + {0x00000e80, 0x0fbf16d0}, | |
| 441 | + {0x00000e90, 0x10a0540b}, | |
| 442 | + {0x00000ea8, 0x2c01010a}, | |
| 443 | + {0x00000f00, 0xf5053460}, | |
| 444 | + {0x00000f10, 0xc030101c}, | |
| 445 | + {0x00000f38, 0xdf808444}, | |
| 446 | + {0x00000f3c, 0xc8422204}, | |
| 447 | + {0x00000f40, 0x8180088c}, | |
| 448 | + {0x00000f74, 0x00000004}, | |
| 449 | + {0x00001494, 0x136850fa}, | |
| 450 | + {0x00001480, 0xf9d34bdc}, | |
| 451 | + {0x00001490, 0x10a0540b}, | |
| 452 | + {0x000014a8, 0x5a01010a}, | |
| 453 | + {0x00001500, 0x1b053460}, | |
| 454 | + {0x00001510, 0x0030101c}, | |
| 455 | + {0x00001538, 0xdf808444}, | |
| 456 | + {0x0000153c, 0xc8422204}, | |
| 457 | + {0x00001540, 0x8180088c}, | |
| 458 | + {0x00001574, 0x00000004}, | |
| 459 | + {0x00001694, 0x3050d0fa}, | |
| 460 | + {0x00001680, 0x0ef6d04c}, | |
| 461 | + {0x00001690, 0x10a0540b}, | |
| 462 | + {0x000016a8, 0xe301010a}, | |
| 463 | + {0x00001700, 0x69053460}, | |
| 464 | + {0x00001710, 0xa030101c}, | |
| 465 | + {0x00001738, 0xdf808444}, | |
| 466 | + {0x0000173c, 0xc8422204}, | |
| 467 | + {0x00001740, 0x0180088c}, | |
| 468 | + {0x00001774, 0x00000004}, | |
| 469 | + {0x00000000, 0x00000000} | |
| 470 | +}; | |
| 471 | + | |
| 472 | +static const struct phy_reg x4_port3_config_regs[] = { | |
| 473 | + {0x00000000, 0x00000000} | |
| 474 | +}; | |
| 475 | + | |
| 476 | +static const struct phy_reg *x1_config_regs[4] = { | |
| 477 | + x1_port0_config_regs, | |
| 478 | + x1_port1_config_regs, | |
| 479 | + x1_port2_config_regs, | |
| 480 | + x1_port3_config_regs | |
| 481 | +}; | |
| 482 | + | |
| 483 | +static const struct phy_reg *x2_config_regs[4] = { | |
| 484 | + x2_port0_config_regs, | |
| 485 | + x2_port1_config_regs, | |
| 486 | + x2_port2_config_regs, | |
| 487 | + x2_port3_config_regs | |
| 488 | +}; | |
| 489 | + | |
| 490 | +static const struct phy_reg *x4_config_regs[4] = { | |
| 491 | + x4_port0_config_regs, | |
| 492 | + x4_port1_config_regs, | |
| 493 | + x4_port2_config_regs, | |
| 494 | + x4_port3_config_regs | |
| 495 | +}; | |
| 496 | + | |
| 497 | +static const struct phy_reg **config_regs[3] = { | |
| 498 | + x1_config_regs, | |
| 499 | + x2_config_regs, | |
| 500 | + x4_config_regs | |
| 501 | +}; | |
| 502 | + | |
| 503 | +static int ipu6_isys_mcd_phy_powerup_ack(struct ipu6_isys *isys, u8 id) | |
| 504 | +{ | |
| 505 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 506 | + void __iomem *isys_base = isys->pdata->base; | |
| 507 | + u32 val; | |
| 508 | + int ret; | |
| 509 | + | |
| 510 | + val = readl(isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id)); | |
| 511 | + val |= CSI_REG_HUB_GPREG_PHY_CTL_PWR_EN; | |
| 512 | + writel(val, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id)); | |
| 513 | + | |
| 514 | + ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id), | |
| 515 | + val, val & CSI_REG_HUB_GPREG_PHY_POWER_ACK, | |
| 516 | + 200, MCD_PHY_POWER_STATUS_TIMEOUT); | |
| 517 | + if (ret) | |
| 518 | + dev_err(dev, "PHY%d powerup ack timeout", id); | |
| 519 | + | |
| 520 | + return ret; | |
| 521 | +} | |
| 522 | + | |
| 523 | +static int ipu6_isys_mcd_phy_powerdown_ack(struct ipu6_isys *isys, u8 id) | |
| 524 | +{ | |
| 525 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 526 | + void __iomem *isys_base = isys->pdata->base; | |
| 527 | + u32 val; | |
| 528 | + int ret; | |
| 529 | + | |
| 530 | + writel(0, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id)); | |
| 531 | + ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id), | |
| 532 | + val, !(val & CSI_REG_HUB_GPREG_PHY_POWER_ACK), | |
| 533 | + 200, MCD_PHY_POWER_STATUS_TIMEOUT); | |
| 534 | + if (ret) | |
| 535 | + dev_err(dev, "PHY%d powerdown ack timeout", id); | |
| 536 | + | |
| 537 | + return ret; | |
| 538 | +} | |
| 539 | + | |
| 540 | +static void ipu6_isys_mcd_phy_reset(struct ipu6_isys *isys, u8 id, bool assert) | |
| 541 | +{ | |
| 542 | + void __iomem *isys_base = isys->pdata->base; | |
| 543 | + u32 val; | |
| 544 | + | |
| 545 | + val = readl(isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id)); | |
| 546 | + if (assert) | |
| 547 | + val |= CSI_REG_HUB_GPREG_PHY_CTL_RESET; | |
| 548 | + else | |
| 549 | + val &= ~(CSI_REG_HUB_GPREG_PHY_CTL_RESET); | |
| 550 | + | |
| 551 | + writel(val, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id)); | |
| 552 | +} | |
| 553 | + | |
| 554 | +static int ipu6_isys_mcd_phy_ready(struct ipu6_isys *isys, u8 id) | |
| 555 | +{ | |
| 556 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 557 | + void __iomem *isys_base = isys->pdata->base; | |
| 558 | + u32 val; | |
| 559 | + int ret; | |
| 560 | + | |
| 561 | + ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id), | |
| 562 | + val, val & CSI_REG_HUB_GPREG_PHY_READY, | |
| 563 | + 200, MCD_PHY_POWER_STATUS_TIMEOUT); | |
| 564 | + if (ret) | |
| 565 | + dev_err(dev, "PHY%d ready ack timeout", id); | |
| 566 | + | |
| 567 | + return ret; | |
| 568 | +} | |
| 569 | + | |
| 570 | +static void ipu6_isys_mcd_phy_common_init(struct ipu6_isys *isys) | |
| 571 | +{ | |
| 572 | + struct ipu6_bus_device *adev = isys->adev; | |
| 573 | + struct ipu6_device *isp = adev->isp; | |
| 574 | + void __iomem *isp_base = isp->base; | |
| 575 | + struct sensor_async_sd *s_asd; | |
| 576 | + struct v4l2_async_connection *asc; | |
| 577 | + void __iomem *phy_base; | |
| 578 | + unsigned int i; | |
| 579 | + u8 phy_id; | |
| 580 | + | |
| 581 | + list_for_each_entry(asc, &isys->notifier.done_list, asc_entry) { | |
| 582 | + s_asd = container_of(asc, struct sensor_async_sd, asc); | |
| 583 | + phy_id = s_asd->csi2.port / 4; | |
| 584 | + phy_base = isp_base + IPU6_ISYS_MCD_PHY_BASE(phy_id); | |
| 585 | + | |
| 586 | + for (i = 0; i < ARRAY_SIZE(common_init_regs); i++) | |
| 587 | + writel(common_init_regs[i].val, | |
| 588 | + phy_base + common_init_regs[i].reg); | |
| 589 | + } | |
| 590 | +} | |
| 591 | + | |
| 592 | +static int ipu6_isys_driver_port_to_phy_port(struct ipu6_isys_csi2_config *cfg) | |
| 593 | +{ | |
| 594 | + int phy_port; | |
| 595 | + int ret; | |
| 596 | + | |
| 597 | + if (!(cfg->nlanes == 4 || cfg->nlanes == 2 || cfg->nlanes == 1)) | |
| 598 | + return -EINVAL; | |
| 599 | + | |
| 600 | + /* B,F -> C0 A,E -> C1 C,G -> C2 D,H -> C4 */ | |
| 601 | + /* normalize driver port number */ | |
| 602 | + phy_port = cfg->port % 4; | |
| 603 | + | |
| 604 | + /* swap port number only for A and B */ | |
| 605 | + if (phy_port == 0) | |
| 606 | + phy_port = 1; | |
| 607 | + else if (phy_port == 1) | |
| 608 | + phy_port = 0; | |
| 609 | + | |
| 610 | + ret = phy_port; | |
| 611 | + | |
| 612 | + /* check validity per lane configuration */ | |
| 613 | + if (cfg->nlanes == 4 && !(phy_port == 0 || phy_port == 2)) | |
| 614 | + ret = -EINVAL; | |
| 615 | + else if ((cfg->nlanes == 2 || cfg->nlanes == 1) && | |
| 616 | + !(phy_port >= 0 && phy_port <= 3)) | |
| 617 | + ret = -EINVAL; | |
| 618 | + | |
| 619 | + return ret; | |
| 620 | +} | |
| 621 | + | |
| 622 | +static int ipu6_isys_mcd_phy_config(struct ipu6_isys *isys) | |
| 623 | +{ | |
| 624 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 625 | + struct ipu6_bus_device *adev = isys->adev; | |
| 626 | + const struct phy_reg **phy_config_regs; | |
| 627 | + struct ipu6_device *isp = adev->isp; | |
| 628 | + void __iomem *isp_base = isp->base; | |
| 629 | + struct sensor_async_sd *s_asd; | |
| 630 | + struct ipu6_isys_csi2_config cfg; | |
| 631 | + struct v4l2_async_connection *asc; | |
| 632 | + int phy_port, phy_id; | |
| 633 | + unsigned int i; | |
| 634 | + void __iomem *phy_base; | |
| 635 | + | |
| 636 | + list_for_each_entry(asc, &isys->notifier.done_list, asc_entry) { | |
| 637 | + s_asd = container_of(asc, struct sensor_async_sd, asc); | |
| 638 | + cfg.port = s_asd->csi2.port; | |
| 639 | + cfg.nlanes = s_asd->csi2.nlanes; | |
| 640 | + phy_port = ipu6_isys_driver_port_to_phy_port(&cfg); | |
| 641 | + if (phy_port < 0) { | |
| 642 | + dev_err(dev, "invalid port %d for lane %d", cfg.port, | |
| 643 | + cfg.nlanes); | |
| 644 | + return -ENXIO; | |
| 645 | + } | |
| 646 | + | |
| 647 | + phy_id = cfg.port / 4; | |
| 648 | + phy_base = isp_base + IPU6_ISYS_MCD_PHY_BASE(phy_id); | |
| 649 | + dev_dbg(dev, "port%d PHY%u lanes %u\n", cfg.port, phy_id, | |
| 650 | + cfg.nlanes); | |
| 651 | + | |
| 652 | + phy_config_regs = config_regs[cfg.nlanes / 2]; | |
| 653 | + cfg.port = phy_port; | |
| 654 | + for (i = 0; phy_config_regs[cfg.port][i].reg; i++) | |
| 655 | + writel(phy_config_regs[cfg.port][i].val, | |
| 656 | + phy_base + phy_config_regs[cfg.port][i].reg); | |
| 657 | + } | |
| 658 | + | |
| 659 | + return 0; | |
| 660 | +} | |
| 661 | + | |
| 662 | +#define CSI_MCD_PHY_NUM 2 | |
| 663 | +static refcount_t phy_power_ref_count[CSI_MCD_PHY_NUM]; | |
| 664 | + | |
| 665 | +int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys, | |
| 666 | + struct ipu6_isys_csi2_config *cfg, | |
| 667 | + const struct ipu6_isys_csi2_timing *timing, | |
| 668 | + bool on) | |
| 669 | +{ | |
| 670 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 671 | + void __iomem *isys_base = isys->pdata->base; | |
| 672 | + u8 port, phy_id; | |
| 673 | + refcount_t *ref; | |
| 674 | + int ret; | |
| 675 | + | |
| 676 | + port = cfg->port; | |
| 677 | + phy_id = port / 4; | |
| 678 | + | |
| 679 | + ref = &phy_power_ref_count[phy_id]; | |
| 680 | + | |
| 681 | + dev_dbg(dev, "for phy %d port %d, lanes: %d\n", phy_id, port, | |
| 682 | + cfg->nlanes); | |
| 683 | + | |
| 684 | + if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) { | |
| 685 | + dev_warn(dev, "invalid port ID %d\n", port); | |
| 686 | + return -EINVAL; | |
| 687 | + } | |
| 688 | + | |
| 689 | + if (on) { | |
| 690 | + if (refcount_read(ref)) { | |
| 691 | + dev_dbg(dev, "for phy %d is already UP", phy_id); | |
| 692 | + refcount_inc(ref); | |
| 693 | + return 0; | |
| 694 | + } | |
| 695 | + | |
| 696 | + ret = ipu6_isys_mcd_phy_powerup_ack(isys, phy_id); | |
| 697 | + if (ret) | |
| 698 | + return ret; | |
| 699 | + | |
| 700 | + ipu6_isys_mcd_phy_reset(isys, phy_id, 0); | |
| 701 | + ipu6_isys_mcd_phy_common_init(isys); | |
| 702 | + | |
| 703 | + ret = ipu6_isys_mcd_phy_config(isys); | |
| 704 | + if (ret) | |
| 705 | + return ret; | |
| 706 | + | |
| 707 | + ipu6_isys_mcd_phy_reset(isys, phy_id, 1); | |
| 708 | + ret = ipu6_isys_mcd_phy_ready(isys, phy_id); | |
| 709 | + if (ret) | |
| 710 | + return ret; | |
| 711 | + | |
| 712 | + refcount_set(ref, 1); | |
| 713 | + return 0; | |
| 714 | + } | |
| 715 | + | |
| 716 | + if (!refcount_dec_and_test(ref)) | |
| 717 | + return 0; | |
| 718 | + | |
| 719 | + return ipu6_isys_mcd_phy_powerdown_ack(isys, phy_id); | |
| 720 | +} | |
| @@ -0,0 +1,810 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | +#include <linux/atomic.h> | |
| 6 | +#include <linux/bug.h> | |
| 7 | +#include <linux/device.h> | |
| 8 | +#include <linux/list.h> | |
| 9 | +#include <linux/lockdep.h> | |
| 10 | +#include <linux/mutex.h> | |
| 11 | +#include <linux/spinlock.h> | |
| 12 | +#include <linux/types.h> | |
| 13 | + | |
| 14 | +#include <media/media-entity.h> | |
| 15 | +#include <media/v4l2-subdev.h> | |
| 16 | +#include <media/videobuf2-dma-contig.h> | |
| 17 | +#include <media/videobuf2-v4l2.h> | |
| 18 | + | |
| 19 | +#include "ipu6-bus.h" | |
| 20 | +#include "ipu6-fw-isys.h" | |
| 21 | +#include "ipu6-isys.h" | |
| 22 | +#include "ipu6-isys-video.h" | |
| 23 | + | |
| 24 | +static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers, | |
| 25 | + unsigned int *num_planes, unsigned int sizes[], | |
| 26 | + struct device *alloc_devs[]) | |
| 27 | +{ | |
| 28 | + struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q); | |
| 29 | + struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); | |
| 30 | + struct device *dev = &av->isys->adev->auxdev.dev; | |
| 31 | + u32 size = ipu6_isys_get_data_size(av); | |
| 32 | + | |
| 33 | + /* num_planes == 0: we're being called through VIDIOC_REQBUFS */ | |
| 34 | + if (!*num_planes) { | |
| 35 | + sizes[0] = size; | |
| 36 | + } else if (sizes[0] < size) { | |
| 37 | + dev_dbg(dev, "%s: queue setup: size %u < %u\n", | |
| 38 | + av->vdev.name, sizes[0], size); | |
| 39 | + return -EINVAL; | |
| 40 | + } | |
| 41 | + | |
| 42 | + *num_planes = 1; | |
| 43 | + | |
| 44 | + return 0; | |
| 45 | +} | |
| 46 | + | |
| 47 | +static int ipu6_isys_buf_prepare(struct vb2_buffer *vb) | |
| 48 | +{ | |
| 49 | + struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); | |
| 50 | + struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); | |
| 51 | + struct device *dev = &av->isys->adev->auxdev.dev; | |
| 52 | + u32 bytesperline = ipu6_isys_get_bytes_per_line(av); | |
| 53 | + u32 height = ipu6_isys_get_frame_height(av); | |
| 54 | + u32 size = ipu6_isys_get_data_size(av); | |
| 55 | + | |
| 56 | + dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n", | |
| 57 | + av->vdev.name, size, vb2_plane_size(vb, 0)); | |
| 58 | + | |
| 59 | + if (size > vb2_plane_size(vb, 0)) | |
| 60 | + return -EINVAL; | |
| 61 | + | |
| 62 | + vb2_set_plane_payload(vb, 0, bytesperline * height); | |
| 63 | + | |
| 64 | + return 0; | |
| 65 | +} | |
| 66 | + | |
| 67 | +/* | |
| 68 | + * Queue a buffer list back to incoming or active queues. The buffers | |
| 69 | + * are removed from the buffer list. | |
| 70 | + */ | |
| 71 | +void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl, | |
| 72 | + unsigned long op_flags, | |
| 73 | + enum vb2_buffer_state state) | |
| 74 | +{ | |
| 75 | + struct ipu6_isys_buffer *ib, *ib_safe; | |
| 76 | + unsigned long flags; | |
| 77 | + bool first = true; | |
| 78 | + | |
| 79 | + if (!bl) | |
| 80 | + return; | |
| 81 | + | |
| 82 | + WARN_ON_ONCE(!bl->nbufs); | |
| 83 | + WARN_ON_ONCE(op_flags & IPU6_ISYS_BUFFER_LIST_FL_ACTIVE && | |
| 84 | + op_flags & IPU6_ISYS_BUFFER_LIST_FL_INCOMING); | |
| 85 | + | |
| 86 | + list_for_each_entry_safe(ib, ib_safe, &bl->head, head) { | |
| 87 | + struct ipu6_isys_video *av; | |
| 88 | + struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); | |
| 89 | + struct ipu6_isys_queue *aq = | |
| 90 | + vb2_queue_to_isys_queue(vb->vb2_queue); | |
| 91 | + struct device *dev; | |
| 92 | + | |
| 93 | + av = ipu6_isys_queue_to_video(aq); | |
| 94 | + dev = &av->isys->adev->auxdev.dev; | |
| 95 | + spin_lock_irqsave(&aq->lock, flags); | |
| 96 | + list_del(&ib->head); | |
| 97 | + if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_ACTIVE) | |
| 98 | + list_add(&ib->head, &aq->active); | |
| 99 | + else if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_INCOMING) | |
| 100 | + list_add_tail(&ib->head, &aq->incoming); | |
| 101 | + spin_unlock_irqrestore(&aq->lock, flags); | |
| 102 | + | |
| 103 | + if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_SET_STATE) | |
| 104 | + vb2_buffer_done(vb, state); | |
| 105 | + | |
| 106 | + if (first) { | |
| 107 | + dev_dbg(dev, | |
| 108 | + "queue buf list %p flags %lx, s %d, %d bufs\n", | |
| 109 | + bl, op_flags, state, bl->nbufs); | |
| 110 | + first = false; | |
| 111 | + } | |
| 112 | + | |
| 113 | + bl->nbufs--; | |
| 114 | + } | |
| 115 | + | |
| 116 | + WARN_ON(bl->nbufs); | |
| 117 | +} | |
| 118 | + | |
| 119 | +/* | |
| 120 | + * flush_firmware_streamon_fail() - Flush in cases where requests may | |
| 121 | + * have been queued to firmware and the *firmware streamon fails for a | |
| 122 | + * reason or another. | |
| 123 | + */ | |
| 124 | +static void flush_firmware_streamon_fail(struct ipu6_isys_stream *stream) | |
| 125 | +{ | |
| 126 | + struct device *dev = &stream->isys->adev->auxdev.dev; | |
| 127 | + struct ipu6_isys_queue *aq; | |
| 128 | + unsigned long flags; | |
| 129 | + | |
| 130 | + lockdep_assert_held(&stream->mutex); | |
| 131 | + | |
| 132 | + list_for_each_entry(aq, &stream->queues, node) { | |
| 133 | + struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); | |
| 134 | + struct ipu6_isys_buffer *ib, *ib_safe; | |
| 135 | + | |
| 136 | + spin_lock_irqsave(&aq->lock, flags); | |
| 137 | + list_for_each_entry_safe(ib, ib_safe, &aq->active, head) { | |
| 138 | + struct vb2_buffer *vb = | |
| 139 | + ipu6_isys_buffer_to_vb2_buffer(ib); | |
| 140 | + | |
| 141 | + list_del(&ib->head); | |
| 142 | + if (av->streaming) { | |
| 143 | + dev_dbg(dev, | |
| 144 | + "%s: queue buffer %u back to incoming\n", | |
| 145 | + av->vdev.name, vb->index); | |
| 146 | + /* Queue already streaming, return to driver. */ | |
| 147 | + list_add(&ib->head, &aq->incoming); | |
| 148 | + continue; | |
| 149 | + } | |
| 150 | + /* Queue not yet streaming, return to user. */ | |
| 151 | + dev_dbg(dev, "%s: return %u back to videobuf2\n", | |
| 152 | + av->vdev.name, vb->index); | |
| 153 | + vb2_buffer_done(ipu6_isys_buffer_to_vb2_buffer(ib), | |
| 154 | + VB2_BUF_STATE_QUEUED); | |
| 155 | + } | |
| 156 | + spin_unlock_irqrestore(&aq->lock, flags); | |
| 157 | + } | |
| 158 | +} | |
| 159 | + | |
| 160 | +/* | |
| 161 | + * Attempt obtaining a buffer list from the incoming queues, a list of buffers | |
| 162 | + * that contains one entry from each video buffer queue. If a buffer can't be | |
| 163 | + * obtained from every queue, the buffers are returned back to the queue. | |
| 164 | + */ | |
| 165 | +static int buffer_list_get(struct ipu6_isys_stream *stream, | |
| 166 | + struct ipu6_isys_buffer_list *bl) | |
| 167 | +{ | |
| 168 | + struct device *dev = &stream->isys->adev->auxdev.dev; | |
| 169 | + struct ipu6_isys_queue *aq; | |
| 170 | + unsigned long flags; | |
| 171 | + unsigned long buf_flag = IPU6_ISYS_BUFFER_LIST_FL_INCOMING; | |
| 172 | + | |
| 173 | + bl->nbufs = 0; | |
| 174 | + INIT_LIST_HEAD(&bl->head); | |
| 175 | + | |
| 176 | + list_for_each_entry(aq, &stream->queues, node) { | |
| 177 | + struct ipu6_isys_buffer *ib; | |
| 178 | + | |
| 179 | + spin_lock_irqsave(&aq->lock, flags); | |
| 180 | + if (list_empty(&aq->incoming)) { | |
| 181 | + spin_unlock_irqrestore(&aq->lock, flags); | |
| 182 | + if (!list_empty(&bl->head)) | |
| 183 | + ipu6_isys_buffer_list_queue(bl, buf_flag, 0); | |
| 184 | + return -ENODATA; | |
| 185 | + } | |
| 186 | + | |
| 187 | + ib = list_last_entry(&aq->incoming, | |
| 188 | + struct ipu6_isys_buffer, head); | |
| 189 | + | |
| 190 | + dev_dbg(dev, "buffer: %s: buffer %u\n", | |
| 191 | + ipu6_isys_queue_to_video(aq)->vdev.name, | |
| 192 | + ipu6_isys_buffer_to_vb2_buffer(ib)->index); | |
| 193 | + list_del(&ib->head); | |
| 194 | + list_add(&ib->head, &bl->head); | |
| 195 | + spin_unlock_irqrestore(&aq->lock, flags); | |
| 196 | + | |
| 197 | + bl->nbufs++; | |
| 198 | + } | |
| 199 | + | |
| 200 | + dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs); | |
| 201 | + | |
| 202 | + return 0; | |
| 203 | +} | |
| 204 | + | |
| 205 | +static void | |
| 206 | +ipu6_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, | |
| 207 | + struct ipu6_fw_isys_frame_buff_set_abi *set) | |
| 208 | +{ | |
| 209 | + struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); | |
| 210 | + | |
| 211 | + set->output_pins[aq->fw_output].addr = | |
| 212 | + vb2_dma_contig_plane_dma_addr(vb, 0); | |
| 213 | + set->output_pins[aq->fw_output].out_buf_id = vb->index + 1; | |
| 214 | +} | |
| 215 | + | |
| 216 | +/* | |
| 217 | + * Convert a buffer list to a isys fw ABI framebuffer set. The | |
| 218 | + * buffer list is not modified. | |
| 219 | + */ | |
| 220 | +#define IPU6_ISYS_FRAME_NUM_THRESHOLD (30) | |
| 221 | +void | |
| 222 | +ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set, | |
| 223 | + struct ipu6_isys_stream *stream, | |
| 224 | + struct ipu6_isys_buffer_list *bl) | |
| 225 | +{ | |
| 226 | + struct ipu6_isys_buffer *ib; | |
| 227 | + | |
| 228 | + WARN_ON(!bl->nbufs); | |
| 229 | + | |
| 230 | + set->send_irq_sof = 1; | |
| 231 | + set->send_resp_sof = 1; | |
| 232 | + set->send_irq_eof = 0; | |
| 233 | + set->send_resp_eof = 0; | |
| 234 | + | |
| 235 | + if (stream->streaming) | |
| 236 | + set->send_irq_capture_ack = 0; | |
| 237 | + else | |
| 238 | + set->send_irq_capture_ack = 1; | |
| 239 | + set->send_irq_capture_done = 0; | |
| 240 | + | |
| 241 | + set->send_resp_capture_ack = 1; | |
| 242 | + set->send_resp_capture_done = 1; | |
| 243 | + if (atomic_read(&stream->sequence) >= IPU6_ISYS_FRAME_NUM_THRESHOLD) { | |
| 244 | + set->send_resp_capture_ack = 0; | |
| 245 | + set->send_resp_capture_done = 0; | |
| 246 | + } | |
| 247 | + | |
| 248 | + list_for_each_entry(ib, &bl->head, head) { | |
| 249 | + struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); | |
| 250 | + | |
| 251 | + ipu6_isys_buf_to_fw_frame_buf_pin(vb, set); | |
| 252 | + } | |
| 253 | +} | |
| 254 | + | |
| 255 | +/* Start streaming for real. The buffer list must be available. */ | |
| 256 | +static int ipu6_isys_stream_start(struct ipu6_isys_video *av, | |
| 257 | + struct ipu6_isys_buffer_list *bl, bool error) | |
| 258 | +{ | |
| 259 | + struct ipu6_isys_stream *stream = av->stream; | |
| 260 | + struct device *dev = &stream->isys->adev->auxdev.dev; | |
| 261 | + struct ipu6_isys_buffer_list __bl; | |
| 262 | + int ret; | |
| 263 | + | |
| 264 | + mutex_lock(&stream->isys->stream_mutex); | |
| 265 | + ret = ipu6_isys_video_set_streaming(av, 1, bl); | |
| 266 | + mutex_unlock(&stream->isys->stream_mutex); | |
| 267 | + if (ret) | |
| 268 | + goto out_requeue; | |
| 269 | + | |
| 270 | + stream->streaming = 1; | |
| 271 | + | |
| 272 | + bl = &__bl; | |
| 273 | + | |
| 274 | + do { | |
| 275 | + struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL; | |
| 276 | + struct isys_fw_msgs *msg; | |
| 277 | + u16 send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE; | |
| 278 | + | |
| 279 | + ret = buffer_list_get(stream, bl); | |
| 280 | + if (ret < 0) | |
| 281 | + break; | |
| 282 | + | |
| 283 | + msg = ipu6_get_fw_msg_buf(stream); | |
| 284 | + if (!msg) | |
| 285 | + return -ENOMEM; | |
| 286 | + | |
| 287 | + buf = &msg->fw_msg.frame; | |
| 288 | + ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl); | |
| 289 | + ipu6_fw_isys_dump_frame_buff_set(dev, buf, | |
| 290 | + stream->nr_output_pins); | |
| 291 | + ipu6_isys_buffer_list_queue(bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, | |
| 292 | + 0); | |
| 293 | + ret = ipu6_fw_isys_complex_cmd(stream->isys, | |
| 294 | + stream->stream_handle, buf, | |
| 295 | + msg->dma_addr, sizeof(*buf), | |
| 296 | + send_type); | |
| 297 | + } while (!WARN_ON(ret)); | |
| 298 | + | |
| 299 | + return 0; | |
| 300 | + | |
| 301 | +out_requeue: | |
| 302 | + if (bl && bl->nbufs) | |
| 303 | + ipu6_isys_buffer_list_queue(bl, | |
| 304 | + (IPU6_ISYS_BUFFER_LIST_FL_INCOMING | | |
| 305 | + error) ? | |
| 306 | + IPU6_ISYS_BUFFER_LIST_FL_SET_STATE : | |
| 307 | + 0, error ? VB2_BUF_STATE_ERROR : | |
| 308 | + VB2_BUF_STATE_QUEUED); | |
| 309 | + flush_firmware_streamon_fail(stream); | |
| 310 | + | |
| 311 | + return ret; | |
| 312 | +} | |
| 313 | + | |
| 314 | +static void buf_queue(struct vb2_buffer *vb) | |
| 315 | +{ | |
| 316 | + struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); | |
| 317 | + struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); | |
| 318 | + struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); | |
| 319 | + struct ipu6_isys_video_buffer *ivb = | |
| 320 | + vb2_buffer_to_ipu6_isys_video_buffer(vvb); | |
| 321 | + struct ipu6_isys_buffer *ib = &ivb->ib; | |
| 322 | + struct device *dev = &av->isys->adev->auxdev.dev; | |
| 323 | + struct media_pipeline *media_pipe = | |
| 324 | + media_entity_pipeline(&av->vdev.entity); | |
| 325 | + struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL; | |
| 326 | + struct ipu6_isys_stream *stream = av->stream; | |
| 327 | + struct ipu6_isys_buffer_list bl; | |
| 328 | + struct isys_fw_msgs *msg; | |
| 329 | + unsigned long flags; | |
| 330 | + dma_addr_t dma; | |
| 331 | + int ret; | |
| 332 | + | |
| 333 | + dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name); | |
| 334 | + | |
| 335 | + dma = vb2_dma_contig_plane_dma_addr(vb, 0); | |
| 336 | + dev_dbg(dev, "iova: iova %pad\n", &dma); | |
| 337 | + | |
| 338 | + spin_lock_irqsave(&aq->lock, flags); | |
| 339 | + list_add(&ib->head, &aq->incoming); | |
| 340 | + spin_unlock_irqrestore(&aq->lock, flags); | |
| 341 | + | |
| 342 | + if (!media_pipe || !vb->vb2_queue->start_streaming_called) { | |
| 343 | + dev_dbg(dev, "media pipeline is not ready for %s\n", | |
| 344 | + av->vdev.name); | |
| 345 | + return; | |
| 346 | + } | |
| 347 | + | |
| 348 | + mutex_lock(&stream->mutex); | |
| 349 | + | |
| 350 | + if (stream->nr_streaming != stream->nr_queues) { | |
| 351 | + dev_dbg(dev, "not streaming yet, adding to incoming\n"); | |
| 352 | + goto out; | |
| 353 | + } | |
| 354 | + | |
| 355 | + /* | |
| 356 | + * We just put one buffer to the incoming list of this queue | |
| 357 | + * (above). Let's see whether all queues in the pipeline would | |
| 358 | + * have a buffer. | |
| 359 | + */ | |
| 360 | + ret = buffer_list_get(stream, &bl); | |
| 361 | + if (ret < 0) { | |
| 362 | + dev_dbg(dev, "No buffers available\n"); | |
| 363 | + goto out; | |
| 364 | + } | |
| 365 | + | |
| 366 | + msg = ipu6_get_fw_msg_buf(stream); | |
| 367 | + if (!msg) { | |
| 368 | + ret = -ENOMEM; | |
| 369 | + goto out; | |
| 370 | + } | |
| 371 | + | |
| 372 | + buf = &msg->fw_msg.frame; | |
| 373 | + ipu6_isys_buf_to_fw_frame_buf(buf, stream, &bl); | |
| 374 | + ipu6_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins); | |
| 375 | + | |
| 376 | + if (!stream->streaming) { | |
| 377 | + ret = ipu6_isys_stream_start(av, &bl, true); | |
| 378 | + if (ret) | |
| 379 | + dev_err(dev, "stream start failed.\n"); | |
| 380 | + goto out; | |
| 381 | + } | |
| 382 | + | |
| 383 | + /* | |
| 384 | + * We must queue the buffers in the buffer list to the | |
| 385 | + * appropriate video buffer queues BEFORE passing them to the | |
| 386 | + * firmware since we could get a buffer event back before we | |
| 387 | + * have queued them ourselves to the active queue. | |
| 388 | + */ | |
| 389 | + ipu6_isys_buffer_list_queue(&bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0); | |
| 390 | + | |
| 391 | + ret = ipu6_fw_isys_complex_cmd(stream->isys, stream->stream_handle, | |
| 392 | + buf, msg->dma_addr, sizeof(*buf), | |
| 393 | + IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE); | |
| 394 | + if (ret < 0) | |
| 395 | + dev_err(dev, "send stream capture failed\n"); | |
| 396 | + | |
| 397 | +out: | |
| 398 | + mutex_unlock(&stream->mutex); | |
| 399 | +} | |
| 400 | + | |
| 401 | +static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq) | |
| 402 | +{ | |
| 403 | + struct v4l2_mbus_framefmt format; | |
| 404 | + struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); | |
| 405 | + struct device *dev = &av->isys->adev->auxdev.dev; | |
| 406 | + struct media_pad *remote_pad = | |
| 407 | + media_pad_remote_pad_first(av->vdev.entity.pads); | |
| 408 | + struct v4l2_subdev *sd; | |
| 409 | + u32 r_stream, code; | |
| 410 | + int ret; | |
| 411 | + | |
| 412 | + if (!remote_pad) | |
| 413 | + return -ENOTCONN; | |
| 414 | + | |
| 415 | + sd = media_entity_to_v4l2_subdev(remote_pad->entity); | |
| 416 | + r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, remote_pad->index); | |
| 417 | + | |
| 418 | + ret = ipu6_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream, | |
| 419 | + &format); | |
| 420 | + | |
| 421 | + if (ret) { | |
| 422 | + dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n", | |
| 423 | + sd->entity.name, remote_pad->index, r_stream); | |
| 424 | + return ret; | |
| 425 | + } | |
| 426 | + | |
| 427 | + if (format.width != ipu6_isys_get_frame_width(av) || | |
| 428 | + format.height != ipu6_isys_get_frame_height(av)) { | |
| 429 | + dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n", | |
| 430 | + ipu6_isys_get_frame_width(av), | |
| 431 | + ipu6_isys_get_frame_height(av), format.width, | |
| 432 | + format.height); | |
| 433 | + return -EINVAL; | |
| 434 | + } | |
| 435 | + | |
| 436 | + code = ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0)->code; | |
| 437 | + if (format.code != code) { | |
| 438 | + dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n", | |
| 439 | + code, format.code); | |
| 440 | + return -EINVAL; | |
| 441 | + } | |
| 442 | + | |
| 443 | + return 0; | |
| 444 | +} | |
| 445 | + | |
| 446 | +static void return_buffers(struct ipu6_isys_queue *aq, | |
| 447 | + enum vb2_buffer_state state) | |
| 448 | +{ | |
| 449 | + struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); | |
| 450 | + struct ipu6_isys_buffer *ib; | |
| 451 | + bool need_reset = false; | |
| 452 | + unsigned long flags; | |
| 453 | + | |
| 454 | + spin_lock_irqsave(&aq->lock, flags); | |
| 455 | + while (!list_empty(&aq->incoming)) { | |
| 456 | + struct vb2_buffer *vb; | |
| 457 | + | |
| 458 | + ib = list_first_entry(&aq->incoming, struct ipu6_isys_buffer, | |
| 459 | + head); | |
| 460 | + vb = ipu6_isys_buffer_to_vb2_buffer(ib); | |
| 461 | + list_del(&ib->head); | |
| 462 | + spin_unlock_irqrestore(&aq->lock, flags); | |
| 463 | + | |
| 464 | + vb2_buffer_done(vb, state); | |
| 465 | + | |
| 466 | + spin_lock_irqsave(&aq->lock, flags); | |
| 467 | + } | |
| 468 | + | |
| 469 | + /* | |
| 470 | + * Something went wrong (FW crash / HW hang / not all buffers | |
| 471 | + * returned from isys) if there are still buffers queued in active | |
| 472 | + * queue. We have to clean up places a bit. | |
| 473 | + */ | |
| 474 | + while (!list_empty(&aq->active)) { | |
| 475 | + struct vb2_buffer *vb; | |
| 476 | + | |
| 477 | + ib = list_first_entry(&aq->active, struct ipu6_isys_buffer, | |
| 478 | + head); | |
| 479 | + vb = ipu6_isys_buffer_to_vb2_buffer(ib); | |
| 480 | + | |
| 481 | + list_del(&ib->head); | |
| 482 | + spin_unlock_irqrestore(&aq->lock, flags); | |
| 483 | + | |
| 484 | + vb2_buffer_done(vb, state); | |
| 485 | + | |
| 486 | + spin_lock_irqsave(&aq->lock, flags); | |
| 487 | + need_reset = true; | |
| 488 | + } | |
| 489 | + | |
| 490 | + spin_unlock_irqrestore(&aq->lock, flags); | |
| 491 | + | |
| 492 | + if (need_reset) { | |
| 493 | + mutex_lock(&av->isys->mutex); | |
| 494 | + av->isys->need_reset = true; | |
| 495 | + mutex_unlock(&av->isys->mutex); | |
| 496 | + } | |
| 497 | +} | |
| 498 | + | |
| 499 | +static void ipu6_isys_stream_cleanup(struct ipu6_isys_video *av) | |
| 500 | +{ | |
| 501 | + video_device_pipeline_stop(&av->vdev); | |
| 502 | + ipu6_isys_put_stream(av->stream); | |
| 503 | + av->stream = NULL; | |
| 504 | +} | |
| 505 | + | |
| 506 | +static int start_streaming(struct vb2_queue *q, unsigned int count) | |
| 507 | +{ | |
| 508 | + struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q); | |
| 509 | + struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); | |
| 510 | + struct device *dev = &av->isys->adev->auxdev.dev; | |
| 511 | + const struct ipu6_isys_pixelformat *pfmt = | |
| 512 | + ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); | |
| 513 | + struct ipu6_isys_buffer_list __bl, *bl = NULL; | |
| 514 | + struct ipu6_isys_stream *stream; | |
| 515 | + struct media_entity *source_entity = NULL; | |
| 516 | + int nr_queues, ret; | |
| 517 | + | |
| 518 | + dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n", | |
| 519 | + av->vdev.name, ipu6_isys_get_frame_width(av), | |
| 520 | + ipu6_isys_get_frame_height(av), pfmt->css_pixelformat); | |
| 521 | + | |
| 522 | + ret = ipu6_isys_setup_video(av, &source_entity, &nr_queues); | |
| 523 | + if (ret < 0) { | |
| 524 | + dev_dbg(dev, "failed to setup video\n"); | |
| 525 | + goto out_return_buffers; | |
| 526 | + } | |
| 527 | + | |
| 528 | + ret = ipu6_isys_link_fmt_validate(aq); | |
| 529 | + if (ret) { | |
| 530 | + dev_dbg(dev, | |
| 531 | + "%s: link format validation failed (%d)\n", | |
| 532 | + av->vdev.name, ret); | |
| 533 | + goto out_pipeline_stop; | |
| 534 | + } | |
| 535 | + | |
| 536 | + ret = ipu6_isys_fw_open(av->isys); | |
| 537 | + if (ret) | |
| 538 | + goto out_pipeline_stop; | |
| 539 | + | |
| 540 | + stream = av->stream; | |
| 541 | + mutex_lock(&stream->mutex); | |
| 542 | + if (!stream->nr_streaming) { | |
| 543 | + ret = ipu6_isys_video_prepare_stream(av, source_entity, | |
| 544 | + nr_queues); | |
| 545 | + if (ret) | |
| 546 | + goto out_fw_close; | |
| 547 | + } | |
| 548 | + | |
| 549 | + stream->nr_streaming++; | |
| 550 | + dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, | |
| 551 | + stream->nr_queues); | |
| 552 | + | |
| 553 | + list_add(&aq->node, &stream->queues); | |
| 554 | + ipu6_isys_set_csi2_streams_status(av, true); | |
| 555 | + ipu6_isys_configure_stream_watermark(av, true); | |
| 556 | + ipu6_isys_update_stream_watermark(av, true); | |
| 557 | + | |
| 558 | + if (stream->nr_streaming != stream->nr_queues) | |
| 559 | + goto out; | |
| 560 | + | |
| 561 | + bl = &__bl; | |
| 562 | + ret = buffer_list_get(stream, bl); | |
| 563 | + if (ret < 0) { | |
| 564 | + dev_warn(dev, "no buffer available, DRIVER BUG?\n"); | |
| 565 | + goto out; | |
| 566 | + } | |
| 567 | + | |
| 568 | + ret = ipu6_isys_stream_start(av, bl, false); | |
| 569 | + if (ret) | |
| 570 | + goto out_stream_start; | |
| 571 | + | |
| 572 | +out: | |
| 573 | + mutex_unlock(&stream->mutex); | |
| 574 | + | |
| 575 | + return 0; | |
| 576 | + | |
| 577 | +out_stream_start: | |
| 578 | + ipu6_isys_update_stream_watermark(av, false); | |
| 579 | + list_del(&aq->node); | |
| 580 | + stream->nr_streaming--; | |
| 581 | + | |
| 582 | +out_fw_close: | |
| 583 | + mutex_unlock(&stream->mutex); | |
| 584 | + ipu6_isys_fw_close(av->isys); | |
| 585 | + | |
| 586 | +out_pipeline_stop: | |
| 587 | + ipu6_isys_stream_cleanup(av); | |
| 588 | + | |
| 589 | +out_return_buffers: | |
| 590 | + return_buffers(aq, VB2_BUF_STATE_QUEUED); | |
| 591 | + | |
| 592 | + return ret; | |
| 593 | +} | |
| 594 | + | |
| 595 | +static void stop_streaming(struct vb2_queue *q) | |
| 596 | +{ | |
| 597 | + struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q); | |
| 598 | + struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); | |
| 599 | + struct ipu6_isys_stream *stream = av->stream; | |
| 600 | + | |
| 601 | + ipu6_isys_set_csi2_streams_status(av, false); | |
| 602 | + | |
| 603 | + mutex_lock(&stream->mutex); | |
| 604 | + | |
| 605 | + ipu6_isys_update_stream_watermark(av, false); | |
| 606 | + | |
| 607 | + mutex_lock(&av->isys->stream_mutex); | |
| 608 | + if (stream->nr_streaming == stream->nr_queues && stream->streaming) | |
| 609 | + ipu6_isys_video_set_streaming(av, 0, NULL); | |
| 610 | + mutex_unlock(&av->isys->stream_mutex); | |
| 611 | + | |
| 612 | + stream->nr_streaming--; | |
| 613 | + list_del(&aq->node); | |
| 614 | + stream->streaming = 0; | |
| 615 | + mutex_unlock(&stream->mutex); | |
| 616 | + | |
| 617 | + ipu6_isys_stream_cleanup(av); | |
| 618 | + | |
| 619 | + return_buffers(aq, VB2_BUF_STATE_ERROR); | |
| 620 | + | |
| 621 | + ipu6_isys_fw_close(av->isys); | |
| 622 | +} | |
| 623 | + | |
| 624 | +static unsigned int | |
| 625 | +get_sof_sequence_by_timestamp(struct ipu6_isys_stream *stream, | |
| 626 | + struct ipu6_fw_isys_resp_info_abi *info) | |
| 627 | +{ | |
| 628 | + u64 time = (u64)info->timestamp[1] << 32 | info->timestamp[0]; | |
| 629 | + struct ipu6_isys *isys = stream->isys; | |
| 630 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 631 | + unsigned int i; | |
| 632 | + | |
| 633 | + /* | |
| 634 | + * The timestamp is invalid as no TSC in some FPGA platform, | |
| 635 | + * so get the sequence from pipeline directly in this case. | |
| 636 | + */ | |
| 637 | + if (time == 0) | |
| 638 | + return atomic_read(&stream->sequence) - 1; | |
| 639 | + | |
| 640 | + for (i = 0; i < IPU6_ISYS_MAX_PARALLEL_SOF; i++) | |
| 641 | + if (time == stream->seq[i].timestamp) { | |
| 642 | + dev_dbg(dev, "sof: using seq nr %u for ts %llu\n", | |
| 643 | + stream->seq[i].sequence, time); | |
| 644 | + return stream->seq[i].sequence; | |
| 645 | + } | |
| 646 | + | |
| 647 | + for (i = 0; i < IPU6_ISYS_MAX_PARALLEL_SOF; i++) | |
| 648 | + dev_dbg(dev, "sof: sequence %u, timestamp value %llu\n", | |
| 649 | + stream->seq[i].sequence, stream->seq[i].timestamp); | |
| 650 | + | |
| 651 | + return 0; | |
| 652 | +} | |
| 653 | + | |
| 654 | +static u64 get_sof_ns_delta(struct ipu6_isys_video *av, | |
| 655 | + struct ipu6_fw_isys_resp_info_abi *info) | |
| 656 | +{ | |
| 657 | + struct ipu6_bus_device *adev = av->isys->adev; | |
| 658 | + struct ipu6_device *isp = adev->isp; | |
| 659 | + u64 delta, tsc_now; | |
| 660 | + | |
| 661 | + ipu6_buttress_tsc_read(isp, &tsc_now); | |
| 662 | + if (!tsc_now) | |
| 663 | + return 0; | |
| 664 | + | |
| 665 | + delta = tsc_now - ((u64)info->timestamp[1] << 32 | info->timestamp[0]); | |
| 666 | + | |
| 667 | + return ipu6_buttress_tsc_ticks_to_ns(delta, isp); | |
| 668 | +} | |
| 669 | + | |
| 670 | +void ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib, | |
| 671 | + struct ipu6_fw_isys_resp_info_abi *info) | |
| 672 | +{ | |
| 673 | + struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); | |
| 674 | + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | |
| 675 | + struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); | |
| 676 | + struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); | |
| 677 | + struct device *dev = &av->isys->adev->auxdev.dev; | |
| 678 | + struct ipu6_isys_stream *stream = av->stream; | |
| 679 | + u64 ns; | |
| 680 | + u32 sequence; | |
| 681 | + | |
| 682 | + ns = ktime_get_ns() - get_sof_ns_delta(av, info); | |
| 683 | + sequence = get_sof_sequence_by_timestamp(stream, info); | |
| 684 | + | |
| 685 | + vbuf->vb2_buf.timestamp = ns; | |
| 686 | + vbuf->sequence = sequence; | |
| 687 | + | |
| 688 | + dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n", | |
| 689 | + av->vdev.name, ktime_get_ns(), sequence); | |
| 690 | + dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index, | |
| 691 | + vbuf->vb2_buf.timestamp); | |
| 692 | +} | |
| 693 | + | |
| 694 | +void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib) | |
| 695 | +{ | |
| 696 | + struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); | |
| 697 | + | |
| 698 | + if (atomic_read(&ib->str2mmio_flag)) { | |
| 699 | + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); | |
| 700 | + /* | |
| 701 | + * Operation on buffer is ended with error and will be reported | |
| 702 | + * to the userspace when it is de-queued | |
| 703 | + */ | |
| 704 | + atomic_set(&ib->str2mmio_flag, 0); | |
| 705 | + } else { | |
| 706 | + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); | |
| 707 | + } | |
| 708 | +} | |
| 709 | + | |
| 710 | +void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, | |
| 711 | + struct ipu6_fw_isys_resp_info_abi *info) | |
| 712 | +{ | |
| 713 | + struct ipu6_isys_queue *aq = stream->output_pins[info->pin_id].aq; | |
| 714 | + struct ipu6_isys *isys = stream->isys; | |
| 715 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 716 | + struct ipu6_isys_buffer *ib; | |
| 717 | + struct vb2_buffer *vb; | |
| 718 | + unsigned long flags; | |
| 719 | + bool first = true; | |
| 720 | + struct vb2_v4l2_buffer *buf; | |
| 721 | + | |
| 722 | + spin_lock_irqsave(&aq->lock, flags); | |
| 723 | + if (list_empty(&aq->active)) { | |
| 724 | + spin_unlock_irqrestore(&aq->lock, flags); | |
| 725 | + dev_err(dev, "active queue empty\n"); | |
| 726 | + return; | |
| 727 | + } | |
| 728 | + | |
| 729 | + list_for_each_entry_reverse(ib, &aq->active, head) { | |
| 730 | + dma_addr_t addr; | |
| 731 | + | |
| 732 | + vb = ipu6_isys_buffer_to_vb2_buffer(ib); | |
| 733 | + addr = vb2_dma_contig_plane_dma_addr(vb, 0); | |
| 734 | + | |
| 735 | + if (info->pin.addr != addr) { | |
| 736 | + if (first) | |
| 737 | + dev_err(dev, "Unexpected buffer address %pad\n", | |
| 738 | + &addr); | |
| 739 | + first = false; | |
| 740 | + continue; | |
| 741 | + } | |
| 742 | + | |
| 743 | + if (info->error_info.error == | |
| 744 | + IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO) { | |
| 745 | + /* | |
| 746 | + * Check for error message: | |
| 747 | + * 'IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO' | |
| 748 | + */ | |
| 749 | + atomic_set(&ib->str2mmio_flag, 1); | |
| 750 | + } | |
| 751 | + dev_dbg(dev, "buffer: found buffer %pad\n", &addr); | |
| 752 | + | |
| 753 | + buf = to_vb2_v4l2_buffer(vb); | |
| 754 | + buf->field = V4L2_FIELD_NONE; | |
| 755 | + | |
| 756 | + list_del(&ib->head); | |
| 757 | + spin_unlock_irqrestore(&aq->lock, flags); | |
| 758 | + | |
| 759 | + ipu6_isys_buf_calc_sequence_time(ib, info); | |
| 760 | + | |
| 761 | + ipu6_isys_queue_buf_done(ib); | |
| 762 | + | |
| 763 | + return; | |
| 764 | + } | |
| 765 | + | |
| 766 | + dev_err(dev, "Failed to find a matching video buffer"); | |
| 767 | + | |
| 768 | + spin_unlock_irqrestore(&aq->lock, flags); | |
| 769 | +} | |
| 770 | + | |
| 771 | +static const struct vb2_ops ipu6_isys_queue_ops = { | |
| 772 | + .queue_setup = queue_setup, | |
| 773 | + .wait_prepare = vb2_ops_wait_prepare, | |
| 774 | + .wait_finish = vb2_ops_wait_finish, | |
| 775 | + .buf_prepare = ipu6_isys_buf_prepare, | |
| 776 | + .start_streaming = start_streaming, | |
| 777 | + .stop_streaming = stop_streaming, | |
| 778 | + .buf_queue = buf_queue, | |
| 779 | +}; | |
| 780 | + | |
| 781 | +int ipu6_isys_queue_init(struct ipu6_isys_queue *aq) | |
| 782 | +{ | |
| 783 | + struct ipu6_isys *isys = ipu6_isys_queue_to_video(aq)->isys; | |
| 784 | + struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); | |
| 785 | + int ret; | |
| 786 | + | |
| 787 | + /* no support for userptr */ | |
| 788 | + if (!aq->vbq.io_modes) | |
| 789 | + aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF; | |
| 790 | + | |
| 791 | + aq->vbq.drv_priv = aq; | |
| 792 | + aq->vbq.ops = &ipu6_isys_queue_ops; | |
| 793 | + aq->vbq.lock = &av->mutex; | |
| 794 | + aq->vbq.mem_ops = &vb2_dma_contig_memops; | |
| 795 | + aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
| 796 | + aq->vbq.min_queued_buffers = 1; | |
| 797 | + aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; | |
| 798 | + | |
| 799 | + ret = vb2_queue_init(&aq->vbq); | |
| 800 | + if (ret) | |
| 801 | + return ret; | |
| 802 | + | |
| 803 | + aq->dev = &isys->adev->auxdev.dev; | |
| 804 | + aq->vbq.dev = &isys->adev->auxdev.dev; | |
| 805 | + spin_lock_init(&aq->lock); | |
| 806 | + INIT_LIST_HEAD(&aq->active); | |
| 807 | + INIT_LIST_HEAD(&aq->incoming); | |
| 808 | + | |
| 809 | + return 0; | |
| 810 | +} | |
| @@ -0,0 +1,78 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2013--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_ISYS_QUEUE_H | |
| 5 | +#define IPU6_ISYS_QUEUE_H | |
| 6 | + | |
| 7 | +#include <linux/container_of.h> | |
| 8 | +#include <linux/atomic.h> | |
| 9 | +#include <linux/device.h> | |
| 10 | +#include <linux/list.h> | |
| 11 | +#include <linux/spinlock_types.h> | |
| 12 | + | |
| 13 | +#include <media/videobuf2-v4l2.h> | |
| 14 | + | |
| 15 | +#include "ipu6-fw-isys.h" | |
| 16 | +#include "ipu6-isys-video.h" | |
| 17 | + | |
| 18 | +struct ipu6_isys_stream; | |
| 19 | + | |
| 20 | +struct ipu6_isys_queue { | |
| 21 | + struct vb2_queue vbq; | |
| 22 | + struct list_head node; | |
| 23 | + struct device *dev; | |
| 24 | + /* | |
| 25 | + * @lock: serialise access to queued and pre_streamon_queued | |
| 26 | + */ | |
| 27 | + spinlock_t lock; | |
| 28 | + struct list_head active; | |
| 29 | + struct list_head incoming; | |
| 30 | + unsigned int fw_output; | |
| 31 | +}; | |
| 32 | + | |
| 33 | +struct ipu6_isys_buffer { | |
| 34 | + struct list_head head; | |
| 35 | + atomic_t str2mmio_flag; | |
| 36 | +}; | |
| 37 | + | |
| 38 | +struct ipu6_isys_video_buffer { | |
| 39 | + struct vb2_v4l2_buffer vb_v4l2; | |
| 40 | + struct ipu6_isys_buffer ib; | |
| 41 | +}; | |
| 42 | + | |
| 43 | +#define IPU6_ISYS_BUFFER_LIST_FL_INCOMING BIT(0) | |
| 44 | +#define IPU6_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1) | |
| 45 | +#define IPU6_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2) | |
| 46 | + | |
| 47 | +struct ipu6_isys_buffer_list { | |
| 48 | + struct list_head head; | |
| 49 | + unsigned int nbufs; | |
| 50 | +}; | |
| 51 | + | |
| 52 | +#define vb2_queue_to_isys_queue(__vb2) \ | |
| 53 | + container_of(__vb2, struct ipu6_isys_queue, vbq) | |
| 54 | + | |
| 55 | +#define ipu6_isys_to_isys_video_buffer(__ib) \ | |
| 56 | + container_of(__ib, struct ipu6_isys_video_buffer, ib) | |
| 57 | + | |
| 58 | +#define vb2_buffer_to_ipu6_isys_video_buffer(__vvb) \ | |
| 59 | + container_of(__vvb, struct ipu6_isys_video_buffer, vb_v4l2) | |
| 60 | + | |
| 61 | +#define ipu6_isys_buffer_to_vb2_buffer(__ib) \ | |
| 62 | + (&ipu6_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf) | |
| 63 | + | |
| 64 | +void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl, | |
| 65 | + unsigned long op_flags, | |
| 66 | + enum vb2_buffer_state state); | |
| 67 | +void | |
| 68 | +ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set, | |
| 69 | + struct ipu6_isys_stream *stream, | |
| 70 | + struct ipu6_isys_buffer_list *bl); | |
| 71 | +void | |
| 72 | +ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib, | |
| 73 | + struct ipu6_fw_isys_resp_info_abi *info); | |
| 74 | +void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib); | |
| 75 | +void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, | |
| 76 | + struct ipu6_fw_isys_resp_info_abi *info); | |
| 77 | +int ipu6_isys_queue_init(struct ipu6_isys_queue *aq); | |
| 78 | +#endif /* IPU6_ISYS_QUEUE_H */ | |
| @@ -0,0 +1,403 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/bug.h> | |
| 7 | +#include <linux/device.h> | |
| 8 | +#include <linux/minmax.h> | |
| 9 | + | |
| 10 | +#include <media/media-entity.h> | |
| 11 | +#include <media/mipi-csi2.h> | |
| 12 | +#include <media/v4l2-ctrls.h> | |
| 13 | +#include <media/v4l2-subdev.h> | |
| 14 | + | |
| 15 | +#include "ipu6-bus.h" | |
| 16 | +#include "ipu6-isys.h" | |
| 17 | +#include "ipu6-isys-subdev.h" | |
| 18 | + | |
| 19 | +unsigned int ipu6_isys_mbus_code_to_bpp(u32 code) | |
| 20 | +{ | |
| 21 | + switch (code) { | |
| 22 | + case MEDIA_BUS_FMT_RGB888_1X24: | |
| 23 | + case MEDIA_BUS_FMT_META_24: | |
| 24 | + return 24; | |
| 25 | + case MEDIA_BUS_FMT_RGB565_1X16: | |
| 26 | + case MEDIA_BUS_FMT_UYVY8_1X16: | |
| 27 | + case MEDIA_BUS_FMT_YUYV8_1X16: | |
| 28 | + case MEDIA_BUS_FMT_META_16: | |
| 29 | + return 16; | |
| 30 | + case MEDIA_BUS_FMT_SBGGR12_1X12: | |
| 31 | + case MEDIA_BUS_FMT_SGBRG12_1X12: | |
| 32 | + case MEDIA_BUS_FMT_SGRBG12_1X12: | |
| 33 | + case MEDIA_BUS_FMT_SRGGB12_1X12: | |
| 34 | + case MEDIA_BUS_FMT_META_12: | |
| 35 | + return 12; | |
| 36 | + case MEDIA_BUS_FMT_SBGGR10_1X10: | |
| 37 | + case MEDIA_BUS_FMT_SGBRG10_1X10: | |
| 38 | + case MEDIA_BUS_FMT_SGRBG10_1X10: | |
| 39 | + case MEDIA_BUS_FMT_SRGGB10_1X10: | |
| 40 | + case MEDIA_BUS_FMT_META_10: | |
| 41 | + return 10; | |
| 42 | + case MEDIA_BUS_FMT_SBGGR8_1X8: | |
| 43 | + case MEDIA_BUS_FMT_SGBRG8_1X8: | |
| 44 | + case MEDIA_BUS_FMT_SGRBG8_1X8: | |
| 45 | + case MEDIA_BUS_FMT_SRGGB8_1X8: | |
| 46 | + case MEDIA_BUS_FMT_META_8: | |
| 47 | + return 8; | |
| 48 | + default: | |
| 49 | + WARN_ON(1); | |
| 50 | + return 8; | |
| 51 | + } | |
| 52 | +} | |
| 53 | + | |
| 54 | +unsigned int ipu6_isys_mbus_code_to_mipi(u32 code) | |
| 55 | +{ | |
| 56 | + switch (code) { | |
| 57 | + case MEDIA_BUS_FMT_RGB565_1X16: | |
| 58 | + return MIPI_CSI2_DT_RGB565; | |
| 59 | + case MEDIA_BUS_FMT_RGB888_1X24: | |
| 60 | + return MIPI_CSI2_DT_RGB888; | |
| 61 | + case MEDIA_BUS_FMT_UYVY8_1X16: | |
| 62 | + case MEDIA_BUS_FMT_YUYV8_1X16: | |
| 63 | + return MIPI_CSI2_DT_YUV422_8B; | |
| 64 | + case MEDIA_BUS_FMT_SBGGR16_1X16: | |
| 65 | + case MEDIA_BUS_FMT_SGBRG16_1X16: | |
| 66 | + case MEDIA_BUS_FMT_SGRBG16_1X16: | |
| 67 | + case MEDIA_BUS_FMT_SRGGB16_1X16: | |
| 68 | + return MIPI_CSI2_DT_RAW16; | |
| 69 | + case MEDIA_BUS_FMT_SBGGR12_1X12: | |
| 70 | + case MEDIA_BUS_FMT_SGBRG12_1X12: | |
| 71 | + case MEDIA_BUS_FMT_SGRBG12_1X12: | |
| 72 | + case MEDIA_BUS_FMT_SRGGB12_1X12: | |
| 73 | + return MIPI_CSI2_DT_RAW12; | |
| 74 | + case MEDIA_BUS_FMT_SBGGR10_1X10: | |
| 75 | + case MEDIA_BUS_FMT_SGBRG10_1X10: | |
| 76 | + case MEDIA_BUS_FMT_SGRBG10_1X10: | |
| 77 | + case MEDIA_BUS_FMT_SRGGB10_1X10: | |
| 78 | + return MIPI_CSI2_DT_RAW10; | |
| 79 | + case MEDIA_BUS_FMT_SBGGR8_1X8: | |
| 80 | + case MEDIA_BUS_FMT_SGBRG8_1X8: | |
| 81 | + case MEDIA_BUS_FMT_SGRBG8_1X8: | |
| 82 | + case MEDIA_BUS_FMT_SRGGB8_1X8: | |
| 83 | + return MIPI_CSI2_DT_RAW8; | |
| 84 | + default: | |
| 85 | + /* return unavailable MIPI data type - 0x3f */ | |
| 86 | + WARN_ON(1); | |
| 87 | + return 0x3f; | |
| 88 | + } | |
| 89 | +} | |
| 90 | + | |
| 91 | +bool ipu6_isys_is_bayer_format(u32 code) | |
| 92 | +{ | |
| 93 | + switch (ipu6_isys_mbus_code_to_mipi(code)) { | |
| 94 | + case MIPI_CSI2_DT_RAW8: | |
| 95 | + case MIPI_CSI2_DT_RAW10: | |
| 96 | + case MIPI_CSI2_DT_RAW12: | |
| 97 | + case MIPI_CSI2_DT_RAW14: | |
| 98 | + case MIPI_CSI2_DT_RAW16: | |
| 99 | + case MIPI_CSI2_DT_RAW20: | |
| 100 | + case MIPI_CSI2_DT_RAW24: | |
| 101 | + case MIPI_CSI2_DT_RAW28: | |
| 102 | + return true; | |
| 103 | + default: | |
| 104 | + return false; | |
| 105 | + } | |
| 106 | +} | |
| 107 | + | |
| 108 | +u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y) | |
| 109 | +{ | |
| 110 | + static const u32 code_map[] = { | |
| 111 | + MEDIA_BUS_FMT_SRGGB8_1X8, | |
| 112 | + MEDIA_BUS_FMT_SGRBG8_1X8, | |
| 113 | + MEDIA_BUS_FMT_SGBRG8_1X8, | |
| 114 | + MEDIA_BUS_FMT_SBGGR8_1X8, | |
| 115 | + MEDIA_BUS_FMT_SRGGB10_1X10, | |
| 116 | + MEDIA_BUS_FMT_SGRBG10_1X10, | |
| 117 | + MEDIA_BUS_FMT_SGBRG10_1X10, | |
| 118 | + MEDIA_BUS_FMT_SBGGR10_1X10, | |
| 119 | + MEDIA_BUS_FMT_SRGGB12_1X12, | |
| 120 | + MEDIA_BUS_FMT_SGRBG12_1X12, | |
| 121 | + MEDIA_BUS_FMT_SGBRG12_1X12, | |
| 122 | + MEDIA_BUS_FMT_SBGGR12_1X12, | |
| 123 | + MEDIA_BUS_FMT_SRGGB16_1X16, | |
| 124 | + MEDIA_BUS_FMT_SGRBG16_1X16, | |
| 125 | + MEDIA_BUS_FMT_SGBRG16_1X16, | |
| 126 | + MEDIA_BUS_FMT_SBGGR16_1X16, | |
| 127 | + }; | |
| 128 | + u32 i; | |
| 129 | + | |
| 130 | + for (i = 0; i < ARRAY_SIZE(code_map); i++) | |
| 131 | + if (code_map[i] == code) | |
| 132 | + break; | |
| 133 | + | |
| 134 | + if (WARN_ON(i == ARRAY_SIZE(code_map))) | |
| 135 | + return code; | |
| 136 | + | |
| 137 | + return code_map[i ^ (((y & 1) << 1) | (x & 1))]; | |
| 138 | +} | |
| 139 | + | |
| 140 | +int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd, | |
| 141 | + struct v4l2_subdev_state *state, | |
| 142 | + struct v4l2_subdev_format *format) | |
| 143 | +{ | |
| 144 | + struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); | |
| 145 | + struct v4l2_mbus_framefmt *fmt; | |
| 146 | + struct v4l2_rect *crop; | |
| 147 | + u32 code = asd->supported_codes[0]; | |
| 148 | + u32 other_pad, other_stream; | |
| 149 | + unsigned int i; | |
| 150 | + int ret; | |
| 151 | + | |
| 152 | + /* No transcoding, source and sink formats must match. */ | |
| 153 | + if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) && | |
| 154 | + sd->entity.num_pads > 1) | |
| 155 | + return v4l2_subdev_get_fmt(sd, state, format); | |
| 156 | + | |
| 157 | + format->format.width = clamp(format->format.width, IPU6_ISYS_MIN_WIDTH, | |
| 158 | + IPU6_ISYS_MAX_WIDTH); | |
| 159 | + format->format.height = clamp(format->format.height, | |
| 160 | + IPU6_ISYS_MIN_HEIGHT, | |
| 161 | + IPU6_ISYS_MAX_HEIGHT); | |
| 162 | + | |
| 163 | + for (i = 0; asd->supported_codes[i]; i++) { | |
| 164 | + if (asd->supported_codes[i] == format->format.code) { | |
| 165 | + code = asd->supported_codes[i]; | |
| 166 | + break; | |
| 167 | + } | |
| 168 | + } | |
| 169 | + format->format.code = code; | |
| 170 | + format->format.field = V4L2_FIELD_NONE; | |
| 171 | + | |
| 172 | + /* Store the format and propagate it to the source pad. */ | |
| 173 | + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); | |
| 174 | + if (!fmt) | |
| 175 | + return -EINVAL; | |
| 176 | + | |
| 177 | + *fmt = format->format; | |
| 178 | + | |
| 179 | + if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK)) | |
| 180 | + return 0; | |
| 181 | + | |
| 182 | + /* propagate format to following source pad */ | |
| 183 | + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, | |
| 184 | + format->stream); | |
| 185 | + if (!fmt) | |
| 186 | + return -EINVAL; | |
| 187 | + | |
| 188 | + *fmt = format->format; | |
| 189 | + | |
| 190 | + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, | |
| 191 | + format->pad, | |
| 192 | + format->stream, | |
| 193 | + &other_pad, | |
| 194 | + &other_stream); | |
| 195 | + if (ret) | |
| 196 | + return -EINVAL; | |
| 197 | + | |
| 198 | + crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream); | |
| 199 | + /* reset crop */ | |
| 200 | + crop->left = 0; | |
| 201 | + crop->top = 0; | |
| 202 | + crop->width = fmt->width; | |
| 203 | + crop->height = fmt->height; | |
| 204 | + | |
| 205 | + return 0; | |
| 206 | +} | |
| 207 | + | |
| 208 | +int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, | |
| 209 | + struct v4l2_subdev_state *state, | |
| 210 | + struct v4l2_subdev_mbus_code_enum *code) | |
| 211 | +{ | |
| 212 | + struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); | |
| 213 | + const u32 *supported_codes = asd->supported_codes; | |
| 214 | + u32 index; | |
| 215 | + | |
| 216 | + for (index = 0; supported_codes[index]; index++) { | |
| 217 | + if (index == code->index) { | |
| 218 | + code->code = supported_codes[index]; | |
| 219 | + return 0; | |
| 220 | + } | |
| 221 | + } | |
| 222 | + | |
| 223 | + return -EINVAL; | |
| 224 | +} | |
| 225 | + | |
| 226 | +static int subdev_set_routing(struct v4l2_subdev *sd, | |
| 227 | + struct v4l2_subdev_state *state, | |
| 228 | + struct v4l2_subdev_krouting *routing) | |
| 229 | +{ | |
| 230 | + static const struct v4l2_mbus_framefmt format = { | |
| 231 | + .width = 4096, | |
| 232 | + .height = 3072, | |
| 233 | + .code = MEDIA_BUS_FMT_SGRBG10_1X10, | |
| 234 | + .field = V4L2_FIELD_NONE, | |
| 235 | + }; | |
| 236 | + int ret; | |
| 237 | + | |
| 238 | + ret = v4l2_subdev_routing_validate(sd, routing, | |
| 239 | + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); | |
| 240 | + if (ret) | |
| 241 | + return ret; | |
| 242 | + | |
| 243 | + return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); | |
| 244 | +} | |
| 245 | + | |
| 246 | +int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, | |
| 247 | + struct v4l2_mbus_framefmt *format) | |
| 248 | +{ | |
| 249 | + struct v4l2_mbus_framefmt *fmt; | |
| 250 | + struct v4l2_subdev_state *state; | |
| 251 | + | |
| 252 | + if (!sd || !format) | |
| 253 | + return -EINVAL; | |
| 254 | + | |
| 255 | + state = v4l2_subdev_lock_and_get_active_state(sd); | |
| 256 | + fmt = v4l2_subdev_state_get_format(state, pad, stream); | |
| 257 | + if (fmt) | |
| 258 | + *format = *fmt; | |
| 259 | + v4l2_subdev_unlock_state(state); | |
| 260 | + | |
| 261 | + return fmt ? 0 : -EINVAL; | |
| 262 | +} | |
| 263 | + | |
| 264 | +int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream, | |
| 265 | + struct v4l2_rect *crop) | |
| 266 | +{ | |
| 267 | + struct v4l2_subdev_state *state; | |
| 268 | + struct v4l2_rect *rect; | |
| 269 | + | |
| 270 | + if (!sd || !crop) | |
| 271 | + return -EINVAL; | |
| 272 | + | |
| 273 | + state = v4l2_subdev_lock_and_get_active_state(sd); | |
| 274 | + rect = v4l2_subdev_state_get_crop(state, pad, stream); | |
| 275 | + if (rect) | |
| 276 | + *crop = *rect; | |
| 277 | + v4l2_subdev_unlock_state(state); | |
| 278 | + | |
| 279 | + return rect ? 0 : -EINVAL; | |
| 280 | +} | |
| 281 | + | |
| 282 | +u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) | |
| 283 | +{ | |
| 284 | + struct v4l2_subdev_state *state; | |
| 285 | + struct v4l2_subdev_route *routes; | |
| 286 | + unsigned int i; | |
| 287 | + u32 source_stream = 0; | |
| 288 | + | |
| 289 | + state = v4l2_subdev_lock_and_get_active_state(sd); | |
| 290 | + if (!state) | |
| 291 | + return 0; | |
| 292 | + | |
| 293 | + routes = state->routing.routes; | |
| 294 | + for (i = 0; i < state->routing.num_routes; i++) { | |
| 295 | + if (routes[i].source_pad == pad) { | |
| 296 | + source_stream = routes[i].source_stream; | |
| 297 | + break; | |
| 298 | + } | |
| 299 | + } | |
| 300 | + | |
| 301 | + v4l2_subdev_unlock_state(state); | |
| 302 | + | |
| 303 | + return source_stream; | |
| 304 | +} | |
| 305 | + | |
| 306 | +static int ipu6_isys_subdev_init_state(struct v4l2_subdev *sd, | |
| 307 | + struct v4l2_subdev_state *state) | |
| 308 | +{ | |
| 309 | + struct v4l2_subdev_route route = { | |
| 310 | + .sink_pad = 0, | |
| 311 | + .sink_stream = 0, | |
| 312 | + .source_pad = 1, | |
| 313 | + .source_stream = 0, | |
| 314 | + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, | |
| 315 | + }; | |
| 316 | + struct v4l2_subdev_krouting routing = { | |
| 317 | + .num_routes = 1, | |
| 318 | + .routes = &route, | |
| 319 | + }; | |
| 320 | + | |
| 321 | + return subdev_set_routing(sd, state, &routing); | |
| 322 | +} | |
| 323 | + | |
| 324 | +int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd, | |
| 325 | + struct v4l2_subdev_state *state, | |
| 326 | + enum v4l2_subdev_format_whence which, | |
| 327 | + struct v4l2_subdev_krouting *routing) | |
| 328 | +{ | |
| 329 | + return subdev_set_routing(sd, state, routing); | |
| 330 | +} | |
| 331 | + | |
| 332 | +static const struct v4l2_subdev_internal_ops ipu6_isys_subdev_internal_ops = { | |
| 333 | + .init_state = ipu6_isys_subdev_init_state, | |
| 334 | +}; | |
| 335 | + | |
| 336 | +int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd, | |
| 337 | + const struct v4l2_subdev_ops *ops, | |
| 338 | + unsigned int nr_ctrls, | |
| 339 | + unsigned int num_sink_pads, | |
| 340 | + unsigned int num_source_pads) | |
| 341 | +{ | |
| 342 | + unsigned int num_pads = num_sink_pads + num_source_pads; | |
| 343 | + unsigned int i; | |
| 344 | + int ret; | |
| 345 | + | |
| 346 | + v4l2_subdev_init(&asd->sd, ops); | |
| 347 | + | |
| 348 | + asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | | |
| 349 | + V4L2_SUBDEV_FL_HAS_EVENTS | | |
| 350 | + V4L2_SUBDEV_FL_STREAMS; | |
| 351 | + asd->sd.owner = THIS_MODULE; | |
| 352 | + asd->sd.dev = &asd->isys->adev->auxdev.dev; | |
| 353 | + asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; | |
| 354 | + asd->sd.internal_ops = &ipu6_isys_subdev_internal_ops; | |
| 355 | + | |
| 356 | + asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads, | |
| 357 | + sizeof(*asd->pad), GFP_KERNEL); | |
| 358 | + if (!asd->pad) | |
| 359 | + return -ENOMEM; | |
| 360 | + | |
| 361 | + for (i = 0; i < num_sink_pads; i++) | |
| 362 | + asd->pad[i].flags = MEDIA_PAD_FL_SINK | | |
| 363 | + MEDIA_PAD_FL_MUST_CONNECT; | |
| 364 | + | |
| 365 | + for (i = num_sink_pads; i < num_pads; i++) | |
| 366 | + asd->pad[i].flags = MEDIA_PAD_FL_SOURCE; | |
| 367 | + | |
| 368 | + ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad); | |
| 369 | + if (ret) | |
| 370 | + return ret; | |
| 371 | + | |
| 372 | + if (asd->ctrl_init) { | |
| 373 | + ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls); | |
| 374 | + if (ret) | |
| 375 | + goto out_media_entity_cleanup; | |
| 376 | + | |
| 377 | + asd->ctrl_init(&asd->sd); | |
| 378 | + if (asd->ctrl_handler.error) { | |
| 379 | + ret = asd->ctrl_handler.error; | |
| 380 | + goto out_v4l2_ctrl_handler_free; | |
| 381 | + } | |
| 382 | + | |
| 383 | + asd->sd.ctrl_handler = &asd->ctrl_handler; | |
| 384 | + } | |
| 385 | + | |
| 386 | + asd->source = -1; | |
| 387 | + | |
| 388 | + return 0; | |
| 389 | + | |
| 390 | +out_v4l2_ctrl_handler_free: | |
| 391 | + v4l2_ctrl_handler_free(&asd->ctrl_handler); | |
| 392 | + | |
| 393 | +out_media_entity_cleanup: | |
| 394 | + media_entity_cleanup(&asd->sd.entity); | |
| 395 | + | |
| 396 | + return ret; | |
| 397 | +} | |
| 398 | + | |
| 399 | +void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd) | |
| 400 | +{ | |
| 401 | + media_entity_cleanup(&asd->sd.entity); | |
| 402 | + v4l2_ctrl_handler_free(&asd->ctrl_handler); | |
| 403 | +} | |
| @@ -0,0 +1,59 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2013--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_ISYS_SUBDEV_H | |
| 5 | +#define IPU6_ISYS_SUBDEV_H | |
| 6 | + | |
| 7 | +#include <linux/container_of.h> | |
| 8 | + | |
| 9 | +#include <media/media-entity.h> | |
| 10 | +#include <media/v4l2-ctrls.h> | |
| 11 | +#include <media/v4l2-subdev.h> | |
| 12 | + | |
| 13 | +struct ipu6_isys; | |
| 14 | + | |
| 15 | +struct ipu6_isys_subdev { | |
| 16 | + struct v4l2_subdev sd; | |
| 17 | + struct ipu6_isys *isys; | |
| 18 | + u32 const *supported_codes; | |
| 19 | + struct media_pad *pad; | |
| 20 | + struct v4l2_ctrl_handler ctrl_handler; | |
| 21 | + void (*ctrl_init)(struct v4l2_subdev *sd); | |
| 22 | + int source; /* SSI stream source; -1 if unset */ | |
| 23 | +}; | |
| 24 | + | |
| 25 | +#define to_ipu6_isys_subdev(__sd) \ | |
| 26 | + container_of(__sd, struct ipu6_isys_subdev, sd) | |
| 27 | + | |
| 28 | +unsigned int ipu6_isys_mbus_code_to_bpp(u32 code); | |
| 29 | +unsigned int ipu6_isys_mbus_code_to_mipi(u32 code); | |
| 30 | +bool ipu6_isys_is_bayer_format(u32 code); | |
| 31 | +u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y); | |
| 32 | + | |
| 33 | +int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd, | |
| 34 | + struct v4l2_subdev_state *state, | |
| 35 | + struct v4l2_subdev_format *fmt); | |
| 36 | +int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, | |
| 37 | + struct v4l2_subdev_state *state, | |
| 38 | + struct v4l2_subdev_mbus_code_enum | |
| 39 | + *code); | |
| 40 | +int ipu6_isys_subdev_link_validate(struct v4l2_subdev *sd, | |
| 41 | + struct media_link *link, | |
| 42 | + struct v4l2_subdev_format *source_fmt, | |
| 43 | + struct v4l2_subdev_format *sink_fmt); | |
| 44 | +u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad); | |
| 45 | +int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, | |
| 46 | + struct v4l2_mbus_framefmt *format); | |
| 47 | +int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream, | |
| 48 | + struct v4l2_rect *crop); | |
| 49 | +int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd, | |
| 50 | + struct v4l2_subdev_state *state, | |
| 51 | + enum v4l2_subdev_format_whence which, | |
| 52 | + struct v4l2_subdev_krouting *routing); | |
| 53 | +int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd, | |
| 54 | + const struct v4l2_subdev_ops *ops, | |
| 55 | + unsigned int nr_ctrls, | |
| 56 | + unsigned int num_sink_pads, | |
| 57 | + unsigned int num_source_pads); | |
| 58 | +void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd); | |
| 59 | +#endif /* IPU6_ISYS_SUBDEV_H */ | |
| @@ -0,0 +1,1420 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/align.h> | |
| 7 | +#include <linux/bits.h> | |
| 8 | +#include <linux/bug.h> | |
| 9 | +#include <linux/completion.h> | |
| 10 | +#include <linux/container_of.h> | |
| 11 | +#include <linux/device.h> | |
| 12 | +#include <linux/list.h> | |
| 13 | +#include <linux/math64.h> | |
| 14 | +#include <linux/minmax.h> | |
| 15 | +#include <linux/module.h> | |
| 16 | +#include <linux/mutex.h> | |
| 17 | +#include <linux/pm_runtime.h> | |
| 18 | +#include <linux/spinlock.h> | |
| 19 | +#include <linux/string.h> | |
| 20 | + | |
| 21 | +#include <media/media-entity.h> | |
| 22 | +#include <media/v4l2-ctrls.h> | |
| 23 | +#include <media/v4l2-dev.h> | |
| 24 | +#include <media/v4l2-fh.h> | |
| 25 | +#include <media/v4l2-ioctl.h> | |
| 26 | +#include <media/v4l2-subdev.h> | |
| 27 | +#include <media/videobuf2-v4l2.h> | |
| 28 | + | |
| 29 | +#include "ipu6.h" | |
| 30 | +#include "ipu6-bus.h" | |
| 31 | +#include "ipu6-cpd.h" | |
| 32 | +#include "ipu6-fw-isys.h" | |
| 33 | +#include "ipu6-isys.h" | |
| 34 | +#include "ipu6-isys-csi2.h" | |
| 35 | +#include "ipu6-isys-queue.h" | |
| 36 | +#include "ipu6-isys-video.h" | |
| 37 | +#include "ipu6-platform-regs.h" | |
| 38 | + | |
| 39 | +const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = { | |
| 40 | + { V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12, | |
| 41 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW16 }, | |
| 42 | + { V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12, | |
| 43 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW16 }, | |
| 44 | + { V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12, | |
| 45 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW16 }, | |
| 46 | + { V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12, | |
| 47 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW16 }, | |
| 48 | + { V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10, | |
| 49 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW16 }, | |
| 50 | + { V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10, | |
| 51 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW16 }, | |
| 52 | + { V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10, | |
| 53 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW16 }, | |
| 54 | + { V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10, | |
| 55 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW16 }, | |
| 56 | + { V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8, | |
| 57 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW8 }, | |
| 58 | + { V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8, | |
| 59 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW8 }, | |
| 60 | + { V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8, | |
| 61 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW8 }, | |
| 62 | + { V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8, | |
| 63 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW8 }, | |
| 64 | + { V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12, | |
| 65 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW12 }, | |
| 66 | + { V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12, | |
| 67 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW12 }, | |
| 68 | + { V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12, | |
| 69 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW12 }, | |
| 70 | + { V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12, | |
| 71 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW12 }, | |
| 72 | + { V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10, | |
| 73 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW10 }, | |
| 74 | + { V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10, | |
| 75 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW10 }, | |
| 76 | + { V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10, | |
| 77 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW10 }, | |
| 78 | + { V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10, | |
| 79 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW10 }, | |
| 80 | + { V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16, | |
| 81 | + IPU6_FW_ISYS_FRAME_FORMAT_UYVY}, | |
| 82 | + { V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16, | |
| 83 | + IPU6_FW_ISYS_FRAME_FORMAT_YUYV}, | |
| 84 | + { V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16, | |
| 85 | + IPU6_FW_ISYS_FRAME_FORMAT_RGB565 }, | |
| 86 | + { V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, | |
| 87 | + IPU6_FW_ISYS_FRAME_FORMAT_RGBA888 }, | |
| 88 | + { V4L2_META_FMT_GENERIC_8, 8, 8, MEDIA_BUS_FMT_META_8, | |
| 89 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW8, true }, | |
| 90 | + { V4L2_META_FMT_GENERIC_CSI2_10, 10, 10, MEDIA_BUS_FMT_META_10, | |
| 91 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW10, true }, | |
| 92 | + { V4L2_META_FMT_GENERIC_CSI2_12, 12, 12, MEDIA_BUS_FMT_META_12, | |
| 93 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW12, true }, | |
| 94 | + { V4L2_META_FMT_GENERIC_CSI2_16, 16, 16, MEDIA_BUS_FMT_META_16, | |
| 95 | + IPU6_FW_ISYS_FRAME_FORMAT_RAW16, true }, | |
| 96 | +}; | |
| 97 | + | |
| 98 | +static int video_open(struct file *file) | |
| 99 | +{ | |
| 100 | + struct ipu6_isys_video *av = video_drvdata(file); | |
| 101 | + struct ipu6_isys *isys = av->isys; | |
| 102 | + struct ipu6_bus_device *adev = isys->adev; | |
| 103 | + | |
| 104 | + mutex_lock(&isys->mutex); | |
| 105 | + if (isys->need_reset) { | |
| 106 | + mutex_unlock(&isys->mutex); | |
| 107 | + dev_warn(&adev->auxdev.dev, "isys power cycle required\n"); | |
| 108 | + return -EIO; | |
| 109 | + } | |
| 110 | + mutex_unlock(&isys->mutex); | |
| 111 | + | |
| 112 | + return v4l2_fh_open(file); | |
| 113 | +} | |
| 114 | + | |
| 115 | +const struct ipu6_isys_pixelformat * | |
| 116 | +ipu6_isys_get_isys_format(u32 pixelformat, u32 type) | |
| 117 | +{ | |
| 118 | + const struct ipu6_isys_pixelformat *default_pfmt = NULL; | |
| 119 | + unsigned int i; | |
| 120 | + | |
| 121 | + for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) { | |
| 122 | + const struct ipu6_isys_pixelformat *pfmt = &ipu6_isys_pfmts[i]; | |
| 123 | + | |
| 124 | + if (type && ((!pfmt->is_meta && | |
| 125 | + type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || | |
| 126 | + (pfmt->is_meta && | |
| 127 | + type != V4L2_BUF_TYPE_META_CAPTURE))) | |
| 128 | + continue; | |
| 129 | + | |
| 130 | + if (!default_pfmt) | |
| 131 | + default_pfmt = pfmt; | |
| 132 | + | |
| 133 | + if (pfmt->pixelformat != pixelformat) | |
| 134 | + continue; | |
| 135 | + | |
| 136 | + return pfmt; | |
| 137 | + } | |
| 138 | + | |
| 139 | + return default_pfmt; | |
| 140 | +} | |
| 141 | + | |
| 142 | +static int ipu6_isys_vidioc_querycap(struct file *file, void *fh, | |
| 143 | + struct v4l2_capability *cap) | |
| 144 | +{ | |
| 145 | + struct ipu6_isys_video *av = video_drvdata(file); | |
| 146 | + | |
| 147 | + strscpy(cap->driver, IPU6_ISYS_NAME, sizeof(cap->driver)); | |
| 148 | + strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card)); | |
| 149 | + | |
| 150 | + return 0; | |
| 151 | +} | |
| 152 | + | |
| 153 | +static int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh, | |
| 154 | + struct v4l2_fmtdesc *f) | |
| 155 | +{ | |
| 156 | + unsigned int i, num_found; | |
| 157 | + | |
| 158 | + for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) { | |
| 159 | + if ((ipu6_isys_pfmts[i].is_meta && | |
| 160 | + f->type != V4L2_BUF_TYPE_META_CAPTURE) || | |
| 161 | + (!ipu6_isys_pfmts[i].is_meta && | |
| 162 | + f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) | |
| 163 | + continue; | |
| 164 | + | |
| 165 | + if (f->mbus_code && f->mbus_code != ipu6_isys_pfmts[i].code) | |
| 166 | + continue; | |
| 167 | + | |
| 168 | + if (num_found < f->index) { | |
| 169 | + num_found++; | |
| 170 | + continue; | |
| 171 | + } | |
| 172 | + | |
| 173 | + f->flags = 0; | |
| 174 | + f->pixelformat = ipu6_isys_pfmts[i].pixelformat; | |
| 175 | + | |
| 176 | + return 0; | |
| 177 | + } | |
| 178 | + | |
| 179 | + return -EINVAL; | |
| 180 | +} | |
| 181 | + | |
| 182 | +static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh, | |
| 183 | + struct v4l2_frmsizeenum *fsize) | |
| 184 | +{ | |
| 185 | + unsigned int i; | |
| 186 | + | |
| 187 | + if (fsize->index > 0) | |
| 188 | + return -EINVAL; | |
| 189 | + | |
| 190 | + for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) { | |
| 191 | + if (fsize->pixel_format != ipu6_isys_pfmts[i].pixelformat) | |
| 192 | + continue; | |
| 193 | + | |
| 194 | + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; | |
| 195 | + fsize->stepwise.min_width = IPU6_ISYS_MIN_WIDTH; | |
| 196 | + fsize->stepwise.max_width = IPU6_ISYS_MAX_WIDTH; | |
| 197 | + fsize->stepwise.min_height = IPU6_ISYS_MIN_HEIGHT; | |
| 198 | + fsize->stepwise.max_height = IPU6_ISYS_MAX_HEIGHT; | |
| 199 | + fsize->stepwise.step_width = 2; | |
| 200 | + fsize->stepwise.step_height = 2; | |
| 201 | + | |
| 202 | + return 0; | |
| 203 | + } | |
| 204 | + | |
| 205 | + return -EINVAL; | |
| 206 | +} | |
| 207 | + | |
| 208 | +static int ipu6_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh, | |
| 209 | + struct v4l2_format *f) | |
| 210 | +{ | |
| 211 | + struct ipu6_isys_video *av = video_drvdata(file); | |
| 212 | + | |
| 213 | + f->fmt.pix = av->pix_fmt; | |
| 214 | + | |
| 215 | + return 0; | |
| 216 | +} | |
| 217 | + | |
| 218 | +static int ipu6_isys_vidioc_g_fmt_meta_cap(struct file *file, void *fh, | |
| 219 | + struct v4l2_format *f) | |
| 220 | +{ | |
| 221 | + struct ipu6_isys_video *av = video_drvdata(file); | |
| 222 | + | |
| 223 | + f->fmt.meta = av->meta_fmt; | |
| 224 | + | |
| 225 | + return 0; | |
| 226 | +} | |
| 227 | + | |
| 228 | +static void ipu6_isys_try_fmt_cap(struct ipu6_isys_video *av, u32 type, | |
| 229 | + u32 *format, u32 *width, u32 *height, | |
| 230 | + u32 *bytesperline, u32 *sizeimage) | |
| 231 | +{ | |
| 232 | + const struct ipu6_isys_pixelformat *pfmt = | |
| 233 | + ipu6_isys_get_isys_format(*format, type); | |
| 234 | + | |
| 235 | + *format = pfmt->pixelformat; | |
| 236 | + *width = clamp(*width, IPU6_ISYS_MIN_WIDTH, IPU6_ISYS_MAX_WIDTH); | |
| 237 | + *height = clamp(*height, IPU6_ISYS_MIN_HEIGHT, IPU6_ISYS_MAX_HEIGHT); | |
| 238 | + | |
| 239 | + if (pfmt->bpp != pfmt->bpp_packed) | |
| 240 | + *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE); | |
| 241 | + else | |
| 242 | + *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE); | |
| 243 | + | |
| 244 | + *bytesperline = ALIGN(*bytesperline, av->isys->line_align); | |
| 245 | + | |
| 246 | + /* | |
| 247 | + * (height + 1) * bytesperline due to a hardware issue: the DMA unit | |
| 248 | + * is a power of two, and a line should be transferred as few units | |
| 249 | + * as possible. The result is that up to line length more data than | |
| 250 | + * the image size may be transferred to memory after the image. | |
| 251 | + * Another limitation is the GDA allocation unit size. For low | |
| 252 | + * resolution it gives a bigger number. Use larger one to avoid | |
| 253 | + * memory corruption. | |
| 254 | + */ | |
| 255 | + *sizeimage = *bytesperline * *height + | |
| 256 | + max(*bytesperline, | |
| 257 | + av->isys->pdata->ipdata->isys_dma_overshoot); | |
| 258 | +} | |
| 259 | + | |
| 260 | +static void __ipu6_isys_vidioc_try_fmt_vid_cap(struct ipu6_isys_video *av, | |
| 261 | + struct v4l2_format *f) | |
| 262 | +{ | |
| 263 | + ipu6_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat, | |
| 264 | + &f->fmt.pix.width, &f->fmt.pix.height, | |
| 265 | + &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage); | |
| 266 | + | |
| 267 | + f->fmt.pix.field = V4L2_FIELD_NONE; | |
| 268 | + f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; | |
| 269 | + f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; | |
| 270 | + f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT; | |
| 271 | + f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; | |
| 272 | +} | |
| 273 | + | |
| 274 | +static int ipu6_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh, | |
| 275 | + struct v4l2_format *f) | |
| 276 | +{ | |
| 277 | + struct ipu6_isys_video *av = video_drvdata(file); | |
| 278 | + | |
| 279 | + if (vb2_is_busy(&av->aq.vbq)) | |
| 280 | + return -EBUSY; | |
| 281 | + | |
| 282 | + __ipu6_isys_vidioc_try_fmt_vid_cap(av, f); | |
| 283 | + | |
| 284 | + return 0; | |
| 285 | +} | |
| 286 | + | |
| 287 | +static int __ipu6_isys_vidioc_try_fmt_meta_cap(struct ipu6_isys_video *av, | |
| 288 | + struct v4l2_format *f) | |
| 289 | +{ | |
| 290 | + ipu6_isys_try_fmt_cap(av, f->type, &f->fmt.meta.dataformat, | |
| 291 | + &f->fmt.meta.width, &f->fmt.meta.height, | |
| 292 | + &f->fmt.meta.bytesperline, | |
| 293 | + &f->fmt.meta.buffersize); | |
| 294 | + | |
| 295 | + return 0; | |
| 296 | +} | |
| 297 | + | |
| 298 | +static int ipu6_isys_vidioc_try_fmt_meta_cap(struct file *file, void *fh, | |
| 299 | + struct v4l2_format *f) | |
| 300 | +{ | |
| 301 | + struct ipu6_isys_video *av = video_drvdata(file); | |
| 302 | + | |
| 303 | + __ipu6_isys_vidioc_try_fmt_meta_cap(av, f); | |
| 304 | + | |
| 305 | + return 0; | |
| 306 | +} | |
| 307 | + | |
| 308 | +static int ipu6_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh, | |
| 309 | + struct v4l2_format *f) | |
| 310 | +{ | |
| 311 | + struct ipu6_isys_video *av = video_drvdata(file); | |
| 312 | + | |
| 313 | + ipu6_isys_vidioc_try_fmt_vid_cap(file, fh, f); | |
| 314 | + av->pix_fmt = f->fmt.pix; | |
| 315 | + | |
| 316 | + return 0; | |
| 317 | +} | |
| 318 | + | |
| 319 | +static int ipu6_isys_vidioc_s_fmt_meta_cap(struct file *file, void *fh, | |
| 320 | + struct v4l2_format *f) | |
| 321 | +{ | |
| 322 | + struct ipu6_isys_video *av = video_drvdata(file); | |
| 323 | + | |
| 324 | + if (vb2_is_busy(&av->aq.vbq)) | |
| 325 | + return -EBUSY; | |
| 326 | + | |
| 327 | + ipu6_isys_vidioc_try_fmt_meta_cap(file, fh, f); | |
| 328 | + av->meta_fmt = f->fmt.meta; | |
| 329 | + | |
| 330 | + return 0; | |
| 331 | +} | |
| 332 | + | |
| 333 | +static int ipu6_isys_vidioc_reqbufs(struct file *file, void *priv, | |
| 334 | + struct v4l2_requestbuffers *p) | |
| 335 | +{ | |
| 336 | + struct ipu6_isys_video *av = video_drvdata(file); | |
| 337 | + int ret; | |
| 338 | + | |
| 339 | + av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type); | |
| 340 | + av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type); | |
| 341 | + | |
| 342 | + ret = vb2_queue_change_type(&av->aq.vbq, p->type); | |
| 343 | + if (ret) | |
| 344 | + return ret; | |
| 345 | + | |
| 346 | + return vb2_ioctl_reqbufs(file, priv, p); | |
| 347 | +} | |
| 348 | + | |
| 349 | +static int ipu6_isys_vidioc_create_bufs(struct file *file, void *priv, | |
| 350 | + struct v4l2_create_buffers *p) | |
| 351 | +{ | |
| 352 | + struct ipu6_isys_video *av = video_drvdata(file); | |
| 353 | + int ret; | |
| 354 | + | |
| 355 | + av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type); | |
| 356 | + av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type); | |
| 357 | + | |
| 358 | + ret = vb2_queue_change_type(&av->aq.vbq, p->format.type); | |
| 359 | + if (ret) | |
| 360 | + return ret; | |
| 361 | + | |
| 362 | + return vb2_ioctl_create_bufs(file, priv, p); | |
| 363 | +} | |
| 364 | + | |
| 365 | +static int link_validate(struct media_link *link) | |
| 366 | +{ | |
| 367 | + struct ipu6_isys_video *av = | |
| 368 | + container_of(link->sink, struct ipu6_isys_video, pad); | |
| 369 | + struct device *dev = &av->isys->adev->auxdev.dev; | |
| 370 | + struct v4l2_subdev_state *s_state; | |
| 371 | + struct v4l2_subdev *s_sd; | |
| 372 | + struct v4l2_mbus_framefmt *s_fmt; | |
| 373 | + struct media_pad *s_pad; | |
| 374 | + u32 s_stream, code; | |
| 375 | + int ret = -EPIPE; | |
| 376 | + | |
| 377 | + if (!link->source->entity) | |
| 378 | + return ret; | |
| 379 | + | |
| 380 | + s_sd = media_entity_to_v4l2_subdev(link->source->entity); | |
| 381 | + s_state = v4l2_subdev_get_unlocked_active_state(s_sd); | |
| 382 | + if (!s_state) | |
| 383 | + return ret; | |
| 384 | + | |
| 385 | + dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n", | |
| 386 | + link->source->entity->name, link->source->index, | |
| 387 | + link->sink->entity->name); | |
| 388 | + | |
| 389 | + s_pad = media_pad_remote_pad_first(&av->pad); | |
| 390 | + s_stream = ipu6_isys_get_src_stream_by_src_pad(s_sd, s_pad->index); | |
| 391 | + | |
| 392 | + v4l2_subdev_lock_state(s_state); | |
| 393 | + | |
| 394 | + s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream); | |
| 395 | + if (!s_fmt) { | |
| 396 | + dev_err(dev, "failed to get source pad format\n"); | |
| 397 | + goto unlock; | |
| 398 | + } | |
| 399 | + | |
| 400 | + code = ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0)->code; | |
| 401 | + | |
| 402 | + if (s_fmt->width != ipu6_isys_get_frame_width(av) || | |
| 403 | + s_fmt->height != ipu6_isys_get_frame_height(av) || | |
| 404 | + s_fmt->code != code) { | |
| 405 | + dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n", | |
| 406 | + s_fmt->width, s_fmt->height, s_fmt->code, | |
| 407 | + ipu6_isys_get_frame_width(av), | |
| 408 | + ipu6_isys_get_frame_height(av), code); | |
| 409 | + goto unlock; | |
| 410 | + } | |
| 411 | + | |
| 412 | + v4l2_subdev_unlock_state(s_state); | |
| 413 | + | |
| 414 | + return 0; | |
| 415 | +unlock: | |
| 416 | + v4l2_subdev_unlock_state(s_state); | |
| 417 | + | |
| 418 | + return ret; | |
| 419 | +} | |
| 420 | + | |
| 421 | +static void get_stream_opened(struct ipu6_isys_video *av) | |
| 422 | +{ | |
| 423 | + unsigned long flags; | |
| 424 | + | |
| 425 | + spin_lock_irqsave(&av->isys->streams_lock, flags); | |
| 426 | + av->isys->stream_opened++; | |
| 427 | + spin_unlock_irqrestore(&av->isys->streams_lock, flags); | |
| 428 | +} | |
| 429 | + | |
| 430 | +static void put_stream_opened(struct ipu6_isys_video *av) | |
| 431 | +{ | |
| 432 | + unsigned long flags; | |
| 433 | + | |
| 434 | + spin_lock_irqsave(&av->isys->streams_lock, flags); | |
| 435 | + av->isys->stream_opened--; | |
| 436 | + spin_unlock_irqrestore(&av->isys->streams_lock, flags); | |
| 437 | +} | |
| 438 | + | |
| 439 | +static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av, | |
| 440 | + struct ipu6_fw_isys_stream_cfg_data_abi *cfg) | |
| 441 | +{ | |
| 442 | + struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad); | |
| 443 | + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity); | |
| 444 | + struct ipu6_fw_isys_input_pin_info_abi *input_pin; | |
| 445 | + struct ipu6_fw_isys_output_pin_info_abi *output_pin; | |
| 446 | + struct ipu6_isys_stream *stream = av->stream; | |
| 447 | + struct ipu6_isys_queue *aq = &av->aq; | |
| 448 | + struct v4l2_mbus_framefmt fmt; | |
| 449 | + const struct ipu6_isys_pixelformat *pfmt = | |
| 450 | + ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); | |
| 451 | + struct v4l2_rect v4l2_crop; | |
| 452 | + struct ipu6_isys *isys = av->isys; | |
| 453 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 454 | + int input_pins = cfg->nof_input_pins++; | |
| 455 | + int output_pins; | |
| 456 | + u32 src_stream; | |
| 457 | + int ret; | |
| 458 | + | |
| 459 | + src_stream = ipu6_isys_get_src_stream_by_src_pad(sd, src_pad->index); | |
| 460 | + ret = ipu6_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream, | |
| 461 | + &fmt); | |
| 462 | + if (ret < 0) { | |
| 463 | + dev_err(dev, "can't get stream format (%d)\n", ret); | |
| 464 | + return ret; | |
| 465 | + } | |
| 466 | + | |
| 467 | + ret = ipu6_isys_get_stream_pad_crop(sd, src_pad->index, src_stream, | |
| 468 | + &v4l2_crop); | |
| 469 | + if (ret < 0) { | |
| 470 | + dev_err(dev, "can't get stream crop (%d)\n", ret); | |
| 471 | + return ret; | |
| 472 | + } | |
| 473 | + | |
| 474 | + input_pin = &cfg->input_pins[input_pins]; | |
| 475 | + input_pin->input_res.width = fmt.width; | |
| 476 | + input_pin->input_res.height = fmt.height; | |
| 477 | + input_pin->dt = av->dt; | |
| 478 | + input_pin->bits_per_pix = pfmt->bpp_packed; | |
| 479 | + input_pin->mapped_dt = 0x40; /* invalid mipi data type */ | |
| 480 | + input_pin->mipi_decompression = 0; | |
| 481 | + input_pin->capture_mode = IPU6_FW_ISYS_CAPTURE_MODE_REGULAR; | |
| 482 | + input_pin->mipi_store_mode = pfmt->bpp == pfmt->bpp_packed ? | |
| 483 | + IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER : | |
| 484 | + IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL; | |
| 485 | + input_pin->crop_first_and_last_lines = v4l2_crop.top & 1; | |
| 486 | + | |
| 487 | + output_pins = cfg->nof_output_pins++; | |
| 488 | + aq->fw_output = output_pins; | |
| 489 | + stream->output_pins[output_pins].pin_ready = ipu6_isys_queue_buf_ready; | |
| 490 | + stream->output_pins[output_pins].aq = aq; | |
| 491 | + | |
| 492 | + output_pin = &cfg->output_pins[output_pins]; | |
| 493 | + output_pin->input_pin_id = input_pins; | |
| 494 | + output_pin->output_res.width = ipu6_isys_get_frame_width(av); | |
| 495 | + output_pin->output_res.height = ipu6_isys_get_frame_height(av); | |
| 496 | + | |
| 497 | + output_pin->stride = ipu6_isys_get_bytes_per_line(av); | |
| 498 | + if (pfmt->bpp != pfmt->bpp_packed) | |
| 499 | + output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC; | |
| 500 | + else | |
| 501 | + output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_MIPI; | |
| 502 | + output_pin->ft = pfmt->css_pixelformat; | |
| 503 | + output_pin->send_irq = 1; | |
| 504 | + memset(output_pin->ts_offsets, 0, sizeof(output_pin->ts_offsets)); | |
| 505 | + output_pin->s2m_pixel_soc_pixel_remapping = | |
| 506 | + S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING; | |
| 507 | + output_pin->csi_be_soc_pixel_remapping = | |
| 508 | + CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING; | |
| 509 | + | |
| 510 | + output_pin->snoopable = true; | |
| 511 | + output_pin->error_handling_enable = false; | |
| 512 | + output_pin->sensor_type = isys->sensor_type++; | |
| 513 | + if (isys->sensor_type > isys->pdata->ipdata->sensor_type_end) | |
| 514 | + isys->sensor_type = isys->pdata->ipdata->sensor_type_start; | |
| 515 | + | |
| 516 | + return 0; | |
| 517 | +} | |
| 518 | + | |
| 519 | +static int start_stream_firmware(struct ipu6_isys_video *av, | |
| 520 | + struct ipu6_isys_buffer_list *bl) | |
| 521 | +{ | |
| 522 | + struct ipu6_fw_isys_stream_cfg_data_abi *stream_cfg; | |
| 523 | + struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL; | |
| 524 | + struct ipu6_isys_stream *stream = av->stream; | |
| 525 | + struct device *dev = &av->isys->adev->auxdev.dev; | |
| 526 | + struct isys_fw_msgs *msg = NULL; | |
| 527 | + struct ipu6_isys_queue *aq; | |
| 528 | + int ret, retout, tout; | |
| 529 | + u16 send_type; | |
| 530 | + | |
| 531 | + msg = ipu6_get_fw_msg_buf(stream); | |
| 532 | + if (!msg) | |
| 533 | + return -ENOMEM; | |
| 534 | + | |
| 535 | + stream_cfg = &msg->fw_msg.stream; | |
| 536 | + stream_cfg->src = stream->stream_source; | |
| 537 | + stream_cfg->vc = stream->vc; | |
| 538 | + stream_cfg->isl_use = 0; | |
| 539 | + stream_cfg->sensor_type = IPU6_FW_ISYS_SENSOR_MODE_NORMAL; | |
| 540 | + | |
| 541 | + list_for_each_entry(aq, &stream->queues, node) { | |
| 542 | + struct ipu6_isys_video *__av = ipu6_isys_queue_to_video(aq); | |
| 543 | + | |
| 544 | + ret = ipu6_isys_fw_pin_cfg(__av, stream_cfg); | |
| 545 | + if (ret < 0) { | |
| 546 | + ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg); | |
| 547 | + return ret; | |
| 548 | + } | |
| 549 | + } | |
| 550 | + | |
| 551 | + ipu6_fw_isys_dump_stream_cfg(dev, stream_cfg); | |
| 552 | + | |
| 553 | + stream->nr_output_pins = stream_cfg->nof_output_pins; | |
| 554 | + | |
| 555 | + reinit_completion(&stream->stream_open_completion); | |
| 556 | + | |
| 557 | + ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle, | |
| 558 | + stream_cfg, msg->dma_addr, | |
| 559 | + sizeof(*stream_cfg), | |
| 560 | + IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN); | |
| 561 | + if (ret < 0) { | |
| 562 | + dev_err(dev, "can't open stream (%d)\n", ret); | |
| 563 | + ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg); | |
| 564 | + return ret; | |
| 565 | + } | |
| 566 | + | |
| 567 | + get_stream_opened(av); | |
| 568 | + | |
| 569 | + tout = wait_for_completion_timeout(&stream->stream_open_completion, | |
| 570 | + IPU6_FW_CALL_TIMEOUT_JIFFIES); | |
| 571 | + | |
| 572 | + ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg); | |
| 573 | + | |
| 574 | + if (!tout) { | |
| 575 | + dev_err(dev, "stream open time out\n"); | |
| 576 | + ret = -ETIMEDOUT; | |
| 577 | + goto out_put_stream_opened; | |
| 578 | + } | |
| 579 | + if (stream->error) { | |
| 580 | + dev_err(dev, "stream open error: %d\n", stream->error); | |
| 581 | + ret = -EIO; | |
| 582 | + goto out_put_stream_opened; | |
| 583 | + } | |
| 584 | + dev_dbg(dev, "start stream: open complete\n"); | |
| 585 | + | |
| 586 | + if (bl) { | |
| 587 | + msg = ipu6_get_fw_msg_buf(stream); | |
| 588 | + if (!msg) { | |
| 589 | + ret = -ENOMEM; | |
| 590 | + goto out_put_stream_opened; | |
| 591 | + } | |
| 592 | + buf = &msg->fw_msg.frame; | |
| 593 | + ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl); | |
| 594 | + ipu6_isys_buffer_list_queue(bl, | |
| 595 | + IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0); | |
| 596 | + } | |
| 597 | + | |
| 598 | + reinit_completion(&stream->stream_start_completion); | |
| 599 | + | |
| 600 | + if (bl) { | |
| 601 | + send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE; | |
| 602 | + ipu6_fw_isys_dump_frame_buff_set(dev, buf, | |
| 603 | + stream_cfg->nof_output_pins); | |
| 604 | + ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle, | |
| 605 | + buf, msg->dma_addr, | |
| 606 | + sizeof(*buf), send_type); | |
| 607 | + } else { | |
| 608 | + send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START; | |
| 609 | + ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle, | |
| 610 | + send_type); | |
| 611 | + } | |
| 612 | + | |
| 613 | + if (ret < 0) { | |
| 614 | + dev_err(dev, "can't start streaming (%d)\n", ret); | |
| 615 | + goto out_stream_close; | |
| 616 | + } | |
| 617 | + | |
| 618 | + tout = wait_for_completion_timeout(&stream->stream_start_completion, | |
| 619 | + IPU6_FW_CALL_TIMEOUT_JIFFIES); | |
| 620 | + if (!tout) { | |
| 621 | + dev_err(dev, "stream start time out\n"); | |
| 622 | + ret = -ETIMEDOUT; | |
| 623 | + goto out_stream_close; | |
| 624 | + } | |
| 625 | + if (stream->error) { | |
| 626 | + dev_err(dev, "stream start error: %d\n", stream->error); | |
| 627 | + ret = -EIO; | |
| 628 | + goto out_stream_close; | |
| 629 | + } | |
| 630 | + dev_dbg(dev, "start stream: complete\n"); | |
| 631 | + | |
| 632 | + return 0; | |
| 633 | + | |
| 634 | +out_stream_close: | |
| 635 | + reinit_completion(&stream->stream_close_completion); | |
| 636 | + | |
| 637 | + retout = ipu6_fw_isys_simple_cmd(av->isys, | |
| 638 | + stream->stream_handle, | |
| 639 | + IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE); | |
| 640 | + if (retout < 0) { | |
| 641 | + dev_dbg(dev, "can't close stream (%d)\n", retout); | |
| 642 | + goto out_put_stream_opened; | |
| 643 | + } | |
| 644 | + | |
| 645 | + tout = wait_for_completion_timeout(&stream->stream_close_completion, | |
| 646 | + IPU6_FW_CALL_TIMEOUT_JIFFIES); | |
| 647 | + if (!tout) | |
| 648 | + dev_err(dev, "stream close time out\n"); | |
| 649 | + else if (stream->error) | |
| 650 | + dev_err(dev, "stream close error: %d\n", stream->error); | |
| 651 | + else | |
| 652 | + dev_dbg(dev, "stream close complete\n"); | |
| 653 | + | |
| 654 | +out_put_stream_opened: | |
| 655 | + put_stream_opened(av); | |
| 656 | + | |
| 657 | + return ret; | |
| 658 | +} | |
| 659 | + | |
| 660 | +static void stop_streaming_firmware(struct ipu6_isys_video *av) | |
| 661 | +{ | |
| 662 | + struct device *dev = &av->isys->adev->auxdev.dev; | |
| 663 | + struct ipu6_isys_stream *stream = av->stream; | |
| 664 | + int ret, tout; | |
| 665 | + | |
| 666 | + reinit_completion(&stream->stream_stop_completion); | |
| 667 | + | |
| 668 | + ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle, | |
| 669 | + IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH); | |
| 670 | + | |
| 671 | + if (ret < 0) { | |
| 672 | + dev_err(dev, "can't stop stream (%d)\n", ret); | |
| 673 | + return; | |
| 674 | + } | |
| 675 | + | |
| 676 | + tout = wait_for_completion_timeout(&stream->stream_stop_completion, | |
| 677 | + IPU6_FW_CALL_TIMEOUT_JIFFIES); | |
| 678 | + if (!tout) | |
| 679 | + dev_warn(dev, "stream stop time out\n"); | |
| 680 | + else if (stream->error) | |
| 681 | + dev_warn(dev, "stream stop error: %d\n", stream->error); | |
| 682 | + else | |
| 683 | + dev_dbg(dev, "stop stream: complete\n"); | |
| 684 | +} | |
| 685 | + | |
| 686 | +static void close_streaming_firmware(struct ipu6_isys_video *av) | |
| 687 | +{ | |
| 688 | + struct ipu6_isys_stream *stream = av->stream; | |
| 689 | + struct device *dev = &av->isys->adev->auxdev.dev; | |
| 690 | + int ret, tout; | |
| 691 | + | |
| 692 | + reinit_completion(&stream->stream_close_completion); | |
| 693 | + | |
| 694 | + ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle, | |
| 695 | + IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE); | |
| 696 | + if (ret < 0) { | |
| 697 | + dev_err(dev, "can't close stream (%d)\n", ret); | |
| 698 | + return; | |
| 699 | + } | |
| 700 | + | |
| 701 | + tout = wait_for_completion_timeout(&stream->stream_close_completion, | |
| 702 | + IPU6_FW_CALL_TIMEOUT_JIFFIES); | |
| 703 | + if (!tout) | |
| 704 | + dev_warn(dev, "stream close time out\n"); | |
| 705 | + else if (stream->error) | |
| 706 | + dev_warn(dev, "stream close error: %d\n", stream->error); | |
| 707 | + else | |
| 708 | + dev_dbg(dev, "close stream: complete\n"); | |
| 709 | + | |
| 710 | + put_stream_opened(av); | |
| 711 | +} | |
| 712 | + | |
| 713 | +int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av, | |
| 714 | + struct media_entity *source_entity, | |
| 715 | + int nr_queues) | |
| 716 | +{ | |
| 717 | + struct ipu6_isys_stream *stream = av->stream; | |
| 718 | + struct ipu6_isys_csi2 *csi2; | |
| 719 | + | |
| 720 | + if (WARN_ON(stream->nr_streaming)) | |
| 721 | + return -EINVAL; | |
| 722 | + | |
| 723 | + stream->nr_queues = nr_queues; | |
| 724 | + atomic_set(&stream->sequence, 0); | |
| 725 | + | |
| 726 | + stream->seq_index = 0; | |
| 727 | + memset(stream->seq, 0, sizeof(stream->seq)); | |
| 728 | + | |
| 729 | + if (WARN_ON(!list_empty(&stream->queues))) | |
| 730 | + return -EINVAL; | |
| 731 | + | |
| 732 | + stream->stream_source = stream->asd->source; | |
| 733 | + csi2 = ipu6_isys_subdev_to_csi2(stream->asd); | |
| 734 | + csi2->receiver_errors = 0; | |
| 735 | + stream->source_entity = source_entity; | |
| 736 | + | |
| 737 | + dev_dbg(&av->isys->adev->auxdev.dev, | |
| 738 | + "prepare stream: external entity %s\n", | |
| 739 | + stream->source_entity->name); | |
| 740 | + | |
| 741 | + return 0; | |
| 742 | +} | |
| 743 | + | |
| 744 | +void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, | |
| 745 | + bool state) | |
| 746 | +{ | |
| 747 | + struct ipu6_isys *isys = av->isys; | |
| 748 | + struct ipu6_isys_csi2 *csi2 = NULL; | |
| 749 | + struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; | |
| 750 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 751 | + struct v4l2_mbus_framefmt format; | |
| 752 | + struct v4l2_subdev *esd; | |
| 753 | + struct v4l2_control hb = { .id = V4L2_CID_HBLANK, .value = 0 }; | |
| 754 | + unsigned int bpp, lanes; | |
| 755 | + s64 link_freq = 0; | |
| 756 | + u64 pixel_rate = 0; | |
| 757 | + int ret; | |
| 758 | + | |
| 759 | + if (!state) | |
| 760 | + return; | |
| 761 | + | |
| 762 | + esd = media_entity_to_v4l2_subdev(av->stream->source_entity); | |
| 763 | + | |
| 764 | + av->watermark.width = ipu6_isys_get_frame_width(av); | |
| 765 | + av->watermark.height = ipu6_isys_get_frame_height(av); | |
| 766 | + av->watermark.sram_gran_shift = isys->pdata->ipdata->sram_gran_shift; | |
| 767 | + av->watermark.sram_gran_size = isys->pdata->ipdata->sram_gran_size; | |
| 768 | + | |
| 769 | + ret = v4l2_g_ctrl(esd->ctrl_handler, &hb); | |
| 770 | + if (!ret && hb.value >= 0) | |
| 771 | + av->watermark.hblank = hb.value; | |
| 772 | + else | |
| 773 | + av->watermark.hblank = 0; | |
| 774 | + | |
| 775 | + csi2 = ipu6_isys_subdev_to_csi2(av->stream->asd); | |
| 776 | + link_freq = ipu6_isys_csi2_get_link_freq(csi2); | |
| 777 | + if (link_freq > 0) { | |
| 778 | + lanes = csi2->nlanes; | |
| 779 | + ret = ipu6_isys_get_stream_pad_fmt(&csi2->asd.sd, 0, | |
| 780 | + av->source_stream, &format); | |
| 781 | + if (!ret) { | |
| 782 | + bpp = ipu6_isys_mbus_code_to_bpp(format.code); | |
| 783 | + pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp); | |
| 784 | + } | |
| 785 | + } | |
| 786 | + | |
| 787 | + av->watermark.pixel_rate = pixel_rate; | |
| 788 | + | |
| 789 | + if (!pixel_rate) { | |
| 790 | + mutex_lock(&iwake_watermark->mutex); | |
| 791 | + iwake_watermark->force_iwake_disable = true; | |
| 792 | + mutex_unlock(&iwake_watermark->mutex); | |
| 793 | + dev_warn(dev, "unexpected pixel_rate from %s, disable iwake.\n", | |
| 794 | + av->stream->source_entity->name); | |
| 795 | + } | |
| 796 | +} | |
| 797 | + | |
| 798 | +static void calculate_stream_datarate(struct ipu6_isys_video *av) | |
| 799 | +{ | |
| 800 | + struct video_stream_watermark *watermark = &av->watermark; | |
| 801 | + const struct ipu6_isys_pixelformat *pfmt = | |
| 802 | + ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); | |
| 803 | + u32 pages_per_line, pb_bytes_per_line, pixels_per_line, bytes_per_line; | |
| 804 | + u64 line_time_ns, stream_data_rate; | |
| 805 | + u16 shift, size; | |
| 806 | + | |
| 807 | + shift = watermark->sram_gran_shift; | |
| 808 | + size = watermark->sram_gran_size; | |
| 809 | + | |
| 810 | + pixels_per_line = watermark->width + watermark->hblank; | |
| 811 | + line_time_ns = div_u64(pixels_per_line * NSEC_PER_SEC, | |
| 812 | + watermark->pixel_rate); | |
| 813 | + bytes_per_line = watermark->width * pfmt->bpp / 8; | |
| 814 | + pages_per_line = DIV_ROUND_UP(bytes_per_line, size); | |
| 815 | + pb_bytes_per_line = pages_per_line << shift; | |
| 816 | + stream_data_rate = div64_u64(pb_bytes_per_line * 1000, line_time_ns); | |
| 817 | + | |
| 818 | + watermark->stream_data_rate = stream_data_rate; | |
| 819 | +} | |
| 820 | + | |
| 821 | +void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state) | |
| 822 | +{ | |
| 823 | + struct isys_iwake_watermark *iwake_watermark = | |
| 824 | + &av->isys->iwake_watermark; | |
| 825 | + | |
| 826 | + if (!av->watermark.pixel_rate) | |
| 827 | + return; | |
| 828 | + | |
| 829 | + if (state) { | |
| 830 | + calculate_stream_datarate(av); | |
| 831 | + mutex_lock(&iwake_watermark->mutex); | |
| 832 | + list_add(&av->watermark.stream_node, | |
| 833 | + &iwake_watermark->video_list); | |
| 834 | + mutex_unlock(&iwake_watermark->mutex); | |
| 835 | + } else { | |
| 836 | + av->watermark.stream_data_rate = 0; | |
| 837 | + mutex_lock(&iwake_watermark->mutex); | |
| 838 | + list_del(&av->watermark.stream_node); | |
| 839 | + mutex_unlock(&iwake_watermark->mutex); | |
| 840 | + } | |
| 841 | + | |
| 842 | + update_watermark_setting(av->isys); | |
| 843 | +} | |
| 844 | + | |
| 845 | +void ipu6_isys_put_stream(struct ipu6_isys_stream *stream) | |
| 846 | +{ | |
| 847 | + struct device *dev; | |
| 848 | + unsigned int i; | |
| 849 | + unsigned long flags; | |
| 850 | + | |
| 851 | + if (!stream) { | |
| 852 | + pr_err("ipu6-isys: no available stream\n"); | |
| 853 | + return; | |
| 854 | + } | |
| 855 | + | |
| 856 | + dev = &stream->isys->adev->auxdev.dev; | |
| 857 | + | |
| 858 | + spin_lock_irqsave(&stream->isys->streams_lock, flags); | |
| 859 | + for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { | |
| 860 | + if (&stream->isys->streams[i] == stream) { | |
| 861 | + if (stream->isys->streams_ref_count[i] > 0) | |
| 862 | + stream->isys->streams_ref_count[i]--; | |
| 863 | + else | |
| 864 | + dev_warn(dev, "invalid stream %d\n", i); | |
| 865 | + | |
| 866 | + break; | |
| 867 | + } | |
| 868 | + } | |
| 869 | + spin_unlock_irqrestore(&stream->isys->streams_lock, flags); | |
| 870 | +} | |
| 871 | + | |
| 872 | +static struct ipu6_isys_stream * | |
| 873 | +ipu6_isys_get_stream(struct ipu6_isys_video *av, struct ipu6_isys_subdev *asd) | |
| 874 | +{ | |
| 875 | + struct ipu6_isys_stream *stream = NULL; | |
| 876 | + struct ipu6_isys *isys = av->isys; | |
| 877 | + unsigned long flags; | |
| 878 | + unsigned int i; | |
| 879 | + u8 vc = av->vc; | |
| 880 | + | |
| 881 | + if (!isys) | |
| 882 | + return NULL; | |
| 883 | + | |
| 884 | + spin_lock_irqsave(&isys->streams_lock, flags); | |
| 885 | + for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { | |
| 886 | + if (isys->streams_ref_count[i] && isys->streams[i].vc == vc && | |
| 887 | + isys->streams[i].asd == asd) { | |
| 888 | + isys->streams_ref_count[i]++; | |
| 889 | + stream = &isys->streams[i]; | |
| 890 | + break; | |
| 891 | + } | |
| 892 | + } | |
| 893 | + | |
| 894 | + if (!stream) { | |
| 895 | + for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { | |
| 896 | + if (!isys->streams_ref_count[i]) { | |
| 897 | + isys->streams_ref_count[i]++; | |
| 898 | + stream = &isys->streams[i]; | |
| 899 | + stream->vc = vc; | |
| 900 | + stream->asd = asd; | |
| 901 | + break; | |
| 902 | + } | |
| 903 | + } | |
| 904 | + } | |
| 905 | + spin_unlock_irqrestore(&isys->streams_lock, flags); | |
| 906 | + | |
| 907 | + return stream; | |
| 908 | +} | |
| 909 | + | |
| 910 | +struct ipu6_isys_stream * | |
| 911 | +ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle) | |
| 912 | +{ | |
| 913 | + unsigned long flags; | |
| 914 | + struct ipu6_isys_stream *stream = NULL; | |
| 915 | + | |
| 916 | + if (!isys) | |
| 917 | + return NULL; | |
| 918 | + | |
| 919 | + if (stream_handle >= IPU6_ISYS_MAX_STREAMS) { | |
| 920 | + dev_err(&isys->adev->auxdev.dev, | |
| 921 | + "stream_handle %d is invalid\n", stream_handle); | |
| 922 | + return NULL; | |
| 923 | + } | |
| 924 | + | |
| 925 | + spin_lock_irqsave(&isys->streams_lock, flags); | |
| 926 | + if (isys->streams_ref_count[stream_handle] > 0) { | |
| 927 | + isys->streams_ref_count[stream_handle]++; | |
| 928 | + stream = &isys->streams[stream_handle]; | |
| 929 | + } | |
| 930 | + spin_unlock_irqrestore(&isys->streams_lock, flags); | |
| 931 | + | |
| 932 | + return stream; | |
| 933 | +} | |
| 934 | + | |
| 935 | +struct ipu6_isys_stream * | |
| 936 | +ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc) | |
| 937 | +{ | |
| 938 | + struct ipu6_isys_stream *stream = NULL; | |
| 939 | + unsigned long flags; | |
| 940 | + unsigned int i; | |
| 941 | + | |
| 942 | + if (!isys) | |
| 943 | + return NULL; | |
| 944 | + | |
| 945 | + if (source < 0) { | |
| 946 | + dev_err(&stream->isys->adev->auxdev.dev, | |
| 947 | + "query stream with invalid port number\n"); | |
| 948 | + return NULL; | |
| 949 | + } | |
| 950 | + | |
| 951 | + spin_lock_irqsave(&isys->streams_lock, flags); | |
| 952 | + for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { | |
| 953 | + if (!isys->streams_ref_count[i]) | |
| 954 | + continue; | |
| 955 | + | |
| 956 | + if (isys->streams[i].stream_source == source && | |
| 957 | + isys->streams[i].vc == vc) { | |
| 958 | + stream = &isys->streams[i]; | |
| 959 | + isys->streams_ref_count[i]++; | |
| 960 | + break; | |
| 961 | + } | |
| 962 | + } | |
| 963 | + spin_unlock_irqrestore(&isys->streams_lock, flags); | |
| 964 | + | |
| 965 | + return stream; | |
| 966 | +} | |
| 967 | + | |
| 968 | +static u64 get_stream_mask_by_pipeline(struct ipu6_isys_video *__av) | |
| 969 | +{ | |
| 970 | + struct media_pipeline *pipeline = | |
| 971 | + media_entity_pipeline(&__av->vdev.entity); | |
| 972 | + unsigned int i; | |
| 973 | + u64 stream_mask = 0; | |
| 974 | + | |
| 975 | + for (i = 0; i < NR_OF_CSI2_SRC_PADS; i++) { | |
| 976 | + struct ipu6_isys_video *av = &__av->csi2->av[i]; | |
| 977 | + | |
| 978 | + if (pipeline == media_entity_pipeline(&av->vdev.entity)) | |
| 979 | + stream_mask |= BIT_ULL(av->source_stream); | |
| 980 | + } | |
| 981 | + | |
| 982 | + return stream_mask; | |
| 983 | +} | |
| 984 | + | |
| 985 | +int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, | |
| 986 | + struct ipu6_isys_buffer_list *bl) | |
| 987 | +{ | |
| 988 | + struct v4l2_subdev_krouting *routing; | |
| 989 | + struct ipu6_isys_stream *stream = av->stream; | |
| 990 | + struct v4l2_subdev_state *subdev_state; | |
| 991 | + struct device *dev = &av->isys->adev->auxdev.dev; | |
| 992 | + struct v4l2_subdev *sd; | |
| 993 | + struct v4l2_subdev *ssd; | |
| 994 | + struct media_pad *r_pad; | |
| 995 | + struct media_pad *s_pad; | |
| 996 | + u32 sink_pad, sink_stream; | |
| 997 | + u64 r_stream; | |
| 998 | + u64 stream_mask = 0; | |
| 999 | + int ret = 0; | |
| 1000 | + | |
| 1001 | + dev_dbg(dev, "set stream: %d\n", state); | |
| 1002 | + | |
| 1003 | + if (WARN(!stream->source_entity, "No source entity for stream\n")) | |
| 1004 | + return -ENODEV; | |
| 1005 | + | |
| 1006 | + ssd = media_entity_to_v4l2_subdev(stream->source_entity); | |
| 1007 | + sd = &stream->asd->sd; | |
| 1008 | + r_pad = media_pad_remote_pad_first(&av->pad); | |
| 1009 | + r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index); | |
| 1010 | + | |
| 1011 | + subdev_state = v4l2_subdev_lock_and_get_active_state(sd); | |
| 1012 | + routing = &subdev_state->routing; | |
| 1013 | + ret = v4l2_subdev_routing_find_opposite_end(routing, r_pad->index, | |
| 1014 | + r_stream, &sink_pad, | |
| 1015 | + &sink_stream); | |
| 1016 | + v4l2_subdev_unlock_state(subdev_state); | |
| 1017 | + if (ret) | |
| 1018 | + return ret; | |
| 1019 | + | |
| 1020 | + s_pad = media_pad_remote_pad_first(&stream->asd->pad[sink_pad]); | |
| 1021 | + | |
| 1022 | + stream_mask = get_stream_mask_by_pipeline(av); | |
| 1023 | + if (!state) { | |
| 1024 | + stop_streaming_firmware(av); | |
| 1025 | + | |
| 1026 | + /* stop external sub-device now. */ | |
| 1027 | + dev_dbg(dev, "disable streams 0x%llx of %s\n", stream_mask, | |
| 1028 | + ssd->name); | |
| 1029 | + ret = v4l2_subdev_disable_streams(ssd, s_pad->index, | |
| 1030 | + stream_mask); | |
| 1031 | + if (ret) { | |
| 1032 | + dev_err(dev, "disable streams of %s failed with %d\n", | |
| 1033 | + ssd->name, ret); | |
| 1034 | + return ret; | |
| 1035 | + } | |
| 1036 | + | |
| 1037 | + /* stop sub-device which connects with video */ | |
| 1038 | + dev_dbg(dev, "stream off entity %s pad:%d\n", sd->name, | |
| 1039 | + r_pad->index); | |
| 1040 | + ret = v4l2_subdev_call(sd, video, s_stream, state); | |
| 1041 | + if (ret) { | |
| 1042 | + dev_err(dev, "stream off %s failed with %d\n", sd->name, | |
| 1043 | + ret); | |
| 1044 | + return ret; | |
| 1045 | + } | |
| 1046 | + close_streaming_firmware(av); | |
| 1047 | + } else { | |
| 1048 | + ret = start_stream_firmware(av, bl); | |
| 1049 | + if (ret) { | |
| 1050 | + dev_err(dev, "start stream of firmware failed\n"); | |
| 1051 | + return ret; | |
| 1052 | + } | |
| 1053 | + | |
| 1054 | + /* start sub-device which connects with video */ | |
| 1055 | + dev_dbg(dev, "stream on %s pad %d\n", sd->name, r_pad->index); | |
| 1056 | + ret = v4l2_subdev_call(sd, video, s_stream, state); | |
| 1057 | + if (ret) { | |
| 1058 | + dev_err(dev, "stream on %s failed with %d\n", sd->name, | |
| 1059 | + ret); | |
| 1060 | + goto out_media_entity_stop_streaming_firmware; | |
| 1061 | + } | |
| 1062 | + | |
| 1063 | + /* start external sub-device now. */ | |
| 1064 | + dev_dbg(dev, "enable streams 0x%llx of %s\n", stream_mask, | |
| 1065 | + ssd->name); | |
| 1066 | + ret = v4l2_subdev_enable_streams(ssd, s_pad->index, | |
| 1067 | + stream_mask); | |
| 1068 | + if (ret) { | |
| 1069 | + dev_err(dev, | |
| 1070 | + "enable streams 0x%llx of %s failed with %d\n", | |
| 1071 | + stream_mask, stream->source_entity->name, ret); | |
| 1072 | + goto out_media_entity_stop_streaming; | |
| 1073 | + } | |
| 1074 | + } | |
| 1075 | + | |
| 1076 | + av->streaming = state; | |
| 1077 | + | |
| 1078 | + return 0; | |
| 1079 | + | |
| 1080 | +out_media_entity_stop_streaming: | |
| 1081 | + v4l2_subdev_disable_streams(sd, r_pad->index, BIT(r_stream)); | |
| 1082 | + | |
| 1083 | +out_media_entity_stop_streaming_firmware: | |
| 1084 | + stop_streaming_firmware(av); | |
| 1085 | + | |
| 1086 | + return ret; | |
| 1087 | +} | |
| 1088 | + | |
| 1089 | +static const struct v4l2_ioctl_ops ipu6_v4l2_ioctl_ops = { | |
| 1090 | + .vidioc_querycap = ipu6_isys_vidioc_querycap, | |
| 1091 | + .vidioc_enum_fmt_vid_cap = ipu6_isys_vidioc_enum_fmt, | |
| 1092 | + .vidioc_enum_fmt_meta_cap = ipu6_isys_vidioc_enum_fmt, | |
| 1093 | + .vidioc_enum_framesizes = ipu6_isys_vidioc_enum_framesizes, | |
| 1094 | + .vidioc_g_fmt_vid_cap = ipu6_isys_vidioc_g_fmt_vid_cap, | |
| 1095 | + .vidioc_s_fmt_vid_cap = ipu6_isys_vidioc_s_fmt_vid_cap, | |
| 1096 | + .vidioc_try_fmt_vid_cap = ipu6_isys_vidioc_try_fmt_vid_cap, | |
| 1097 | + .vidioc_g_fmt_meta_cap = ipu6_isys_vidioc_g_fmt_meta_cap, | |
| 1098 | + .vidioc_s_fmt_meta_cap = ipu6_isys_vidioc_s_fmt_meta_cap, | |
| 1099 | + .vidioc_try_fmt_meta_cap = ipu6_isys_vidioc_try_fmt_meta_cap, | |
| 1100 | + .vidioc_reqbufs = ipu6_isys_vidioc_reqbufs, | |
| 1101 | + .vidioc_create_bufs = ipu6_isys_vidioc_create_bufs, | |
| 1102 | + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, | |
| 1103 | + .vidioc_querybuf = vb2_ioctl_querybuf, | |
| 1104 | + .vidioc_qbuf = vb2_ioctl_qbuf, | |
| 1105 | + .vidioc_dqbuf = vb2_ioctl_dqbuf, | |
| 1106 | + .vidioc_streamon = vb2_ioctl_streamon, | |
| 1107 | + .vidioc_streamoff = vb2_ioctl_streamoff, | |
| 1108 | + .vidioc_expbuf = vb2_ioctl_expbuf, | |
| 1109 | +}; | |
| 1110 | + | |
| 1111 | +static const struct media_entity_operations entity_ops = { | |
| 1112 | + .link_validate = link_validate, | |
| 1113 | +}; | |
| 1114 | + | |
| 1115 | +static const struct v4l2_file_operations isys_fops = { | |
| 1116 | + .owner = THIS_MODULE, | |
| 1117 | + .poll = vb2_fop_poll, | |
| 1118 | + .unlocked_ioctl = video_ioctl2, | |
| 1119 | + .mmap = vb2_fop_mmap, | |
| 1120 | + .open = video_open, | |
| 1121 | + .release = vb2_fop_release, | |
| 1122 | +}; | |
| 1123 | + | |
| 1124 | +int ipu6_isys_fw_open(struct ipu6_isys *isys) | |
| 1125 | +{ | |
| 1126 | + struct ipu6_bus_device *adev = isys->adev; | |
| 1127 | + const struct ipu6_isys_internal_pdata *ipdata = isys->pdata->ipdata; | |
| 1128 | + int ret; | |
| 1129 | + | |
| 1130 | + ret = pm_runtime_resume_and_get(&adev->auxdev.dev); | |
| 1131 | + if (ret < 0) | |
| 1132 | + return ret; | |
| 1133 | + | |
| 1134 | + mutex_lock(&isys->mutex); | |
| 1135 | + | |
| 1136 | + if (isys->ref_count++) | |
| 1137 | + goto unlock; | |
| 1138 | + | |
| 1139 | + ipu6_configure_spc(adev->isp, &ipdata->hw_variant, | |
| 1140 | + IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX, isys->pdata->base, | |
| 1141 | + adev->pkg_dir, adev->pkg_dir_dma_addr); | |
| 1142 | + | |
| 1143 | + /* | |
| 1144 | + * Buffers could have been left to wrong queue at last closure. | |
| 1145 | + * Move them now back to empty buffer queue. | |
| 1146 | + */ | |
| 1147 | + ipu6_cleanup_fw_msg_bufs(isys); | |
| 1148 | + | |
| 1149 | + if (isys->fwcom) { | |
| 1150 | + /* | |
| 1151 | + * Something went wrong in previous shutdown. As we are now | |
| 1152 | + * restarting isys we can safely delete old context. | |
| 1153 | + */ | |
| 1154 | + dev_warn(&adev->auxdev.dev, "clearing old context\n"); | |
| 1155 | + ipu6_fw_isys_cleanup(isys); | |
| 1156 | + } | |
| 1157 | + | |
| 1158 | + ret = ipu6_fw_isys_init(isys, ipdata->num_parallel_streams); | |
| 1159 | + if (ret < 0) | |
| 1160 | + goto out; | |
| 1161 | + | |
| 1162 | +unlock: | |
| 1163 | + mutex_unlock(&isys->mutex); | |
| 1164 | + | |
| 1165 | + return 0; | |
| 1166 | + | |
| 1167 | +out: | |
| 1168 | + isys->ref_count--; | |
| 1169 | + mutex_unlock(&isys->mutex); | |
| 1170 | + pm_runtime_put(&adev->auxdev.dev); | |
| 1171 | + | |
| 1172 | + return ret; | |
| 1173 | +} | |
| 1174 | + | |
| 1175 | +void ipu6_isys_fw_close(struct ipu6_isys *isys) | |
| 1176 | +{ | |
| 1177 | + mutex_lock(&isys->mutex); | |
| 1178 | + | |
| 1179 | + isys->ref_count--; | |
| 1180 | + if (!isys->ref_count) { | |
| 1181 | + ipu6_fw_isys_close(isys); | |
| 1182 | + if (isys->fwcom) { | |
| 1183 | + isys->need_reset = true; | |
| 1184 | + dev_warn(&isys->adev->auxdev.dev, | |
| 1185 | + "failed to close fw isys\n"); | |
| 1186 | + } | |
| 1187 | + } | |
| 1188 | + | |
| 1189 | + mutex_unlock(&isys->mutex); | |
| 1190 | + | |
| 1191 | + if (isys->need_reset) | |
| 1192 | + pm_runtime_put_sync(&isys->adev->auxdev.dev); | |
| 1193 | + else | |
| 1194 | + pm_runtime_put(&isys->adev->auxdev.dev); | |
| 1195 | +} | |
| 1196 | + | |
| 1197 | +int ipu6_isys_setup_video(struct ipu6_isys_video *av, | |
| 1198 | + struct media_entity **source_entity, int *nr_queues) | |
| 1199 | +{ | |
| 1200 | + const struct ipu6_isys_pixelformat *pfmt = | |
| 1201 | + ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); | |
| 1202 | + struct device *dev = &av->isys->adev->auxdev.dev; | |
| 1203 | + struct v4l2_mbus_frame_desc_entry entry; | |
| 1204 | + struct v4l2_subdev_route *route = NULL; | |
| 1205 | + struct v4l2_subdev_route *r; | |
| 1206 | + struct v4l2_subdev_state *state; | |
| 1207 | + struct ipu6_isys_subdev *asd; | |
| 1208 | + struct v4l2_subdev *remote_sd; | |
| 1209 | + struct media_pipeline *pipeline; | |
| 1210 | + struct media_pad *source_pad, *remote_pad; | |
| 1211 | + int ret = -EINVAL; | |
| 1212 | + | |
| 1213 | + *nr_queues = 0; | |
| 1214 | + | |
| 1215 | + remote_pad = media_pad_remote_pad_unique(&av->pad); | |
| 1216 | + if (IS_ERR(remote_pad)) { | |
| 1217 | + dev_dbg(dev, "failed to get remote pad\n"); | |
| 1218 | + return PTR_ERR(remote_pad); | |
| 1219 | + } | |
| 1220 | + | |
| 1221 | + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); | |
| 1222 | + asd = to_ipu6_isys_subdev(remote_sd); | |
| 1223 | + source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]); | |
| 1224 | + if (!source_pad) { | |
| 1225 | + dev_dbg(dev, "No external source entity\n"); | |
| 1226 | + return -ENODEV; | |
| 1227 | + } | |
| 1228 | + | |
| 1229 | + *source_entity = source_pad->entity; | |
| 1230 | + | |
| 1231 | + /* Find the root */ | |
| 1232 | + state = v4l2_subdev_lock_and_get_active_state(remote_sd); | |
| 1233 | + for_each_active_route(&state->routing, r) { | |
| 1234 | + (*nr_queues)++; | |
| 1235 | + | |
| 1236 | + if (r->source_pad == remote_pad->index) | |
| 1237 | + route = r; | |
| 1238 | + } | |
| 1239 | + | |
| 1240 | + if (!route) { | |
| 1241 | + v4l2_subdev_unlock_state(state); | |
| 1242 | + dev_dbg(dev, "Failed to find route\n"); | |
| 1243 | + return -ENODEV; | |
| 1244 | + } | |
| 1245 | + av->source_stream = route->sink_stream; | |
| 1246 | + v4l2_subdev_unlock_state(state); | |
| 1247 | + | |
| 1248 | + ret = ipu6_isys_csi2_get_remote_desc(av->source_stream, | |
| 1249 | + to_ipu6_isys_csi2(asd), | |
| 1250 | + *source_entity, &entry); | |
| 1251 | + if (ret == -ENOIOCTLCMD) { | |
| 1252 | + av->vc = 0; | |
| 1253 | + av->dt = ipu6_isys_mbus_code_to_mipi(pfmt->code); | |
| 1254 | + } else if (!ret) { | |
| 1255 | + dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n", | |
| 1256 | + entry.stream, entry.length, entry.bus.csi2.vc, | |
| 1257 | + entry.bus.csi2.dt); | |
| 1258 | + | |
| 1259 | + av->vc = entry.bus.csi2.vc; | |
| 1260 | + av->dt = entry.bus.csi2.dt; | |
| 1261 | + } else { | |
| 1262 | + dev_err(dev, "failed to get remote frame desc\n"); | |
| 1263 | + return ret; | |
| 1264 | + } | |
| 1265 | + | |
| 1266 | + pipeline = media_entity_pipeline(&av->vdev.entity); | |
| 1267 | + if (!pipeline) | |
| 1268 | + ret = video_device_pipeline_alloc_start(&av->vdev); | |
| 1269 | + else | |
| 1270 | + ret = video_device_pipeline_start(&av->vdev, pipeline); | |
| 1271 | + if (ret < 0) { | |
| 1272 | + dev_dbg(dev, "media pipeline start failed\n"); | |
| 1273 | + return ret; | |
| 1274 | + } | |
| 1275 | + | |
| 1276 | + av->stream = ipu6_isys_get_stream(av, asd); | |
| 1277 | + if (!av->stream) { | |
| 1278 | + video_device_pipeline_stop(&av->vdev); | |
| 1279 | + dev_err(dev, "no available stream for firmware\n"); | |
| 1280 | + return -EINVAL; | |
| 1281 | + } | |
| 1282 | + | |
| 1283 | + return 0; | |
| 1284 | +} | |
| 1285 | + | |
| 1286 | +/* | |
| 1287 | + * Do everything that's needed to initialise things related to video | |
| 1288 | + * buffer queue, video node, and the related media entity. The caller | |
| 1289 | + * is expected to assign isys field and set the name of the video | |
| 1290 | + * device. | |
| 1291 | + */ | |
| 1292 | +int ipu6_isys_video_init(struct ipu6_isys_video *av) | |
| 1293 | +{ | |
| 1294 | + struct v4l2_format format = { | |
| 1295 | + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, | |
| 1296 | + .fmt.pix = { | |
| 1297 | + .width = 1920, | |
| 1298 | + .height = 1080, | |
| 1299 | + }, | |
| 1300 | + }; | |
| 1301 | + struct v4l2_format format_meta = { | |
| 1302 | + .type = V4L2_BUF_TYPE_META_CAPTURE, | |
| 1303 | + .fmt.meta = { | |
| 1304 | + .width = 1920, | |
| 1305 | + .height = 4, | |
| 1306 | + }, | |
| 1307 | + }; | |
| 1308 | + int ret; | |
| 1309 | + | |
| 1310 | + mutex_init(&av->mutex); | |
| 1311 | + av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC | | |
| 1312 | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE; | |
| 1313 | + av->vdev.vfl_dir = VFL_DIR_RX; | |
| 1314 | + | |
| 1315 | + ret = ipu6_isys_queue_init(&av->aq); | |
| 1316 | + if (ret) | |
| 1317 | + goto out_free_watermark; | |
| 1318 | + | |
| 1319 | + av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; | |
| 1320 | + ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad); | |
| 1321 | + if (ret) | |
| 1322 | + goto out_vb2_queue_release; | |
| 1323 | + | |
| 1324 | + av->vdev.entity.ops = &entity_ops; | |
| 1325 | + av->vdev.release = video_device_release_empty; | |
| 1326 | + av->vdev.fops = &isys_fops; | |
| 1327 | + av->vdev.v4l2_dev = &av->isys->v4l2_dev; | |
| 1328 | + if (!av->vdev.ioctl_ops) | |
| 1329 | + av->vdev.ioctl_ops = &ipu6_v4l2_ioctl_ops; | |
| 1330 | + av->vdev.queue = &av->aq.vbq; | |
| 1331 | + av->vdev.lock = &av->mutex; | |
| 1332 | + | |
| 1333 | + __ipu6_isys_vidioc_try_fmt_vid_cap(av, &format); | |
| 1334 | + av->pix_fmt = format.fmt.pix; | |
| 1335 | + __ipu6_isys_vidioc_try_fmt_meta_cap(av, &format_meta); | |
| 1336 | + av->meta_fmt = format_meta.fmt.meta; | |
| 1337 | + | |
| 1338 | + set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags); | |
| 1339 | + video_set_drvdata(&av->vdev, av); | |
| 1340 | + | |
| 1341 | + ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1); | |
| 1342 | + if (ret) | |
| 1343 | + goto out_media_entity_cleanup; | |
| 1344 | + | |
| 1345 | + return ret; | |
| 1346 | + | |
| 1347 | +out_media_entity_cleanup: | |
| 1348 | + vb2_video_unregister_device(&av->vdev); | |
| 1349 | + media_entity_cleanup(&av->vdev.entity); | |
| 1350 | + | |
| 1351 | +out_vb2_queue_release: | |
| 1352 | + vb2_queue_release(&av->aq.vbq); | |
| 1353 | + | |
| 1354 | +out_free_watermark: | |
| 1355 | + mutex_destroy(&av->mutex); | |
| 1356 | + | |
| 1357 | + return ret; | |
| 1358 | +} | |
| 1359 | + | |
| 1360 | +void ipu6_isys_video_cleanup(struct ipu6_isys_video *av) | |
| 1361 | +{ | |
| 1362 | + vb2_video_unregister_device(&av->vdev); | |
| 1363 | + media_entity_cleanup(&av->vdev.entity); | |
| 1364 | + mutex_destroy(&av->mutex); | |
| 1365 | +} | |
| 1366 | + | |
| 1367 | +u32 ipu6_isys_get_format(struct ipu6_isys_video *av) | |
| 1368 | +{ | |
| 1369 | + if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
| 1370 | + return av->pix_fmt.pixelformat; | |
| 1371 | + | |
| 1372 | + if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE) | |
| 1373 | + return av->meta_fmt.dataformat; | |
| 1374 | + | |
| 1375 | + return 0; | |
| 1376 | +} | |
| 1377 | + | |
| 1378 | +u32 ipu6_isys_get_data_size(struct ipu6_isys_video *av) | |
| 1379 | +{ | |
| 1380 | + if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
| 1381 | + return av->pix_fmt.sizeimage; | |
| 1382 | + | |
| 1383 | + if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE) | |
| 1384 | + return av->meta_fmt.buffersize; | |
| 1385 | + | |
| 1386 | + return 0; | |
| 1387 | +} | |
| 1388 | + | |
| 1389 | +u32 ipu6_isys_get_bytes_per_line(struct ipu6_isys_video *av) | |
| 1390 | +{ | |
| 1391 | + if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
| 1392 | + return av->pix_fmt.bytesperline; | |
| 1393 | + | |
| 1394 | + if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE) | |
| 1395 | + return av->meta_fmt.bytesperline; | |
| 1396 | + | |
| 1397 | + return 0; | |
| 1398 | +} | |
| 1399 | + | |
| 1400 | +u32 ipu6_isys_get_frame_width(struct ipu6_isys_video *av) | |
| 1401 | +{ | |
| 1402 | + if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
| 1403 | + return av->pix_fmt.width; | |
| 1404 | + | |
| 1405 | + if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE) | |
| 1406 | + return av->meta_fmt.width; | |
| 1407 | + | |
| 1408 | + return 0; | |
| 1409 | +} | |
| 1410 | + | |
| 1411 | +u32 ipu6_isys_get_frame_height(struct ipu6_isys_video *av) | |
| 1412 | +{ | |
| 1413 | + if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
| 1414 | + return av->pix_fmt.height; | |
| 1415 | + | |
| 1416 | + if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE) | |
| 1417 | + return av->meta_fmt.height; | |
| 1418 | + | |
| 1419 | + return 0; | |
| 1420 | +} | |
| @@ -0,0 +1,141 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2013--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_ISYS_VIDEO_H | |
| 5 | +#define IPU6_ISYS_VIDEO_H | |
| 6 | + | |
| 7 | +#include <linux/atomic.h> | |
| 8 | +#include <linux/completion.h> | |
| 9 | +#include <linux/container_of.h> | |
| 10 | +#include <linux/list.h> | |
| 11 | +#include <linux/mutex.h> | |
| 12 | + | |
| 13 | +#include <media/media-entity.h> | |
| 14 | +#include <media/v4l2-dev.h> | |
| 15 | + | |
| 16 | +#include "ipu6-isys-queue.h" | |
| 17 | + | |
| 18 | +#define IPU6_ISYS_OUTPUT_PINS 11 | |
| 19 | +#define IPU6_ISYS_MAX_PARALLEL_SOF 2 | |
| 20 | + | |
| 21 | +struct file; | |
| 22 | +struct ipu6_isys; | |
| 23 | +struct ipu6_isys_csi2; | |
| 24 | +struct ipu6_isys_subdev; | |
| 25 | + | |
| 26 | +struct ipu6_isys_pixelformat { | |
| 27 | + u32 pixelformat; | |
| 28 | + u32 bpp; | |
| 29 | + u32 bpp_packed; | |
| 30 | + u32 code; | |
| 31 | + u32 css_pixelformat; | |
| 32 | + bool is_meta; | |
| 33 | +}; | |
| 34 | + | |
| 35 | +struct sequence_info { | |
| 36 | + unsigned int sequence; | |
| 37 | + u64 timestamp; | |
| 38 | +}; | |
| 39 | + | |
| 40 | +struct output_pin_data { | |
| 41 | + void (*pin_ready)(struct ipu6_isys_stream *stream, | |
| 42 | + struct ipu6_fw_isys_resp_info_abi *info); | |
| 43 | + struct ipu6_isys_queue *aq; | |
| 44 | +}; | |
| 45 | + | |
| 46 | +/* | |
| 47 | + * Align with firmware stream. Each stream represents a CSI virtual channel. | |
| 48 | + * May map to multiple video devices | |
| 49 | + */ | |
| 50 | +struct ipu6_isys_stream { | |
| 51 | + struct mutex mutex; | |
| 52 | + struct media_entity *source_entity; | |
| 53 | + atomic_t sequence; | |
| 54 | + unsigned int seq_index; | |
| 55 | + struct sequence_info seq[IPU6_ISYS_MAX_PARALLEL_SOF]; | |
| 56 | + int stream_source; | |
| 57 | + int stream_handle; | |
| 58 | + unsigned int nr_output_pins; | |
| 59 | + struct ipu6_isys_subdev *asd; | |
| 60 | + | |
| 61 | + int nr_queues; /* Number of capture queues */ | |
| 62 | + int nr_streaming; | |
| 63 | + int streaming; /* Has streaming been really started? */ | |
| 64 | + struct list_head queues; | |
| 65 | + struct completion stream_open_completion; | |
| 66 | + struct completion stream_close_completion; | |
| 67 | + struct completion stream_start_completion; | |
| 68 | + struct completion stream_stop_completion; | |
| 69 | + struct ipu6_isys *isys; | |
| 70 | + | |
| 71 | + struct output_pin_data output_pins[IPU6_ISYS_OUTPUT_PINS]; | |
| 72 | + int error; | |
| 73 | + u8 vc; | |
| 74 | +}; | |
| 75 | + | |
| 76 | +struct video_stream_watermark { | |
| 77 | + u32 width; | |
| 78 | + u32 height; | |
| 79 | + u32 hblank; | |
| 80 | + u32 frame_rate; | |
| 81 | + u64 pixel_rate; | |
| 82 | + u64 stream_data_rate; | |
| 83 | + u16 sram_gran_shift; | |
| 84 | + u16 sram_gran_size; | |
| 85 | + struct list_head stream_node; | |
| 86 | +}; | |
| 87 | + | |
| 88 | +struct ipu6_isys_video { | |
| 89 | + struct ipu6_isys_queue aq; | |
| 90 | + /* Serialise access to other fields in the struct. */ | |
| 91 | + struct mutex mutex; | |
| 92 | + struct media_pad pad; | |
| 93 | + struct video_device vdev; | |
| 94 | + struct v4l2_pix_format pix_fmt; | |
| 95 | + struct v4l2_meta_format meta_fmt; | |
| 96 | + struct ipu6_isys *isys; | |
| 97 | + struct ipu6_isys_csi2 *csi2; | |
| 98 | + struct ipu6_isys_stream *stream; | |
| 99 | + unsigned int streaming; | |
| 100 | + struct video_stream_watermark watermark; | |
| 101 | + u32 source_stream; | |
| 102 | + u8 vc; | |
| 103 | + u8 dt; | |
| 104 | +}; | |
| 105 | + | |
| 106 | +#define ipu6_isys_queue_to_video(__aq) \ | |
| 107 | + container_of(__aq, struct ipu6_isys_video, aq) | |
| 108 | + | |
| 109 | +extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts[]; | |
| 110 | +extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts_packed[]; | |
| 111 | + | |
| 112 | +const struct ipu6_isys_pixelformat * | |
| 113 | +ipu6_isys_get_isys_format(u32 pixelformat, u32 code); | |
| 114 | +int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av, | |
| 115 | + struct media_entity *source_entity, | |
| 116 | + int nr_queues); | |
| 117 | +int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, | |
| 118 | + struct ipu6_isys_buffer_list *bl); | |
| 119 | +int ipu6_isys_fw_open(struct ipu6_isys *isys); | |
| 120 | +void ipu6_isys_fw_close(struct ipu6_isys *isys); | |
| 121 | +int ipu6_isys_setup_video(struct ipu6_isys_video *av, | |
| 122 | + struct media_entity **source_entity, int *nr_queues); | |
| 123 | +int ipu6_isys_video_init(struct ipu6_isys_video *av); | |
| 124 | +void ipu6_isys_video_cleanup(struct ipu6_isys_video *av); | |
| 125 | +void ipu6_isys_put_stream(struct ipu6_isys_stream *stream); | |
| 126 | +struct ipu6_isys_stream * | |
| 127 | +ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle); | |
| 128 | +struct ipu6_isys_stream * | |
| 129 | +ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc); | |
| 130 | + | |
| 131 | +void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, | |
| 132 | + bool state); | |
| 133 | +void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state); | |
| 134 | + | |
| 135 | +u32 ipu6_isys_get_format(struct ipu6_isys_video *av); | |
| 136 | +u32 ipu6_isys_get_data_size(struct ipu6_isys_video *av); | |
| 137 | +u32 ipu6_isys_get_bytes_per_line(struct ipu6_isys_video *av); | |
| 138 | +u32 ipu6_isys_get_frame_width(struct ipu6_isys_video *av); | |
| 139 | +u32 ipu6_isys_get_frame_height(struct ipu6_isys_video *av); | |
| 140 | + | |
| 141 | +#endif /* IPU6_ISYS_VIDEO_H */ | |
| @@ -0,0 +1,1367 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/auxiliary_bus.h> | |
| 7 | +#include <linux/bitfield.h> | |
| 8 | +#include <linux/bits.h> | |
| 9 | +#include <linux/completion.h> | |
| 10 | +#include <linux/container_of.h> | |
| 11 | +#include <linux/delay.h> | |
| 12 | +#include <linux/device.h> | |
| 13 | +#include <linux/dma-mapping.h> | |
| 14 | +#include <linux/err.h> | |
| 15 | +#include <linux/firmware.h> | |
| 16 | +#include <linux/io.h> | |
| 17 | +#include <linux/irqreturn.h> | |
| 18 | +#include <linux/list.h> | |
| 19 | +#include <linux/module.h> | |
| 20 | +#include <linux/mutex.h> | |
| 21 | +#include <linux/pci.h> | |
| 22 | +#include <linux/pm_runtime.h> | |
| 23 | +#include <linux/pm_qos.h> | |
| 24 | +#include <linux/slab.h> | |
| 25 | +#include <linux/spinlock.h> | |
| 26 | +#include <linux/string.h> | |
| 27 | + | |
| 28 | +#include <media/ipu-bridge.h> | |
| 29 | +#include <media/media-device.h> | |
| 30 | +#include <media/media-entity.h> | |
| 31 | +#include <media/v4l2-async.h> | |
| 32 | +#include <media/v4l2-device.h> | |
| 33 | +#include <media/v4l2-fwnode.h> | |
| 34 | + | |
| 35 | +#include "ipu6-bus.h" | |
| 36 | +#include "ipu6-cpd.h" | |
| 37 | +#include "ipu6-isys.h" | |
| 38 | +#include "ipu6-isys-csi2.h" | |
| 39 | +#include "ipu6-mmu.h" | |
| 40 | +#include "ipu6-platform-buttress-regs.h" | |
| 41 | +#include "ipu6-platform-isys-csi2-reg.h" | |
| 42 | +#include "ipu6-platform-regs.h" | |
| 43 | + | |
| 44 | +#define IPU6_BUTTRESS_FABIC_CONTROL 0x68 | |
| 45 | +#define GDA_ENABLE_IWAKE_INDEX 2 | |
| 46 | +#define GDA_IWAKE_THRESHOLD_INDEX 1 | |
| 47 | +#define GDA_IRQ_CRITICAL_THRESHOLD_INDEX 0 | |
| 48 | +#define GDA_MEMOPEN_THRESHOLD_INDEX 3 | |
| 49 | +#define DEFAULT_DID_RATIO 90 | |
| 50 | +#define DEFAULT_IWAKE_THRESHOLD 0x42 | |
| 51 | +#define DEFAULT_MEM_OPEN_TIME 10 | |
| 52 | +#define ONE_THOUSAND_MICROSECOND 1000 | |
| 53 | +/* One page is 2KB, 8 x 16 x 16 = 2048B = 2KB */ | |
| 54 | +#define ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE 0x800 | |
| 55 | + | |
| 56 | +/* LTR & DID value are 10 bit at most */ | |
| 57 | +#define LTR_DID_VAL_MAX 1023 | |
| 58 | +#define LTR_DEFAULT_VALUE 0x70503c19 | |
| 59 | +#define FILL_TIME_DEFAULT_VALUE 0xfff0783c | |
| 60 | +#define LTR_DID_PKGC_2R 20 | |
| 61 | +#define LTR_SCALE_DEFAULT 5 | |
| 62 | +#define LTR_SCALE_1024NS 2 | |
| 63 | +#define DID_SCALE_1US 2 | |
| 64 | +#define DID_SCALE_32US 3 | |
| 65 | +#define REG_PKGC_PMON_CFG 0xb00 | |
| 66 | + | |
| 67 | +#define VAL_PKGC_PMON_CFG_RESET 0x38 | |
| 68 | +#define VAL_PKGC_PMON_CFG_START 0x7 | |
| 69 | + | |
| 70 | +#define IS_PIXEL_BUFFER_PAGES 0x80 | |
| 71 | +/* | |
| 72 | + * when iwake mode is disabled, the critical threshold is statically set | |
| 73 | + * to 75% of the IS pixel buffer, criticalThreshold = (128 * 3) / 4 | |
| 74 | + */ | |
| 75 | +#define CRITICAL_THRESHOLD_IWAKE_DISABLE (IS_PIXEL_BUFFER_PAGES * 3 / 4) | |
| 76 | + | |
| 77 | +union fabric_ctrl { | |
| 78 | + struct { | |
| 79 | + u16 ltr_val : 10; | |
| 80 | + u16 ltr_scale : 3; | |
| 81 | + u16 reserved : 3; | |
| 82 | + u16 did_val : 10; | |
| 83 | + u16 did_scale : 3; | |
| 84 | + u16 reserved2 : 1; | |
| 85 | + u16 keep_power_in_D0 : 1; | |
| 86 | + u16 keep_power_override : 1; | |
| 87 | + } bits; | |
| 88 | + u32 value; | |
| 89 | +}; | |
| 90 | + | |
| 91 | +enum ltr_did_type { | |
| 92 | + LTR_IWAKE_ON, | |
| 93 | + LTR_IWAKE_OFF, | |
| 94 | + LTR_ISYS_ON, | |
| 95 | + LTR_ISYS_OFF, | |
| 96 | + LTR_ENHANNCE_IWAKE, | |
| 97 | + LTR_TYPE_MAX | |
| 98 | +}; | |
| 99 | + | |
| 100 | +#define ISYS_PM_QOS_VALUE 300 | |
| 101 | + | |
| 102 | +static int isys_isr_one(struct ipu6_bus_device *adev); | |
| 103 | + | |
| 104 | +static int | |
| 105 | +isys_complete_ext_device_registration(struct ipu6_isys *isys, | |
| 106 | + struct v4l2_subdev *sd, | |
| 107 | + struct ipu6_isys_csi2_config *csi2) | |
| 108 | +{ | |
| 109 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 110 | + unsigned int i; | |
| 111 | + int ret; | |
| 112 | + | |
| 113 | + for (i = 0; i < sd->entity.num_pads; i++) { | |
| 114 | + if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) | |
| 115 | + break; | |
| 116 | + } | |
| 117 | + | |
| 118 | + if (i == sd->entity.num_pads) { | |
| 119 | + dev_warn(dev, "no src pad in external entity\n"); | |
| 120 | + ret = -ENOENT; | |
| 121 | + goto unregister_subdev; | |
| 122 | + } | |
| 123 | + | |
| 124 | + ret = media_create_pad_link(&sd->entity, i, | |
| 125 | + &isys->csi2[csi2->port].asd.sd.entity, | |
| 126 | + 0, MEDIA_LNK_FL_ENABLED | | |
| 127 | + MEDIA_LNK_FL_IMMUTABLE); | |
| 128 | + if (ret) { | |
| 129 | + dev_warn(dev, "can't create link\n"); | |
| 130 | + goto unregister_subdev; | |
| 131 | + } | |
| 132 | + | |
| 133 | + isys->csi2[csi2->port].nlanes = csi2->nlanes; | |
| 134 | + | |
| 135 | + return 0; | |
| 136 | + | |
| 137 | +unregister_subdev: | |
| 138 | + v4l2_device_unregister_subdev(sd); | |
| 139 | + | |
| 140 | + return ret; | |
| 141 | +} | |
| 142 | + | |
| 143 | +static void isys_stream_init(struct ipu6_isys *isys) | |
| 144 | +{ | |
| 145 | + u32 i; | |
| 146 | + | |
| 147 | + for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { | |
| 148 | + mutex_init(&isys->streams[i].mutex); | |
| 149 | + init_completion(&isys->streams[i].stream_open_completion); | |
| 150 | + init_completion(&isys->streams[i].stream_close_completion); | |
| 151 | + init_completion(&isys->streams[i].stream_start_completion); | |
| 152 | + init_completion(&isys->streams[i].stream_stop_completion); | |
| 153 | + INIT_LIST_HEAD(&isys->streams[i].queues); | |
| 154 | + isys->streams[i].isys = isys; | |
| 155 | + isys->streams[i].stream_handle = i; | |
| 156 | + isys->streams[i].vc = INVALID_VC_ID; | |
| 157 | + } | |
| 158 | +} | |
| 159 | + | |
| 160 | +static void isys_csi2_unregister_subdevices(struct ipu6_isys *isys) | |
| 161 | +{ | |
| 162 | + const struct ipu6_isys_internal_csi2_pdata *csi2 = | |
| 163 | + &isys->pdata->ipdata->csi2; | |
| 164 | + unsigned int i; | |
| 165 | + | |
| 166 | + for (i = 0; i < csi2->nports; i++) | |
| 167 | + ipu6_isys_csi2_cleanup(&isys->csi2[i]); | |
| 168 | +} | |
| 169 | + | |
| 170 | +static int isys_csi2_register_subdevices(struct ipu6_isys *isys) | |
| 171 | +{ | |
| 172 | + const struct ipu6_isys_internal_csi2_pdata *csi2_pdata = | |
| 173 | + &isys->pdata->ipdata->csi2; | |
| 174 | + unsigned int i; | |
| 175 | + int ret; | |
| 176 | + | |
| 177 | + for (i = 0; i < csi2_pdata->nports; i++) { | |
| 178 | + ret = ipu6_isys_csi2_init(&isys->csi2[i], isys, | |
| 179 | + isys->pdata->base + | |
| 180 | + CSI_REG_PORT_BASE(i), i); | |
| 181 | + if (ret) | |
| 182 | + goto fail; | |
| 183 | + | |
| 184 | + isys->isr_csi2_bits |= IPU6_ISYS_UNISPART_IRQ_CSI2(i); | |
| 185 | + } | |
| 186 | + | |
| 187 | + return 0; | |
| 188 | + | |
| 189 | +fail: | |
| 190 | + while (i--) | |
| 191 | + ipu6_isys_csi2_cleanup(&isys->csi2[i]); | |
| 192 | + | |
| 193 | + return ret; | |
| 194 | +} | |
| 195 | + | |
| 196 | +static int isys_csi2_create_media_links(struct ipu6_isys *isys) | |
| 197 | +{ | |
| 198 | + const struct ipu6_isys_internal_csi2_pdata *csi2_pdata = | |
| 199 | + &isys->pdata->ipdata->csi2; | |
| 200 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 201 | + unsigned int i, j; | |
| 202 | + int ret; | |
| 203 | + | |
| 204 | + for (i = 0; i < csi2_pdata->nports; i++) { | |
| 205 | + struct media_entity *sd = &isys->csi2[i].asd.sd.entity; | |
| 206 | + | |
| 207 | + for (j = 0; j < NR_OF_CSI2_SRC_PADS; j++) { | |
| 208 | + struct ipu6_isys_video *av = &isys->csi2[i].av[j]; | |
| 209 | + | |
| 210 | + ret = media_create_pad_link(sd, CSI2_PAD_SRC + j, | |
| 211 | + &av->vdev.entity, 0, 0); | |
| 212 | + if (ret) { | |
| 213 | + dev_err(dev, "CSI2 can't create link\n"); | |
| 214 | + return ret; | |
| 215 | + } | |
| 216 | + | |
| 217 | + av->csi2 = &isys->csi2[i]; | |
| 218 | + } | |
| 219 | + } | |
| 220 | + | |
| 221 | + return 0; | |
| 222 | +} | |
| 223 | + | |
| 224 | +static void isys_unregister_video_devices(struct ipu6_isys *isys) | |
| 225 | +{ | |
| 226 | + const struct ipu6_isys_internal_csi2_pdata *csi2_pdata = | |
| 227 | + &isys->pdata->ipdata->csi2; | |
| 228 | + unsigned int i, j; | |
| 229 | + | |
| 230 | + for (i = 0; i < csi2_pdata->nports; i++) | |
| 231 | + for (j = 0; j < NR_OF_CSI2_SRC_PADS; j++) | |
| 232 | + ipu6_isys_video_cleanup(&isys->csi2[i].av[j]); | |
| 233 | +} | |
| 234 | + | |
| 235 | +static int isys_register_video_devices(struct ipu6_isys *isys) | |
| 236 | +{ | |
| 237 | + const struct ipu6_isys_internal_csi2_pdata *csi2_pdata = | |
| 238 | + &isys->pdata->ipdata->csi2; | |
| 239 | + unsigned int i, j; | |
| 240 | + int ret; | |
| 241 | + | |
| 242 | + for (i = 0; i < csi2_pdata->nports; i++) { | |
| 243 | + for (j = 0; j < NR_OF_CSI2_SRC_PADS; j++) { | |
| 244 | + struct ipu6_isys_video *av = &isys->csi2[i].av[j]; | |
| 245 | + | |
| 246 | + snprintf(av->vdev.name, sizeof(av->vdev.name), | |
| 247 | + IPU6_ISYS_ENTITY_PREFIX " ISYS Capture %u", | |
| 248 | + i * NR_OF_CSI2_SRC_PADS + j); | |
| 249 | + av->isys = isys; | |
| 250 | + av->aq.vbq.buf_struct_size = | |
| 251 | + sizeof(struct ipu6_isys_video_buffer); | |
| 252 | + | |
| 253 | + ret = ipu6_isys_video_init(av); | |
| 254 | + if (ret) | |
| 255 | + goto fail; | |
| 256 | + } | |
| 257 | + } | |
| 258 | + | |
| 259 | + return 0; | |
| 260 | + | |
| 261 | +fail: | |
| 262 | + while (i--) { | |
| 263 | + while (j--) | |
| 264 | + ipu6_isys_video_cleanup(&isys->csi2[i].av[j]); | |
| 265 | + j = NR_OF_CSI2_SRC_PADS; | |
| 266 | + } | |
| 267 | + | |
| 268 | + return ret; | |
| 269 | +} | |
| 270 | + | |
| 271 | +void isys_setup_hw(struct ipu6_isys *isys) | |
| 272 | +{ | |
| 273 | + void __iomem *base = isys->pdata->base; | |
| 274 | + const u8 *thd = isys->pdata->ipdata->hw_variant.cdc_fifo_threshold; | |
| 275 | + u32 irqs = 0; | |
| 276 | + unsigned int i, nports; | |
| 277 | + | |
| 278 | + nports = isys->pdata->ipdata->csi2.nports; | |
| 279 | + | |
| 280 | + /* Enable irqs for all MIPI ports */ | |
| 281 | + for (i = 0; i < nports; i++) | |
| 282 | + irqs |= IPU6_ISYS_UNISPART_IRQ_CSI2(i); | |
| 283 | + | |
| 284 | + writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_edge); | |
| 285 | + writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_lnp); | |
| 286 | + writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_mask); | |
| 287 | + writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_enable); | |
| 288 | + writel(GENMASK(19, 0), | |
| 289 | + base + isys->pdata->ipdata->csi2.ctrl0_irq_clear); | |
| 290 | + | |
| 291 | + irqs = ISYS_UNISPART_IRQS; | |
| 292 | + writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_EDGE); | |
| 293 | + writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE); | |
| 294 | + writel(GENMASK(28, 0), base + IPU6_REG_ISYS_UNISPART_IRQ_CLEAR); | |
| 295 | + writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK); | |
| 296 | + writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_ENABLE); | |
| 297 | + | |
| 298 | + writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG); | |
| 299 | + writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_MUX_REG); | |
| 300 | + | |
| 301 | + /* Write CDC FIFO threshold values for isys */ | |
| 302 | + for (i = 0; i < isys->pdata->ipdata->hw_variant.cdc_fifos; i++) | |
| 303 | + writel(thd[i], base + IPU6_REG_ISYS_CDC_THRESHOLD(i)); | |
| 304 | +} | |
| 305 | + | |
| 306 | +static void ipu6_isys_csi2_isr(struct ipu6_isys_csi2 *csi2) | |
| 307 | +{ | |
| 308 | + struct ipu6_isys_stream *stream; | |
| 309 | + unsigned int i; | |
| 310 | + u32 status; | |
| 311 | + int source; | |
| 312 | + | |
| 313 | + ipu6_isys_register_errors(csi2); | |
| 314 | + | |
| 315 | + status = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + | |
| 316 | + CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET); | |
| 317 | + | |
| 318 | + writel(status, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + | |
| 319 | + CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); | |
| 320 | + | |
| 321 | + source = csi2->asd.source; | |
| 322 | + for (i = 0; i < NR_OF_CSI2_VC; i++) { | |
| 323 | + if (status & IPU_CSI_RX_IRQ_FS_VC(i)) { | |
| 324 | + stream = ipu6_isys_query_stream_by_source(csi2->isys, | |
| 325 | + source, i); | |
| 326 | + if (stream) { | |
| 327 | + ipu6_isys_csi2_sof_event_by_stream(stream); | |
| 328 | + ipu6_isys_put_stream(stream); | |
| 329 | + } | |
| 330 | + } | |
| 331 | + | |
| 332 | + if (status & IPU_CSI_RX_IRQ_FE_VC(i)) { | |
| 333 | + stream = ipu6_isys_query_stream_by_source(csi2->isys, | |
| 334 | + source, i); | |
| 335 | + if (stream) { | |
| 336 | + ipu6_isys_csi2_eof_event_by_stream(stream); | |
| 337 | + ipu6_isys_put_stream(stream); | |
| 338 | + } | |
| 339 | + } | |
| 340 | + } | |
| 341 | +} | |
| 342 | + | |
| 343 | +irqreturn_t isys_isr(struct ipu6_bus_device *adev) | |
| 344 | +{ | |
| 345 | + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); | |
| 346 | + void __iomem *base = isys->pdata->base; | |
| 347 | + u32 status_sw, status_csi; | |
| 348 | + u32 ctrl0_status, ctrl0_clear; | |
| 349 | + | |
| 350 | + spin_lock(&isys->power_lock); | |
| 351 | + if (!isys->power) { | |
| 352 | + spin_unlock(&isys->power_lock); | |
| 353 | + return IRQ_NONE; | |
| 354 | + } | |
| 355 | + | |
| 356 | + ctrl0_status = isys->pdata->ipdata->csi2.ctrl0_irq_status; | |
| 357 | + ctrl0_clear = isys->pdata->ipdata->csi2.ctrl0_irq_clear; | |
| 358 | + | |
| 359 | + status_csi = readl(isys->pdata->base + ctrl0_status); | |
| 360 | + status_sw = readl(isys->pdata->base + | |
| 361 | + IPU6_REG_ISYS_UNISPART_IRQ_STATUS); | |
| 362 | + | |
| 363 | + writel(ISYS_UNISPART_IRQS & ~IPU6_ISYS_UNISPART_IRQ_SW, | |
| 364 | + base + IPU6_REG_ISYS_UNISPART_IRQ_MASK); | |
| 365 | + | |
| 366 | + do { | |
| 367 | + writel(status_csi, isys->pdata->base + ctrl0_clear); | |
| 368 | + | |
| 369 | + writel(status_sw, isys->pdata->base + | |
| 370 | + IPU6_REG_ISYS_UNISPART_IRQ_CLEAR); | |
| 371 | + | |
| 372 | + if (isys->isr_csi2_bits & status_csi) { | |
| 373 | + unsigned int i; | |
| 374 | + | |
| 375 | + for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { | |
| 376 | + /* irq from not enabled port */ | |
| 377 | + if (!isys->csi2[i].base) | |
| 378 | + continue; | |
| 379 | + if (status_csi & IPU6_ISYS_UNISPART_IRQ_CSI2(i)) | |
| 380 | + ipu6_isys_csi2_isr(&isys->csi2[i]); | |
| 381 | + } | |
| 382 | + } | |
| 383 | + | |
| 384 | + writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG); | |
| 385 | + | |
| 386 | + if (!isys_isr_one(adev)) | |
| 387 | + status_sw = IPU6_ISYS_UNISPART_IRQ_SW; | |
| 388 | + else | |
| 389 | + status_sw = 0; | |
| 390 | + | |
| 391 | + status_csi = readl(isys->pdata->base + ctrl0_status); | |
| 392 | + status_sw |= readl(isys->pdata->base + | |
| 393 | + IPU6_REG_ISYS_UNISPART_IRQ_STATUS); | |
| 394 | + } while ((status_csi & isys->isr_csi2_bits) || | |
| 395 | + (status_sw & IPU6_ISYS_UNISPART_IRQ_SW)); | |
| 396 | + | |
| 397 | + writel(ISYS_UNISPART_IRQS, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK); | |
| 398 | + | |
| 399 | + spin_unlock(&isys->power_lock); | |
| 400 | + | |
| 401 | + return IRQ_HANDLED; | |
| 402 | +} | |
| 403 | + | |
| 404 | +static void get_lut_ltrdid(struct ipu6_isys *isys, struct ltr_did *pltr_did) | |
| 405 | +{ | |
| 406 | + struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; | |
| 407 | + struct ltr_did ltrdid_default; | |
| 408 | + | |
| 409 | + ltrdid_default.lut_ltr.value = LTR_DEFAULT_VALUE; | |
| 410 | + ltrdid_default.lut_fill_time.value = FILL_TIME_DEFAULT_VALUE; | |
| 411 | + | |
| 412 | + if (iwake_watermark->ltrdid.lut_ltr.value) | |
| 413 | + *pltr_did = iwake_watermark->ltrdid; | |
| 414 | + else | |
| 415 | + *pltr_did = ltrdid_default; | |
| 416 | +} | |
| 417 | + | |
| 418 | +static int set_iwake_register(struct ipu6_isys *isys, u32 index, u32 value) | |
| 419 | +{ | |
| 420 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 421 | + u32 req_id = index; | |
| 422 | + u32 offset = 0; | |
| 423 | + int ret; | |
| 424 | + | |
| 425 | + ret = ipu6_fw_isys_send_proxy_token(isys, req_id, index, offset, value); | |
| 426 | + if (ret) | |
| 427 | + dev_err(dev, "write %d failed %d", index, ret); | |
| 428 | + | |
| 429 | + return ret; | |
| 430 | +} | |
| 431 | + | |
| 432 | +/* | |
| 433 | + * When input system is powered up and before enabling any new sensor capture, | |
| 434 | + * or after disabling any sensor capture the following values need to be set: | |
| 435 | + * LTR_value = LTR(usec) from calculation; | |
| 436 | + * LTR_scale = 2; | |
| 437 | + * DID_value = DID(usec) from calculation; | |
| 438 | + * DID_scale = 2; | |
| 439 | + * | |
| 440 | + * When input system is powered down, the LTR and DID values | |
| 441 | + * must be returned to the default values: | |
| 442 | + * LTR_value = 1023; | |
| 443 | + * LTR_scale = 5; | |
| 444 | + * DID_value = 1023; | |
| 445 | + * DID_scale = 2; | |
| 446 | + */ | |
| 447 | +static void set_iwake_ltrdid(struct ipu6_isys *isys, u16 ltr, u16 did, | |
| 448 | + enum ltr_did_type use) | |
| 449 | +{ | |
| 450 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 451 | + u16 ltr_val, ltr_scale = LTR_SCALE_1024NS; | |
| 452 | + u16 did_val, did_scale = DID_SCALE_1US; | |
| 453 | + struct ipu6_device *isp = isys->adev->isp; | |
| 454 | + union fabric_ctrl fc; | |
| 455 | + | |
| 456 | + switch (use) { | |
| 457 | + case LTR_IWAKE_ON: | |
| 458 | + ltr_val = min_t(u16, ltr, (u16)LTR_DID_VAL_MAX); | |
| 459 | + did_val = min_t(u16, did, (u16)LTR_DID_VAL_MAX); | |
| 460 | + ltr_scale = (ltr == LTR_DID_VAL_MAX && | |
| 461 | + did == LTR_DID_VAL_MAX) ? | |
| 462 | + LTR_SCALE_DEFAULT : LTR_SCALE_1024NS; | |
| 463 | + break; | |
| 464 | + case LTR_ISYS_ON: | |
| 465 | + case LTR_IWAKE_OFF: | |
| 466 | + ltr_val = LTR_DID_PKGC_2R; | |
| 467 | + did_val = LTR_DID_PKGC_2R; | |
| 468 | + break; | |
| 469 | + case LTR_ISYS_OFF: | |
| 470 | + ltr_val = LTR_DID_VAL_MAX; | |
| 471 | + did_val = LTR_DID_VAL_MAX; | |
| 472 | + ltr_scale = LTR_SCALE_DEFAULT; | |
| 473 | + break; | |
| 474 | + case LTR_ENHANNCE_IWAKE: | |
| 475 | + if (ltr == LTR_DID_VAL_MAX && did == LTR_DID_VAL_MAX) { | |
| 476 | + ltr_val = LTR_DID_VAL_MAX; | |
| 477 | + did_val = LTR_DID_VAL_MAX; | |
| 478 | + ltr_scale = LTR_SCALE_DEFAULT; | |
| 479 | + } else if (did < ONE_THOUSAND_MICROSECOND) { | |
| 480 | + ltr_val = ltr; | |
| 481 | + did_val = did; | |
| 482 | + } else { | |
| 483 | + ltr_val = ltr; | |
| 484 | + /* div 90% value by 32 to account for scale change */ | |
| 485 | + did_val = did / 32; | |
| 486 | + did_scale = DID_SCALE_32US; | |
| 487 | + } | |
| 488 | + break; | |
| 489 | + default: | |
| 490 | + ltr_val = LTR_DID_VAL_MAX; | |
| 491 | + did_val = LTR_DID_VAL_MAX; | |
| 492 | + ltr_scale = LTR_SCALE_DEFAULT; | |
| 493 | + break; | |
| 494 | + } | |
| 495 | + | |
| 496 | + fc.value = readl(isp->base + IPU6_BUTTRESS_FABIC_CONTROL); | |
| 497 | + fc.bits.ltr_val = ltr_val; | |
| 498 | + fc.bits.ltr_scale = ltr_scale; | |
| 499 | + fc.bits.did_val = did_val; | |
| 500 | + fc.bits.did_scale = did_scale; | |
| 501 | + | |
| 502 | + dev_dbg(dev, "ltr: value %u scale %u, did: value %u scale %u\n", | |
| 503 | + ltr_val, ltr_scale, did_val, did_scale); | |
| 504 | + writel(fc.value, isp->base + IPU6_BUTTRESS_FABIC_CONTROL); | |
| 505 | +} | |
| 506 | + | |
| 507 | +/* | |
| 508 | + * Driver may clear register GDA_ENABLE_IWAKE before FW configures the | |
| 509 | + * stream for debug purpose. Otherwise driver should not access this register. | |
| 510 | + */ | |
| 511 | +static void enable_iwake(struct ipu6_isys *isys, bool enable) | |
| 512 | +{ | |
| 513 | + struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; | |
| 514 | + int ret; | |
| 515 | + | |
| 516 | + mutex_lock(&iwake_watermark->mutex); | |
| 517 | + | |
| 518 | + if (iwake_watermark->iwake_enabled == enable) { | |
| 519 | + mutex_unlock(&iwake_watermark->mutex); | |
| 520 | + return; | |
| 521 | + } | |
| 522 | + | |
| 523 | + ret = set_iwake_register(isys, GDA_ENABLE_IWAKE_INDEX, enable); | |
| 524 | + if (!ret) | |
| 525 | + iwake_watermark->iwake_enabled = enable; | |
| 526 | + | |
| 527 | + mutex_unlock(&iwake_watermark->mutex); | |
| 528 | +} | |
| 529 | + | |
| 530 | +void update_watermark_setting(struct ipu6_isys *isys) | |
| 531 | +{ | |
| 532 | + struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; | |
| 533 | + u32 iwake_threshold, iwake_critical_threshold, page_num; | |
| 534 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 535 | + u32 calc_fill_time_us = 0, ltr = 0, did = 0; | |
| 536 | + struct video_stream_watermark *p_watermark; | |
| 537 | + enum ltr_did_type ltr_did_type; | |
| 538 | + struct list_head *stream_node; | |
| 539 | + u64 isys_pb_datarate_mbs = 0; | |
| 540 | + u32 mem_open_threshold = 0; | |
| 541 | + struct ltr_did ltrdid; | |
| 542 | + u64 threshold_bytes; | |
| 543 | + u32 max_sram_size; | |
| 544 | + u32 shift; | |
| 545 | + | |
| 546 | + shift = isys->pdata->ipdata->sram_gran_shift; | |
| 547 | + max_sram_size = isys->pdata->ipdata->max_sram_size; | |
| 548 | + | |
| 549 | + mutex_lock(&iwake_watermark->mutex); | |
| 550 | + if (iwake_watermark->force_iwake_disable) { | |
| 551 | + set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF); | |
| 552 | + set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX, | |
| 553 | + CRITICAL_THRESHOLD_IWAKE_DISABLE); | |
| 554 | + goto unlock_exit; | |
| 555 | + } | |
| 556 | + | |
| 557 | + if (list_empty(&iwake_watermark->video_list)) { | |
| 558 | + isys_pb_datarate_mbs = 0; | |
| 559 | + } else { | |
| 560 | + list_for_each(stream_node, &iwake_watermark->video_list) { | |
| 561 | + p_watermark = list_entry(stream_node, | |
| 562 | + struct video_stream_watermark, | |
| 563 | + stream_node); | |
| 564 | + isys_pb_datarate_mbs += p_watermark->stream_data_rate; | |
| 565 | + } | |
| 566 | + } | |
| 567 | + mutex_unlock(&iwake_watermark->mutex); | |
| 568 | + | |
| 569 | + if (!isys_pb_datarate_mbs) { | |
| 570 | + enable_iwake(isys, false); | |
| 571 | + set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF); | |
| 572 | + mutex_lock(&iwake_watermark->mutex); | |
| 573 | + set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX, | |
| 574 | + CRITICAL_THRESHOLD_IWAKE_DISABLE); | |
| 575 | + goto unlock_exit; | |
| 576 | + } | |
| 577 | + | |
| 578 | + enable_iwake(isys, true); | |
| 579 | + calc_fill_time_us = max_sram_size / isys_pb_datarate_mbs; | |
| 580 | + | |
| 581 | + if (isys->pdata->ipdata->enhanced_iwake) { | |
| 582 | + ltr = isys->pdata->ipdata->ltr; | |
| 583 | + did = calc_fill_time_us * DEFAULT_DID_RATIO / 100; | |
| 584 | + ltr_did_type = LTR_ENHANNCE_IWAKE; | |
| 585 | + } else { | |
| 586 | + get_lut_ltrdid(isys, <rdid); | |
| 587 | + | |
| 588 | + if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th0) | |
| 589 | + ltr = 0; | |
| 590 | + else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th1) | |
| 591 | + ltr = ltrdid.lut_ltr.bits.val0; | |
| 592 | + else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th2) | |
| 593 | + ltr = ltrdid.lut_ltr.bits.val1; | |
| 594 | + else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th3) | |
| 595 | + ltr = ltrdid.lut_ltr.bits.val2; | |
| 596 | + else | |
| 597 | + ltr = ltrdid.lut_ltr.bits.val3; | |
| 598 | + | |
| 599 | + did = calc_fill_time_us - ltr; | |
| 600 | + ltr_did_type = LTR_IWAKE_ON; | |
| 601 | + } | |
| 602 | + | |
| 603 | + set_iwake_ltrdid(isys, ltr, did, ltr_did_type); | |
| 604 | + | |
| 605 | + /* calculate iwake threshold with 2KB granularity pages */ | |
| 606 | + threshold_bytes = did * isys_pb_datarate_mbs; | |
| 607 | + iwake_threshold = max_t(u32, 1, threshold_bytes >> shift); | |
| 608 | + iwake_threshold = min_t(u32, iwake_threshold, max_sram_size); | |
| 609 | + | |
| 610 | + mutex_lock(&iwake_watermark->mutex); | |
| 611 | + if (isys->pdata->ipdata->enhanced_iwake) { | |
| 612 | + set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX, | |
| 613 | + DEFAULT_IWAKE_THRESHOLD); | |
| 614 | + /* calculate number of pages that will be filled in 10 usec */ | |
| 615 | + page_num = (DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) / | |
| 616 | + ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE; | |
| 617 | + page_num += ((DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) % | |
| 618 | + ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE) ? 1 : 0; | |
| 619 | + mem_open_threshold = isys->pdata->ipdata->memopen_threshold; | |
| 620 | + mem_open_threshold = max_t(u32, mem_open_threshold, page_num); | |
| 621 | + dev_dbg(dev, "mem_open_threshold: %u\n", mem_open_threshold); | |
| 622 | + set_iwake_register(isys, GDA_MEMOPEN_THRESHOLD_INDEX, | |
| 623 | + mem_open_threshold); | |
| 624 | + } else { | |
| 625 | + set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX, | |
| 626 | + iwake_threshold); | |
| 627 | + } | |
| 628 | + | |
| 629 | + iwake_critical_threshold = iwake_threshold + | |
| 630 | + (IS_PIXEL_BUFFER_PAGES - iwake_threshold) / 2; | |
| 631 | + | |
| 632 | + dev_dbg(dev, "threshold: %u critical: %u\n", iwake_threshold, | |
| 633 | + iwake_critical_threshold); | |
| 634 | + | |
| 635 | + set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX, | |
| 636 | + iwake_critical_threshold); | |
| 637 | + | |
| 638 | + writel(VAL_PKGC_PMON_CFG_RESET, | |
| 639 | + isys->adev->isp->base + REG_PKGC_PMON_CFG); | |
| 640 | + writel(VAL_PKGC_PMON_CFG_START, | |
| 641 | + isys->adev->isp->base + REG_PKGC_PMON_CFG); | |
| 642 | +unlock_exit: | |
| 643 | + mutex_unlock(&iwake_watermark->mutex); | |
| 644 | +} | |
| 645 | + | |
| 646 | +static void isys_iwake_watermark_init(struct ipu6_isys *isys) | |
| 647 | +{ | |
| 648 | + struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; | |
| 649 | + | |
| 650 | + INIT_LIST_HEAD(&iwake_watermark->video_list); | |
| 651 | + mutex_init(&iwake_watermark->mutex); | |
| 652 | + | |
| 653 | + iwake_watermark->ltrdid.lut_ltr.value = 0; | |
| 654 | + iwake_watermark->isys = isys; | |
| 655 | + iwake_watermark->iwake_enabled = false; | |
| 656 | + iwake_watermark->force_iwake_disable = false; | |
| 657 | +} | |
| 658 | + | |
| 659 | +static void isys_iwake_watermark_cleanup(struct ipu6_isys *isys) | |
| 660 | +{ | |
| 661 | + struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; | |
| 662 | + | |
| 663 | + mutex_lock(&iwake_watermark->mutex); | |
| 664 | + list_del(&iwake_watermark->video_list); | |
| 665 | + mutex_unlock(&iwake_watermark->mutex); | |
| 666 | + | |
| 667 | + mutex_destroy(&iwake_watermark->mutex); | |
| 668 | +} | |
| 669 | + | |
| 670 | +/* The .bound() notifier callback when a match is found */ | |
| 671 | +static int isys_notifier_bound(struct v4l2_async_notifier *notifier, | |
| 672 | + struct v4l2_subdev *sd, | |
| 673 | + struct v4l2_async_connection *asc) | |
| 674 | +{ | |
| 675 | + struct ipu6_isys *isys = | |
| 676 | + container_of(notifier, struct ipu6_isys, notifier); | |
| 677 | + struct sensor_async_sd *s_asd = | |
| 678 | + container_of(asc, struct sensor_async_sd, asc); | |
| 679 | + int ret; | |
| 680 | + | |
| 681 | + ret = ipu_bridge_instantiate_vcm(sd->dev); | |
| 682 | + if (ret) { | |
| 683 | + dev_err(&isys->adev->auxdev.dev, "instantiate vcm failed\n"); | |
| 684 | + return ret; | |
| 685 | + } | |
| 686 | + | |
| 687 | + dev_dbg(&isys->adev->auxdev.dev, "bind %s nlanes is %d port is %d\n", | |
| 688 | + sd->name, s_asd->csi2.nlanes, s_asd->csi2.port); | |
| 689 | + ret = isys_complete_ext_device_registration(isys, sd, &s_asd->csi2); | |
| 690 | + if (ret) | |
| 691 | + return ret; | |
| 692 | + | |
| 693 | + return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); | |
| 694 | +} | |
| 695 | + | |
| 696 | +static int isys_notifier_complete(struct v4l2_async_notifier *notifier) | |
| 697 | +{ | |
| 698 | + struct ipu6_isys *isys = | |
| 699 | + container_of(notifier, struct ipu6_isys, notifier); | |
| 700 | + | |
| 701 | + return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); | |
| 702 | +} | |
| 703 | + | |
| 704 | +static const struct v4l2_async_notifier_operations isys_async_ops = { | |
| 705 | + .bound = isys_notifier_bound, | |
| 706 | + .complete = isys_notifier_complete, | |
| 707 | +}; | |
| 708 | + | |
| 709 | +#define ISYS_MAX_PORTS 8 | |
| 710 | +static int isys_notifier_init(struct ipu6_isys *isys) | |
| 711 | +{ | |
| 712 | + struct ipu6_device *isp = isys->adev->isp; | |
| 713 | + struct device *dev = &isp->pdev->dev; | |
| 714 | + unsigned int i; | |
| 715 | + int ret; | |
| 716 | + | |
| 717 | + v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev); | |
| 718 | + | |
| 719 | + for (i = 0; i < ISYS_MAX_PORTS; i++) { | |
| 720 | + struct v4l2_fwnode_endpoint vep = { | |
| 721 | + .bus_type = V4L2_MBUS_CSI2_DPHY | |
| 722 | + }; | |
| 723 | + struct sensor_async_sd *s_asd; | |
| 724 | + struct fwnode_handle *ep; | |
| 725 | + | |
| 726 | + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0, | |
| 727 | + FWNODE_GRAPH_ENDPOINT_NEXT); | |
| 728 | + if (!ep) | |
| 729 | + continue; | |
| 730 | + | |
| 731 | + ret = v4l2_fwnode_endpoint_parse(ep, &vep); | |
| 732 | + if (ret) { | |
| 733 | + dev_err(dev, "fwnode endpoint parse failed: %d\n", ret); | |
| 734 | + goto err_parse; | |
| 735 | + } | |
| 736 | + | |
| 737 | + s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep, | |
| 738 | + struct sensor_async_sd); | |
| 739 | + if (IS_ERR(s_asd)) { | |
| 740 | + ret = PTR_ERR(s_asd); | |
| 741 | + dev_err(dev, "add remove fwnode failed: %d\n", ret); | |
| 742 | + goto err_parse; | |
| 743 | + } | |
| 744 | + | |
| 745 | + s_asd->csi2.port = vep.base.port; | |
| 746 | + s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes; | |
| 747 | + | |
| 748 | + dev_dbg(dev, "remote endpoint port %d with %d lanes added\n", | |
| 749 | + s_asd->csi2.port, s_asd->csi2.nlanes); | |
| 750 | + | |
| 751 | + fwnode_handle_put(ep); | |
| 752 | + | |
| 753 | + continue; | |
| 754 | + | |
| 755 | +err_parse: | |
| 756 | + fwnode_handle_put(ep); | |
| 757 | + return ret; | |
| 758 | + } | |
| 759 | + | |
| 760 | + isys->notifier.ops = &isys_async_ops; | |
| 761 | + ret = v4l2_async_nf_register(&isys->notifier); | |
| 762 | + if (ret) { | |
| 763 | + dev_err(dev, "failed to register async notifier : %d\n", ret); | |
| 764 | + v4l2_async_nf_cleanup(&isys->notifier); | |
| 765 | + } | |
| 766 | + | |
| 767 | + return ret; | |
| 768 | +} | |
| 769 | + | |
| 770 | +static void isys_notifier_cleanup(struct ipu6_isys *isys) | |
| 771 | +{ | |
| 772 | + v4l2_async_nf_unregister(&isys->notifier); | |
| 773 | + v4l2_async_nf_cleanup(&isys->notifier); | |
| 774 | +} | |
| 775 | + | |
| 776 | +static int isys_register_devices(struct ipu6_isys *isys) | |
| 777 | +{ | |
| 778 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 779 | + struct pci_dev *pdev = isys->adev->isp->pdev; | |
| 780 | + int ret; | |
| 781 | + | |
| 782 | + isys->media_dev.dev = dev; | |
| 783 | + media_device_pci_init(&isys->media_dev, | |
| 784 | + pdev, IPU6_MEDIA_DEV_MODEL_NAME); | |
| 785 | + | |
| 786 | + strscpy(isys->v4l2_dev.name, isys->media_dev.model, | |
| 787 | + sizeof(isys->v4l2_dev.name)); | |
| 788 | + | |
| 789 | + ret = media_device_register(&isys->media_dev); | |
| 790 | + if (ret < 0) | |
| 791 | + goto out_media_device_unregister; | |
| 792 | + | |
| 793 | + isys->v4l2_dev.mdev = &isys->media_dev; | |
| 794 | + isys->v4l2_dev.ctrl_handler = NULL; | |
| 795 | + | |
| 796 | + ret = v4l2_device_register(&pdev->dev, &isys->v4l2_dev); | |
| 797 | + if (ret < 0) | |
| 798 | + goto out_media_device_unregister; | |
| 799 | + | |
| 800 | + ret = isys_register_video_devices(isys); | |
| 801 | + if (ret) | |
| 802 | + goto out_v4l2_device_unregister; | |
| 803 | + | |
| 804 | + ret = isys_csi2_register_subdevices(isys); | |
| 805 | + if (ret) | |
| 806 | + goto out_isys_unregister_video_device; | |
| 807 | + | |
| 808 | + ret = isys_csi2_create_media_links(isys); | |
| 809 | + if (ret) | |
| 810 | + goto out_isys_unregister_subdevices; | |
| 811 | + | |
| 812 | + ret = isys_notifier_init(isys); | |
| 813 | + if (ret) | |
| 814 | + goto out_isys_unregister_subdevices; | |
| 815 | + | |
| 816 | + return 0; | |
| 817 | + | |
| 818 | +out_isys_unregister_subdevices: | |
| 819 | + isys_csi2_unregister_subdevices(isys); | |
| 820 | + | |
| 821 | +out_isys_unregister_video_device: | |
| 822 | + isys_unregister_video_devices(isys); | |
| 823 | + | |
| 824 | +out_v4l2_device_unregister: | |
| 825 | + v4l2_device_unregister(&isys->v4l2_dev); | |
| 826 | + | |
| 827 | +out_media_device_unregister: | |
| 828 | + media_device_unregister(&isys->media_dev); | |
| 829 | + media_device_cleanup(&isys->media_dev); | |
| 830 | + | |
| 831 | + dev_err(dev, "failed to register isys devices\n"); | |
| 832 | + | |
| 833 | + return ret; | |
| 834 | +} | |
| 835 | + | |
| 836 | +static void isys_unregister_devices(struct ipu6_isys *isys) | |
| 837 | +{ | |
| 838 | + isys_unregister_video_devices(isys); | |
| 839 | + isys_csi2_unregister_subdevices(isys); | |
| 840 | + v4l2_device_unregister(&isys->v4l2_dev); | |
| 841 | + media_device_unregister(&isys->media_dev); | |
| 842 | + media_device_cleanup(&isys->media_dev); | |
| 843 | +} | |
| 844 | + | |
| 845 | +static int isys_runtime_pm_resume(struct device *dev) | |
| 846 | +{ | |
| 847 | + struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); | |
| 848 | + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); | |
| 849 | + struct ipu6_device *isp = adev->isp; | |
| 850 | + unsigned long flags; | |
| 851 | + int ret; | |
| 852 | + | |
| 853 | + if (!isys) | |
| 854 | + return 0; | |
| 855 | + | |
| 856 | + ret = ipu6_mmu_hw_init(adev->mmu); | |
| 857 | + if (ret) | |
| 858 | + return ret; | |
| 859 | + | |
| 860 | + cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE); | |
| 861 | + | |
| 862 | + ret = ipu6_buttress_start_tsc_sync(isp); | |
| 863 | + if (ret) | |
| 864 | + return ret; | |
| 865 | + | |
| 866 | + spin_lock_irqsave(&isys->power_lock, flags); | |
| 867 | + isys->power = 1; | |
| 868 | + spin_unlock_irqrestore(&isys->power_lock, flags); | |
| 869 | + | |
| 870 | + isys_setup_hw(isys); | |
| 871 | + | |
| 872 | + set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_ON); | |
| 873 | + | |
| 874 | + return 0; | |
| 875 | +} | |
| 876 | + | |
| 877 | +static int isys_runtime_pm_suspend(struct device *dev) | |
| 878 | +{ | |
| 879 | + struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); | |
| 880 | + struct ipu6_isys *isys; | |
| 881 | + unsigned long flags; | |
| 882 | + | |
| 883 | + isys = dev_get_drvdata(dev); | |
| 884 | + if (!isys) | |
| 885 | + return 0; | |
| 886 | + | |
| 887 | + spin_lock_irqsave(&isys->power_lock, flags); | |
| 888 | + isys->power = 0; | |
| 889 | + spin_unlock_irqrestore(&isys->power_lock, flags); | |
| 890 | + | |
| 891 | + mutex_lock(&isys->mutex); | |
| 892 | + isys->need_reset = false; | |
| 893 | + mutex_unlock(&isys->mutex); | |
| 894 | + | |
| 895 | + isys->phy_termcal_val = 0; | |
| 896 | + cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); | |
| 897 | + | |
| 898 | + set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_OFF); | |
| 899 | + | |
| 900 | + ipu6_mmu_hw_cleanup(adev->mmu); | |
| 901 | + | |
| 902 | + return 0; | |
| 903 | +} | |
| 904 | + | |
| 905 | +static int isys_suspend(struct device *dev) | |
| 906 | +{ | |
| 907 | + struct ipu6_isys *isys = dev_get_drvdata(dev); | |
| 908 | + | |
| 909 | + /* If stream is open, refuse to suspend */ | |
| 910 | + if (isys->stream_opened) | |
| 911 | + return -EBUSY; | |
| 912 | + | |
| 913 | + return 0; | |
| 914 | +} | |
| 915 | + | |
| 916 | +static int isys_resume(struct device *dev) | |
| 917 | +{ | |
| 918 | + return 0; | |
| 919 | +} | |
| 920 | + | |
| 921 | +static const struct dev_pm_ops isys_pm_ops = { | |
| 922 | + .runtime_suspend = isys_runtime_pm_suspend, | |
| 923 | + .runtime_resume = isys_runtime_pm_resume, | |
| 924 | + .suspend = isys_suspend, | |
| 925 | + .resume = isys_resume, | |
| 926 | +}; | |
| 927 | + | |
| 928 | +static void isys_remove(struct auxiliary_device *auxdev) | |
| 929 | +{ | |
| 930 | + struct ipu6_bus_device *adev = auxdev_to_adev(auxdev); | |
| 931 | + struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev); | |
| 932 | + struct ipu6_device *isp = adev->isp; | |
| 933 | + struct isys_fw_msgs *fwmsg, *safe; | |
| 934 | + unsigned int i; | |
| 935 | + | |
| 936 | + list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) | |
| 937 | + dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs), | |
| 938 | + fwmsg, fwmsg->dma_addr, 0); | |
| 939 | + | |
| 940 | + list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) | |
| 941 | + dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs), | |
| 942 | + fwmsg, fwmsg->dma_addr, 0); | |
| 943 | + | |
| 944 | + isys_unregister_devices(isys); | |
| 945 | + isys_notifier_cleanup(isys); | |
| 946 | + | |
| 947 | + cpu_latency_qos_remove_request(&isys->pm_qos); | |
| 948 | + | |
| 949 | + if (!isp->secure_mode) { | |
| 950 | + ipu6_cpd_free_pkg_dir(adev); | |
| 951 | + ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt); | |
| 952 | + release_firmware(adev->fw); | |
| 953 | + } | |
| 954 | + | |
| 955 | + for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) | |
| 956 | + mutex_destroy(&isys->streams[i].mutex); | |
| 957 | + | |
| 958 | + isys_iwake_watermark_cleanup(isys); | |
| 959 | + mutex_destroy(&isys->stream_mutex); | |
| 960 | + mutex_destroy(&isys->mutex); | |
| 961 | +} | |
| 962 | + | |
| 963 | +static int alloc_fw_msg_bufs(struct ipu6_isys *isys, int amount) | |
| 964 | +{ | |
| 965 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 966 | + struct isys_fw_msgs *addr; | |
| 967 | + dma_addr_t dma_addr; | |
| 968 | + unsigned long flags; | |
| 969 | + unsigned int i; | |
| 970 | + | |
| 971 | + for (i = 0; i < amount; i++) { | |
| 972 | + addr = dma_alloc_attrs(dev, sizeof(struct isys_fw_msgs), | |
| 973 | + &dma_addr, GFP_KERNEL, 0); | |
| 974 | + if (!addr) | |
| 975 | + break; | |
| 976 | + addr->dma_addr = dma_addr; | |
| 977 | + | |
| 978 | + spin_lock_irqsave(&isys->listlock, flags); | |
| 979 | + list_add(&addr->head, &isys->framebuflist); | |
| 980 | + spin_unlock_irqrestore(&isys->listlock, flags); | |
| 981 | + } | |
| 982 | + | |
| 983 | + if (i == amount) | |
| 984 | + return 0; | |
| 985 | + | |
| 986 | + spin_lock_irqsave(&isys->listlock, flags); | |
| 987 | + while (!list_empty(&isys->framebuflist)) { | |
| 988 | + addr = list_first_entry(&isys->framebuflist, | |
| 989 | + struct isys_fw_msgs, head); | |
| 990 | + list_del(&addr->head); | |
| 991 | + spin_unlock_irqrestore(&isys->listlock, flags); | |
| 992 | + dma_free_attrs(dev, sizeof(struct isys_fw_msgs), addr, | |
| 993 | + addr->dma_addr, 0); | |
| 994 | + spin_lock_irqsave(&isys->listlock, flags); | |
| 995 | + } | |
| 996 | + spin_unlock_irqrestore(&isys->listlock, flags); | |
| 997 | + | |
| 998 | + return -ENOMEM; | |
| 999 | +} | |
| 1000 | + | |
| 1001 | +struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream) | |
| 1002 | +{ | |
| 1003 | + struct ipu6_isys *isys = stream->isys; | |
| 1004 | + struct device *dev = &isys->adev->auxdev.dev; | |
| 1005 | + struct isys_fw_msgs *msg; | |
| 1006 | + unsigned long flags; | |
| 1007 | + int ret; | |
| 1008 | + | |
| 1009 | + spin_lock_irqsave(&isys->listlock, flags); | |
| 1010 | + if (list_empty(&isys->framebuflist)) { | |
| 1011 | + spin_unlock_irqrestore(&isys->listlock, flags); | |
| 1012 | + dev_dbg(dev, "Frame list empty\n"); | |
| 1013 | + | |
| 1014 | + ret = alloc_fw_msg_bufs(isys, 5); | |
| 1015 | + if (ret < 0) | |
| 1016 | + return NULL; | |
| 1017 | + | |
| 1018 | + spin_lock_irqsave(&isys->listlock, flags); | |
| 1019 | + if (list_empty(&isys->framebuflist)) { | |
| 1020 | + spin_unlock_irqrestore(&isys->listlock, flags); | |
| 1021 | + dev_err(dev, "Frame list empty\n"); | |
| 1022 | + return NULL; | |
| 1023 | + } | |
| 1024 | + } | |
| 1025 | + msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head); | |
| 1026 | + list_move(&msg->head, &isys->framebuflist_fw); | |
| 1027 | + spin_unlock_irqrestore(&isys->listlock, flags); | |
| 1028 | + memset(&msg->fw_msg, 0, sizeof(msg->fw_msg)); | |
| 1029 | + | |
| 1030 | + return msg; | |
| 1031 | +} | |
| 1032 | + | |
| 1033 | +void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys) | |
| 1034 | +{ | |
| 1035 | + struct isys_fw_msgs *fwmsg, *fwmsg0; | |
| 1036 | + unsigned long flags; | |
| 1037 | + | |
| 1038 | + spin_lock_irqsave(&isys->listlock, flags); | |
| 1039 | + list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head) | |
| 1040 | + list_move(&fwmsg->head, &isys->framebuflist); | |
| 1041 | + spin_unlock_irqrestore(&isys->listlock, flags); | |
| 1042 | +} | |
| 1043 | + | |
| 1044 | +void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, u64 data) | |
| 1045 | +{ | |
| 1046 | + struct isys_fw_msgs *msg; | |
| 1047 | + unsigned long flags; | |
| 1048 | + u64 *ptr = (u64 *)data; | |
| 1049 | + | |
| 1050 | + if (!ptr) | |
| 1051 | + return; | |
| 1052 | + | |
| 1053 | + spin_lock_irqsave(&isys->listlock, flags); | |
| 1054 | + msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy); | |
| 1055 | + list_move(&msg->head, &isys->framebuflist); | |
| 1056 | + spin_unlock_irqrestore(&isys->listlock, flags); | |
| 1057 | +} | |
| 1058 | + | |
| 1059 | +static int isys_probe(struct auxiliary_device *auxdev, | |
| 1060 | + const struct auxiliary_device_id *auxdev_id) | |
| 1061 | +{ | |
| 1062 | + const struct ipu6_isys_internal_csi2_pdata *csi2_pdata; | |
| 1063 | + struct ipu6_bus_device *adev = auxdev_to_adev(auxdev); | |
| 1064 | + struct ipu6_device *isp = adev->isp; | |
| 1065 | + const struct firmware *fw; | |
| 1066 | + struct ipu6_isys *isys; | |
| 1067 | + unsigned int i; | |
| 1068 | + int ret; | |
| 1069 | + | |
| 1070 | + if (!isp->bus_ready_to_probe) | |
| 1071 | + return -EPROBE_DEFER; | |
| 1072 | + | |
| 1073 | + isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL); | |
| 1074 | + if (!isys) | |
| 1075 | + return -ENOMEM; | |
| 1076 | + | |
| 1077 | + adev->auxdrv_data = | |
| 1078 | + (const struct ipu6_auxdrv_data *)auxdev_id->driver_data; | |
| 1079 | + adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver); | |
| 1080 | + isys->adev = adev; | |
| 1081 | + isys->pdata = adev->pdata; | |
| 1082 | + csi2_pdata = &isys->pdata->ipdata->csi2; | |
| 1083 | + | |
| 1084 | + isys->csi2 = devm_kcalloc(&auxdev->dev, csi2_pdata->nports, | |
| 1085 | + sizeof(*isys->csi2), GFP_KERNEL); | |
| 1086 | + if (!isys->csi2) | |
| 1087 | + return -ENOMEM; | |
| 1088 | + | |
| 1089 | + ret = ipu6_mmu_hw_init(adev->mmu); | |
| 1090 | + if (ret) | |
| 1091 | + return ret; | |
| 1092 | + | |
| 1093 | + /* initial sensor type */ | |
| 1094 | + isys->sensor_type = isys->pdata->ipdata->sensor_type_start; | |
| 1095 | + | |
| 1096 | + spin_lock_init(&isys->streams_lock); | |
| 1097 | + spin_lock_init(&isys->power_lock); | |
| 1098 | + isys->power = 0; | |
| 1099 | + isys->phy_termcal_val = 0; | |
| 1100 | + | |
| 1101 | + mutex_init(&isys->mutex); | |
| 1102 | + mutex_init(&isys->stream_mutex); | |
| 1103 | + | |
| 1104 | + spin_lock_init(&isys->listlock); | |
| 1105 | + INIT_LIST_HEAD(&isys->framebuflist); | |
| 1106 | + INIT_LIST_HEAD(&isys->framebuflist_fw); | |
| 1107 | + | |
| 1108 | + isys->line_align = IPU6_ISYS_2600_MEM_LINE_ALIGN; | |
| 1109 | + isys->icache_prefetch = 0; | |
| 1110 | + | |
| 1111 | + dev_set_drvdata(&auxdev->dev, isys); | |
| 1112 | + | |
| 1113 | + isys_stream_init(isys); | |
| 1114 | + | |
| 1115 | + if (!isp->secure_mode) { | |
| 1116 | + fw = isp->cpd_fw; | |
| 1117 | + ret = ipu6_buttress_map_fw_image(adev, fw, &adev->fw_sgt); | |
| 1118 | + if (ret) | |
| 1119 | + goto release_firmware; | |
| 1120 | + | |
| 1121 | + ret = ipu6_cpd_create_pkg_dir(adev, isp->cpd_fw->data); | |
| 1122 | + if (ret) | |
| 1123 | + goto remove_shared_buffer; | |
| 1124 | + } | |
| 1125 | + | |
| 1126 | + cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); | |
| 1127 | + | |
| 1128 | + ret = alloc_fw_msg_bufs(isys, 20); | |
| 1129 | + if (ret < 0) | |
| 1130 | + goto out_remove_pkg_dir_shared_buffer; | |
| 1131 | + | |
| 1132 | + isys_iwake_watermark_init(isys); | |
| 1133 | + | |
| 1134 | + if (is_ipu6se(adev->isp->hw_ver)) | |
| 1135 | + isys->phy_set_power = ipu6_isys_jsl_phy_set_power; | |
| 1136 | + else if (is_ipu6ep_mtl(adev->isp->hw_ver)) | |
| 1137 | + isys->phy_set_power = ipu6_isys_dwc_phy_set_power; | |
| 1138 | + else | |
| 1139 | + isys->phy_set_power = ipu6_isys_mcd_phy_set_power; | |
| 1140 | + | |
| 1141 | + ret = isys_register_devices(isys); | |
| 1142 | + if (ret) | |
| 1143 | + goto out_remove_pkg_dir_shared_buffer; | |
| 1144 | + | |
| 1145 | + ipu6_mmu_hw_cleanup(adev->mmu); | |
| 1146 | + | |
| 1147 | + return 0; | |
| 1148 | + | |
| 1149 | +out_remove_pkg_dir_shared_buffer: | |
| 1150 | + if (!isp->secure_mode) | |
| 1151 | + ipu6_cpd_free_pkg_dir(adev); | |
| 1152 | +remove_shared_buffer: | |
| 1153 | + if (!isp->secure_mode) | |
| 1154 | + ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt); | |
| 1155 | +release_firmware: | |
| 1156 | + if (!isp->secure_mode) | |
| 1157 | + release_firmware(adev->fw); | |
| 1158 | + | |
| 1159 | + for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) | |
| 1160 | + mutex_destroy(&isys->streams[i].mutex); | |
| 1161 | + | |
| 1162 | + mutex_destroy(&isys->mutex); | |
| 1163 | + mutex_destroy(&isys->stream_mutex); | |
| 1164 | + | |
| 1165 | + ipu6_mmu_hw_cleanup(adev->mmu); | |
| 1166 | + | |
| 1167 | + return ret; | |
| 1168 | +} | |
| 1169 | + | |
| 1170 | +struct fwmsg { | |
| 1171 | + int type; | |
| 1172 | + char *msg; | |
| 1173 | + bool valid_ts; | |
| 1174 | +}; | |
| 1175 | + | |
| 1176 | +static const struct fwmsg fw_msg[] = { | |
| 1177 | + {IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE", 0}, | |
| 1178 | + {IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK", 0}, | |
| 1179 | + {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK, "STREAM_START_ACK", 0}, | |
| 1180 | + {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, | |
| 1181 | + "STREAM_START_AND_CAPTURE_ACK", 0}, | |
| 1182 | + {IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, "STREAM_STOP_ACK", 0}, | |
| 1183 | + {IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK", 0}, | |
| 1184 | + {IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY", 1}, | |
| 1185 | + {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK", 0}, | |
| 1186 | + {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, | |
| 1187 | + "STREAM_START_AND_CAPTURE_DONE", 1}, | |
| 1188 | + {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE", 1}, | |
| 1189 | + {IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF", 1}, | |
| 1190 | + {IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF", 1}, | |
| 1191 | + {IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY, "STATS_READY", 1}, | |
| 1192 | + {-1, "UNKNOWN MESSAGE", 0} | |
| 1193 | +}; | |
| 1194 | + | |
| 1195 | +static u32 resp_type_to_index(int type) | |
| 1196 | +{ | |
| 1197 | + unsigned int i; | |
| 1198 | + | |
| 1199 | + for (i = 0; i < ARRAY_SIZE(fw_msg); i++) | |
| 1200 | + if (fw_msg[i].type == type) | |
| 1201 | + return i; | |
| 1202 | + | |
| 1203 | + return ARRAY_SIZE(fw_msg) - 1; | |
| 1204 | +} | |
| 1205 | + | |
| 1206 | +static int isys_isr_one(struct ipu6_bus_device *adev) | |
| 1207 | +{ | |
| 1208 | + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); | |
| 1209 | + struct ipu6_fw_isys_resp_info_abi *resp; | |
| 1210 | + struct ipu6_isys_stream *stream; | |
| 1211 | + struct ipu6_isys_csi2 *csi2 = NULL; | |
| 1212 | + u32 index; | |
| 1213 | + u64 ts; | |
| 1214 | + | |
| 1215 | + if (!isys->fwcom) | |
| 1216 | + return 1; | |
| 1217 | + | |
| 1218 | + resp = ipu6_fw_isys_get_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES); | |
| 1219 | + if (!resp) | |
| 1220 | + return 1; | |
| 1221 | + | |
| 1222 | + ts = (u64)resp->timestamp[1] << 32 | resp->timestamp[0]; | |
| 1223 | + | |
| 1224 | + index = resp_type_to_index(resp->type); | |
| 1225 | + dev_dbg(&adev->auxdev.dev, | |
| 1226 | + "FW resp %02d %s, stream %u, ts 0x%16.16llx, pin %d\n", | |
| 1227 | + resp->type, fw_msg[index].msg, resp->stream_handle, | |
| 1228 | + fw_msg[index].valid_ts ? ts : 0, resp->pin_id); | |
| 1229 | + | |
| 1230 | + if (resp->error_info.error == IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION) | |
| 1231 | + /* Suspension is kind of special case: not enough buffers */ | |
| 1232 | + dev_dbg(&adev->auxdev.dev, | |
| 1233 | + "FW error resp SUSPENSION, details %d\n", | |
| 1234 | + resp->error_info.error_details); | |
| 1235 | + else if (resp->error_info.error) | |
| 1236 | + dev_dbg(&adev->auxdev.dev, | |
| 1237 | + "FW error resp error %d, details %d\n", | |
| 1238 | + resp->error_info.error, resp->error_info.error_details); | |
| 1239 | + | |
| 1240 | + if (resp->stream_handle >= IPU6_ISYS_MAX_STREAMS) { | |
| 1241 | + dev_err(&adev->auxdev.dev, "bad stream handle %u\n", | |
| 1242 | + resp->stream_handle); | |
| 1243 | + goto leave; | |
| 1244 | + } | |
| 1245 | + | |
| 1246 | + stream = ipu6_isys_query_stream_by_handle(isys, resp->stream_handle); | |
| 1247 | + if (!stream) { | |
| 1248 | + dev_err(&adev->auxdev.dev, "stream of stream_handle %u is unused\n", | |
| 1249 | + resp->stream_handle); | |
| 1250 | + goto leave; | |
| 1251 | + } | |
| 1252 | + stream->error = resp->error_info.error; | |
| 1253 | + | |
| 1254 | + csi2 = ipu6_isys_subdev_to_csi2(stream->asd); | |
| 1255 | + | |
| 1256 | + switch (resp->type) { | |
| 1257 | + case IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE: | |
| 1258 | + complete(&stream->stream_open_completion); | |
| 1259 | + break; | |
| 1260 | + case IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK: | |
| 1261 | + complete(&stream->stream_close_completion); | |
| 1262 | + break; | |
| 1263 | + case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK: | |
| 1264 | + complete(&stream->stream_start_completion); | |
| 1265 | + break; | |
| 1266 | + case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: | |
| 1267 | + complete(&stream->stream_start_completion); | |
| 1268 | + break; | |
| 1269 | + case IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK: | |
| 1270 | + complete(&stream->stream_stop_completion); | |
| 1271 | + break; | |
| 1272 | + case IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK: | |
| 1273 | + complete(&stream->stream_stop_completion); | |
| 1274 | + break; | |
| 1275 | + case IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY: | |
| 1276 | + /* | |
| 1277 | + * firmware only release the capture msg until software | |
| 1278 | + * get pin_data_ready event | |
| 1279 | + */ | |
| 1280 | + ipu6_put_fw_msg_buf(ipu6_bus_get_drvdata(adev), resp->buf_id); | |
| 1281 | + if (resp->pin_id < IPU6_ISYS_OUTPUT_PINS && | |
| 1282 | + stream->output_pins[resp->pin_id].pin_ready) | |
| 1283 | + stream->output_pins[resp->pin_id].pin_ready(stream, | |
| 1284 | + resp); | |
| 1285 | + else | |
| 1286 | + dev_warn(&adev->auxdev.dev, | |
| 1287 | + "%d:No data pin ready handler for pin id %d\n", | |
| 1288 | + resp->stream_handle, resp->pin_id); | |
| 1289 | + if (csi2) | |
| 1290 | + ipu6_isys_csi2_error(csi2); | |
| 1291 | + | |
| 1292 | + break; | |
| 1293 | + case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK: | |
| 1294 | + break; | |
| 1295 | + case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: | |
| 1296 | + case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE: | |
| 1297 | + break; | |
| 1298 | + case IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF: | |
| 1299 | + | |
| 1300 | + ipu6_isys_csi2_sof_event_by_stream(stream); | |
| 1301 | + stream->seq[stream->seq_index].sequence = | |
| 1302 | + atomic_read(&stream->sequence) - 1; | |
| 1303 | + stream->seq[stream->seq_index].timestamp = ts; | |
| 1304 | + dev_dbg(&adev->auxdev.dev, | |
| 1305 | + "sof: handle %d: (index %u), timestamp 0x%16.16llx\n", | |
| 1306 | + resp->stream_handle, | |
| 1307 | + stream->seq[stream->seq_index].sequence, ts); | |
| 1308 | + stream->seq_index = (stream->seq_index + 1) | |
| 1309 | + % IPU6_ISYS_MAX_PARALLEL_SOF; | |
| 1310 | + break; | |
| 1311 | + case IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF: | |
| 1312 | + ipu6_isys_csi2_eof_event_by_stream(stream); | |
| 1313 | + dev_dbg(&adev->auxdev.dev, | |
| 1314 | + "eof: handle %d: (index %u), timestamp 0x%16.16llx\n", | |
| 1315 | + resp->stream_handle, | |
| 1316 | + stream->seq[stream->seq_index].sequence, ts); | |
| 1317 | + break; | |
| 1318 | + case IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY: | |
| 1319 | + break; | |
| 1320 | + default: | |
| 1321 | + dev_err(&adev->auxdev.dev, "%d:unknown response type %u\n", | |
| 1322 | + resp->stream_handle, resp->type); | |
| 1323 | + break; | |
| 1324 | + } | |
| 1325 | + | |
| 1326 | + ipu6_isys_put_stream(stream); | |
| 1327 | +leave: | |
| 1328 | + ipu6_fw_isys_put_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES); | |
| 1329 | + return 0; | |
| 1330 | +} | |
| 1331 | + | |
| 1332 | +static const struct ipu6_auxdrv_data ipu6_isys_auxdrv_data = { | |
| 1333 | + .isr = isys_isr, | |
| 1334 | + .isr_threaded = NULL, | |
| 1335 | + .wake_isr_thread = false, | |
| 1336 | +}; | |
| 1337 | + | |
| 1338 | +static const struct auxiliary_device_id ipu6_isys_id_table[] = { | |
| 1339 | + { | |
| 1340 | + .name = "intel_ipu6.isys", | |
| 1341 | + .driver_data = (kernel_ulong_t)&ipu6_isys_auxdrv_data, | |
| 1342 | + }, | |
| 1343 | + { } | |
| 1344 | +}; | |
| 1345 | +MODULE_DEVICE_TABLE(auxiliary, ipu6_isys_id_table); | |
| 1346 | + | |
| 1347 | +static struct auxiliary_driver isys_driver = { | |
| 1348 | + .name = IPU6_ISYS_NAME, | |
| 1349 | + .probe = isys_probe, | |
| 1350 | + .remove = isys_remove, | |
| 1351 | + .id_table = ipu6_isys_id_table, | |
| 1352 | + .driver = { | |
| 1353 | + .pm = &isys_pm_ops, | |
| 1354 | + }, | |
| 1355 | +}; | |
| 1356 | + | |
| 1357 | +module_auxiliary_driver(isys_driver); | |
| 1358 | + | |
| 1359 | +MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); | |
| 1360 | +MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); | |
| 1361 | +MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); | |
| 1362 | +MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>"); | |
| 1363 | +MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>"); | |
| 1364 | +MODULE_LICENSE("GPL"); | |
| 1365 | +MODULE_DESCRIPTION("Intel IPU6 input system driver"); | |
| 1366 | +MODULE_IMPORT_NS(INTEL_IPU6); | |
| 1367 | +MODULE_IMPORT_NS(INTEL_IPU_BRIDGE); | |
| @@ -0,0 +1,206 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2013--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_ISYS_H | |
| 5 | +#define IPU6_ISYS_H | |
| 6 | + | |
| 7 | +#include <linux/irqreturn.h> | |
| 8 | +#include <linux/list.h> | |
| 9 | +#include <linux/mutex.h> | |
| 10 | +#include <linux/pm_qos.h> | |
| 11 | +#include <linux/spinlock_types.h> | |
| 12 | +#include <linux/types.h> | |
| 13 | + | |
| 14 | +#include <media/media-device.h> | |
| 15 | +#include <media/v4l2-async.h> | |
| 16 | +#include <media/v4l2-device.h> | |
| 17 | + | |
| 18 | +#include "ipu6.h" | |
| 19 | +#include "ipu6-fw-isys.h" | |
| 20 | +#include "ipu6-isys-csi2.h" | |
| 21 | +#include "ipu6-isys-video.h" | |
| 22 | + | |
| 23 | +struct ipu6_bus_device; | |
| 24 | + | |
| 25 | +#define IPU6_ISYS_ENTITY_PREFIX "Intel IPU6" | |
| 26 | +/* FW support max 16 streams */ | |
| 27 | +#define IPU6_ISYS_MAX_STREAMS 16 | |
| 28 | +#define ISYS_UNISPART_IRQS (IPU6_ISYS_UNISPART_IRQ_SW | \ | |
| 29 | + IPU6_ISYS_UNISPART_IRQ_CSI0 | \ | |
| 30 | + IPU6_ISYS_UNISPART_IRQ_CSI1) | |
| 31 | + | |
| 32 | +#define IPU6_ISYS_2600_MEM_LINE_ALIGN 64 | |
| 33 | + | |
| 34 | +/* | |
| 35 | + * Current message queue configuration. These must be big enough | |
| 36 | + * so that they never gets full. Queues are located in system memory | |
| 37 | + */ | |
| 38 | +#define IPU6_ISYS_SIZE_RECV_QUEUE 40 | |
| 39 | +#define IPU6_ISYS_SIZE_SEND_QUEUE 40 | |
| 40 | +#define IPU6_ISYS_SIZE_PROXY_RECV_QUEUE 5 | |
| 41 | +#define IPU6_ISYS_SIZE_PROXY_SEND_QUEUE 5 | |
| 42 | +#define IPU6_ISYS_NUM_RECV_QUEUE 1 | |
| 43 | + | |
| 44 | +#define IPU6_ISYS_MIN_WIDTH 2U | |
| 45 | +#define IPU6_ISYS_MIN_HEIGHT 2U | |
| 46 | +#define IPU6_ISYS_MAX_WIDTH 4672U | |
| 47 | +#define IPU6_ISYS_MAX_HEIGHT 3416U | |
| 48 | + | |
| 49 | +/* the threshold granularity is 2KB on IPU6 */ | |
| 50 | +#define IPU6_SRAM_GRANULARITY_SHIFT 11 | |
| 51 | +#define IPU6_SRAM_GRANULARITY_SIZE 2048 | |
| 52 | +/* the threshold granularity is 1KB on IPU6SE */ | |
| 53 | +#define IPU6SE_SRAM_GRANULARITY_SHIFT 10 | |
| 54 | +#define IPU6SE_SRAM_GRANULARITY_SIZE 1024 | |
| 55 | +/* IS pixel buffer is 256KB, MaxSRAMSize is 200KB on IPU6 */ | |
| 56 | +#define IPU6_MAX_SRAM_SIZE (200 << 10) | |
| 57 | +/* IS pixel buffer is 128KB, MaxSRAMSize is 96KB on IPU6SE */ | |
| 58 | +#define IPU6SE_MAX_SRAM_SIZE (96 << 10) | |
| 59 | + | |
| 60 | +#define IPU6EP_LTR_VALUE 200 | |
| 61 | +#define IPU6EP_MIN_MEMOPEN_TH 0x4 | |
| 62 | +#define IPU6EP_MTL_LTR_VALUE 1023 | |
| 63 | +#define IPU6EP_MTL_MIN_MEMOPEN_TH 0xc | |
| 64 | + | |
| 65 | +struct ltr_did { | |
| 66 | + union { | |
| 67 | + u32 value; | |
| 68 | + struct { | |
| 69 | + u8 val0; | |
| 70 | + u8 val1; | |
| 71 | + u8 val2; | |
| 72 | + u8 val3; | |
| 73 | + } bits; | |
| 74 | + } lut_ltr; | |
| 75 | + union { | |
| 76 | + u32 value; | |
| 77 | + struct { | |
| 78 | + u8 th0; | |
| 79 | + u8 th1; | |
| 80 | + u8 th2; | |
| 81 | + u8 th3; | |
| 82 | + } bits; | |
| 83 | + } lut_fill_time; | |
| 84 | +}; | |
| 85 | + | |
| 86 | +struct isys_iwake_watermark { | |
| 87 | + bool iwake_enabled; | |
| 88 | + bool force_iwake_disable; | |
| 89 | + u32 iwake_threshold; | |
| 90 | + u64 isys_pixelbuffer_datarate; | |
| 91 | + struct ltr_did ltrdid; | |
| 92 | + struct mutex mutex; /* protect whole struct */ | |
| 93 | + struct ipu6_isys *isys; | |
| 94 | + struct list_head video_list; | |
| 95 | +}; | |
| 96 | + | |
| 97 | +struct ipu6_isys_csi2_config { | |
| 98 | + u32 nlanes; | |
| 99 | + u32 port; | |
| 100 | +}; | |
| 101 | + | |
| 102 | +struct sensor_async_sd { | |
| 103 | + struct v4l2_async_connection asc; | |
| 104 | + struct ipu6_isys_csi2_config csi2; | |
| 105 | +}; | |
| 106 | + | |
| 107 | +/* | |
| 108 | + * struct ipu6_isys | |
| 109 | + * | |
| 110 | + * @media_dev: Media device | |
| 111 | + * @v4l2_dev: V4L2 device | |
| 112 | + * @adev: ISYS bus device | |
| 113 | + * @power: Is ISYS powered on or not? | |
| 114 | + * @isr_bits: Which bits does the ISR handle? | |
| 115 | + * @power_lock: Serialise access to power (power state in general) | |
| 116 | + * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers | |
| 117 | + * @streams_lock: serialise access to streams | |
| 118 | + * @streams: streams per firmware stream ID | |
| 119 | + * @fwcom: fw communication layer private pointer | |
| 120 | + * or optional external library private pointer | |
| 121 | + * @line_align: line alignment in memory | |
| 122 | + * @phy_termcal_val: the termination calibration value, only used for DWC PHY | |
| 123 | + * @need_reset: Isys requires d0i0->i3 transition | |
| 124 | + * @ref_count: total number of callers fw open | |
| 125 | + * @mutex: serialise access isys video open/release related operations | |
| 126 | + * @stream_mutex: serialise stream start and stop, queueing requests | |
| 127 | + * @pdata: platform data pointer | |
| 128 | + * @csi2: CSI-2 receivers | |
| 129 | + */ | |
| 130 | +struct ipu6_isys { | |
| 131 | + struct media_device media_dev; | |
| 132 | + struct v4l2_device v4l2_dev; | |
| 133 | + struct ipu6_bus_device *adev; | |
| 134 | + | |
| 135 | + int power; | |
| 136 | + spinlock_t power_lock; | |
| 137 | + u32 isr_csi2_bits; | |
| 138 | + u32 csi2_rx_ctrl_cached; | |
| 139 | + spinlock_t streams_lock; | |
| 140 | + struct ipu6_isys_stream streams[IPU6_ISYS_MAX_STREAMS]; | |
| 141 | + int streams_ref_count[IPU6_ISYS_MAX_STREAMS]; | |
| 142 | + void *fwcom; | |
| 143 | + unsigned int line_align; | |
| 144 | + u32 phy_termcal_val; | |
| 145 | + bool need_reset; | |
| 146 | + bool icache_prefetch; | |
| 147 | + bool csi2_cse_ipc_not_supported; | |
| 148 | + unsigned int ref_count; | |
| 149 | + unsigned int stream_opened; | |
| 150 | + unsigned int sensor_type; | |
| 151 | + | |
| 152 | + struct mutex mutex; | |
| 153 | + struct mutex stream_mutex; | |
| 154 | + | |
| 155 | + struct ipu6_isys_pdata *pdata; | |
| 156 | + | |
| 157 | + int (*phy_set_power)(struct ipu6_isys *isys, | |
| 158 | + struct ipu6_isys_csi2_config *cfg, | |
| 159 | + const struct ipu6_isys_csi2_timing *timing, | |
| 160 | + bool on); | |
| 161 | + | |
| 162 | + struct ipu6_isys_csi2 *csi2; | |
| 163 | + | |
| 164 | + struct pm_qos_request pm_qos; | |
| 165 | + spinlock_t listlock; /* Protect framebuflist */ | |
| 166 | + struct list_head framebuflist; | |
| 167 | + struct list_head framebuflist_fw; | |
| 168 | + struct v4l2_async_notifier notifier; | |
| 169 | + struct isys_iwake_watermark iwake_watermark; | |
| 170 | +}; | |
| 171 | + | |
| 172 | +struct isys_fw_msgs { | |
| 173 | + union { | |
| 174 | + u64 dummy; | |
| 175 | + struct ipu6_fw_isys_frame_buff_set_abi frame; | |
| 176 | + struct ipu6_fw_isys_stream_cfg_data_abi stream; | |
| 177 | + } fw_msg; | |
| 178 | + struct list_head head; | |
| 179 | + dma_addr_t dma_addr; | |
| 180 | +}; | |
| 181 | + | |
| 182 | +struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream); | |
| 183 | +void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, u64 data); | |
| 184 | +void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys); | |
| 185 | + | |
| 186 | +extern const struct v4l2_ioctl_ops ipu6_isys_ioctl_ops; | |
| 187 | + | |
| 188 | +void isys_setup_hw(struct ipu6_isys *isys); | |
| 189 | +irqreturn_t isys_isr(struct ipu6_bus_device *adev); | |
| 190 | +void update_watermark_setting(struct ipu6_isys *isys); | |
| 191 | + | |
| 192 | +int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys, | |
| 193 | + struct ipu6_isys_csi2_config *cfg, | |
| 194 | + const struct ipu6_isys_csi2_timing *timing, | |
| 195 | + bool on); | |
| 196 | + | |
| 197 | +int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys, | |
| 198 | + struct ipu6_isys_csi2_config *cfg, | |
| 199 | + const struct ipu6_isys_csi2_timing *timing, | |
| 200 | + bool on); | |
| 201 | + | |
| 202 | +int ipu6_isys_jsl_phy_set_power(struct ipu6_isys *isys, | |
| 203 | + struct ipu6_isys_csi2_config *cfg, | |
| 204 | + const struct ipu6_isys_csi2_timing *timing, | |
| 205 | + bool on); | |
| 206 | +#endif /* IPU6_ISYS_H */ | |
| @@ -0,0 +1,846 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | +#include <asm/barrier.h> | |
| 6 | + | |
| 7 | +#include <linux/align.h> | |
| 8 | +#include <linux/atomic.h> | |
| 9 | +#include <linux/bitops.h> | |
| 10 | +#include <linux/bits.h> | |
| 11 | +#include <linux/bug.h> | |
| 12 | +#include <linux/cacheflush.h> | |
| 13 | +#include <linux/dma-mapping.h> | |
| 14 | +#include <linux/err.h> | |
| 15 | +#include <linux/gfp.h> | |
| 16 | +#include <linux/io.h> | |
| 17 | +#include <linux/iova.h> | |
| 18 | +#include <linux/math.h> | |
| 19 | +#include <linux/minmax.h> | |
| 20 | +#include <linux/mm.h> | |
| 21 | +#include <linux/pfn.h> | |
| 22 | +#include <linux/slab.h> | |
| 23 | +#include <linux/spinlock.h> | |
| 24 | +#include <linux/types.h> | |
| 25 | +#include <linux/vmalloc.h> | |
| 26 | + | |
| 27 | +#include "ipu6.h" | |
| 28 | +#include "ipu6-dma.h" | |
| 29 | +#include "ipu6-mmu.h" | |
| 30 | +#include "ipu6-platform-regs.h" | |
| 31 | + | |
| 32 | +#define ISP_PAGE_SHIFT 12 | |
| 33 | +#define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT) | |
| 34 | +#define ISP_PAGE_MASK (~(ISP_PAGE_SIZE - 1)) | |
| 35 | + | |
| 36 | +#define ISP_L1PT_SHIFT 22 | |
| 37 | +#define ISP_L1PT_MASK (~((1U << ISP_L1PT_SHIFT) - 1)) | |
| 38 | + | |
| 39 | +#define ISP_L2PT_SHIFT 12 | |
| 40 | +#define ISP_L2PT_MASK (~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK)))) | |
| 41 | + | |
| 42 | +#define ISP_L1PT_PTES 1024 | |
| 43 | +#define ISP_L2PT_PTES 1024 | |
| 44 | + | |
| 45 | +#define ISP_PADDR_SHIFT 12 | |
| 46 | + | |
| 47 | +#define REG_TLB_INVALIDATE 0x0000 | |
| 48 | + | |
| 49 | +#define REG_L1_PHYS 0x0004 /* 27-bit pfn */ | |
| 50 | +#define REG_INFO 0x0008 | |
| 51 | + | |
| 52 | +#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT) | |
| 53 | + | |
| 54 | +static void tlb_invalidate(struct ipu6_mmu *mmu) | |
| 55 | +{ | |
| 56 | + unsigned long flags; | |
| 57 | + unsigned int i; | |
| 58 | + | |
| 59 | + spin_lock_irqsave(&mmu->ready_lock, flags); | |
| 60 | + if (!mmu->ready) { | |
| 61 | + spin_unlock_irqrestore(&mmu->ready_lock, flags); | |
| 62 | + return; | |
| 63 | + } | |
| 64 | + | |
| 65 | + for (i = 0; i < mmu->nr_mmus; i++) { | |
| 66 | + /* | |
| 67 | + * To avoid the HW bug induced dead lock in some of the IPU6 | |
| 68 | + * MMUs on successive invalidate calls, we need to first do a | |
| 69 | + * read to the page table base before writing the invalidate | |
| 70 | + * register. MMUs which need to implement this WA, will have | |
| 71 | + * the insert_read_before_invalidate flags set as true. | |
| 72 | + * Disregard the return value of the read. | |
| 73 | + */ | |
| 74 | + if (mmu->mmu_hw[i].insert_read_before_invalidate) | |
| 75 | + readl(mmu->mmu_hw[i].base + REG_L1_PHYS); | |
| 76 | + | |
| 77 | + writel(0xffffffff, mmu->mmu_hw[i].base + | |
| 78 | + REG_TLB_INVALIDATE); | |
| 79 | + /* | |
| 80 | + * The TLB invalidation is a "single cycle" (IOMMU clock cycles) | |
| 81 | + * When the actual MMIO write reaches the IPU6 TLB Invalidate | |
| 82 | + * register, wmb() will force the TLB invalidate out if the CPU | |
| 83 | + * attempts to update the IOMMU page table (or sooner). | |
| 84 | + */ | |
| 85 | + wmb(); | |
| 86 | + } | |
| 87 | + spin_unlock_irqrestore(&mmu->ready_lock, flags); | |
| 88 | +} | |
| 89 | + | |
| 90 | +#ifdef DEBUG | |
| 91 | +static void page_table_dump(struct ipu6_mmu_info *mmu_info) | |
| 92 | +{ | |
| 93 | + u32 l1_idx; | |
| 94 | + | |
| 95 | + dev_dbg(mmu_info->dev, "begin IOMMU page table dump\n"); | |
| 96 | + | |
| 97 | + for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) { | |
| 98 | + u32 l2_idx; | |
| 99 | + u32 iova = (phys_addr_t)l1_idx << ISP_L1PT_SHIFT; | |
| 100 | + | |
| 101 | + if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) | |
| 102 | + continue; | |
| 103 | + dev_dbg(mmu_info->dev, | |
| 104 | + "l1 entry %u; iovas 0x%8.8x-0x%8.8x, at %pa\n", | |
| 105 | + l1_idx, iova, iova + ISP_PAGE_SIZE, | |
| 106 | + TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx])); | |
| 107 | + | |
| 108 | + for (l2_idx = 0; l2_idx < ISP_L2PT_PTES; l2_idx++) { | |
| 109 | + u32 *l2_pt = mmu_info->l2_pts[l1_idx]; | |
| 110 | + u32 iova2 = iova + (l2_idx << ISP_L2PT_SHIFT); | |
| 111 | + | |
| 112 | + if (l2_pt[l2_idx] == mmu_info->dummy_page_pteval) | |
| 113 | + continue; | |
| 114 | + | |
| 115 | + dev_dbg(mmu_info->dev, | |
| 116 | + "\tl2 entry %u; iova 0x%8.8x, phys %pa\n", | |
| 117 | + l2_idx, iova2, | |
| 118 | + TBL_PHYS_ADDR(l2_pt[l2_idx])); | |
| 119 | + } | |
| 120 | + } | |
| 121 | + | |
| 122 | + dev_dbg(mmu_info->dev, "end IOMMU page table dump\n"); | |
| 123 | +} | |
| 124 | +#endif /* DEBUG */ | |
| 125 | + | |
| 126 | +static dma_addr_t map_single(struct ipu6_mmu_info *mmu_info, void *ptr) | |
| 127 | +{ | |
| 128 | + dma_addr_t dma; | |
| 129 | + | |
| 130 | + dma = dma_map_single(mmu_info->dev, ptr, PAGE_SIZE, DMA_BIDIRECTIONAL); | |
| 131 | + if (dma_mapping_error(mmu_info->dev, dma)) | |
| 132 | + return 0; | |
| 133 | + | |
| 134 | + return dma; | |
| 135 | +} | |
| 136 | + | |
| 137 | +static int get_dummy_page(struct ipu6_mmu_info *mmu_info) | |
| 138 | +{ | |
| 139 | + void *pt = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); | |
| 140 | + dma_addr_t dma; | |
| 141 | + | |
| 142 | + if (!pt) | |
| 143 | + return -ENOMEM; | |
| 144 | + | |
| 145 | + dev_dbg(mmu_info->dev, "dummy_page: get_zeroed_page() == %p\n", pt); | |
| 146 | + | |
| 147 | + dma = map_single(mmu_info, pt); | |
| 148 | + if (!dma) { | |
| 149 | + dev_err(mmu_info->dev, "Failed to map dummy page\n"); | |
| 150 | + goto err_free_page; | |
| 151 | + } | |
| 152 | + | |
| 153 | + mmu_info->dummy_page = pt; | |
| 154 | + mmu_info->dummy_page_pteval = dma >> ISP_PAGE_SHIFT; | |
| 155 | + | |
| 156 | + return 0; | |
| 157 | + | |
| 158 | +err_free_page: | |
| 159 | + free_page((unsigned long)pt); | |
| 160 | + return -ENOMEM; | |
| 161 | +} | |
| 162 | + | |
| 163 | +static void free_dummy_page(struct ipu6_mmu_info *mmu_info) | |
| 164 | +{ | |
| 165 | + dma_unmap_single(mmu_info->dev, | |
| 166 | + TBL_PHYS_ADDR(mmu_info->dummy_page_pteval), | |
| 167 | + PAGE_SIZE, DMA_BIDIRECTIONAL); | |
| 168 | + free_page((unsigned long)mmu_info->dummy_page); | |
| 169 | +} | |
| 170 | + | |
| 171 | +static int alloc_dummy_l2_pt(struct ipu6_mmu_info *mmu_info) | |
| 172 | +{ | |
| 173 | + u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); | |
| 174 | + dma_addr_t dma; | |
| 175 | + unsigned int i; | |
| 176 | + | |
| 177 | + if (!pt) | |
| 178 | + return -ENOMEM; | |
| 179 | + | |
| 180 | + dev_dbg(mmu_info->dev, "dummy_l2: get_zeroed_page() = %p\n", pt); | |
| 181 | + | |
| 182 | + dma = map_single(mmu_info, pt); | |
| 183 | + if (!dma) { | |
| 184 | + dev_err(mmu_info->dev, "Failed to map l2pt page\n"); | |
| 185 | + goto err_free_page; | |
| 186 | + } | |
| 187 | + | |
| 188 | + for (i = 0; i < ISP_L2PT_PTES; i++) | |
| 189 | + pt[i] = mmu_info->dummy_page_pteval; | |
| 190 | + | |
| 191 | + mmu_info->dummy_l2_pt = pt; | |
| 192 | + mmu_info->dummy_l2_pteval = dma >> ISP_PAGE_SHIFT; | |
| 193 | + | |
| 194 | + return 0; | |
| 195 | + | |
| 196 | +err_free_page: | |
| 197 | + free_page((unsigned long)pt); | |
| 198 | + return -ENOMEM; | |
| 199 | +} | |
| 200 | + | |
| 201 | +static void free_dummy_l2_pt(struct ipu6_mmu_info *mmu_info) | |
| 202 | +{ | |
| 203 | + dma_unmap_single(mmu_info->dev, | |
| 204 | + TBL_PHYS_ADDR(mmu_info->dummy_l2_pteval), | |
| 205 | + PAGE_SIZE, DMA_BIDIRECTIONAL); | |
| 206 | + free_page((unsigned long)mmu_info->dummy_l2_pt); | |
| 207 | +} | |
| 208 | + | |
| 209 | +static u32 *alloc_l1_pt(struct ipu6_mmu_info *mmu_info) | |
| 210 | +{ | |
| 211 | + u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); | |
| 212 | + dma_addr_t dma; | |
| 213 | + unsigned int i; | |
| 214 | + | |
| 215 | + if (!pt) | |
| 216 | + return NULL; | |
| 217 | + | |
| 218 | + dev_dbg(mmu_info->dev, "alloc_l1: get_zeroed_page() = %p\n", pt); | |
| 219 | + | |
| 220 | + for (i = 0; i < ISP_L1PT_PTES; i++) | |
| 221 | + pt[i] = mmu_info->dummy_l2_pteval; | |
| 222 | + | |
| 223 | + dma = map_single(mmu_info, pt); | |
| 224 | + if (!dma) { | |
| 225 | + dev_err(mmu_info->dev, "Failed to map l1pt page\n"); | |
| 226 | + goto err_free_page; | |
| 227 | + } | |
| 228 | + | |
| 229 | + mmu_info->l1_pt_dma = dma >> ISP_PADDR_SHIFT; | |
| 230 | + dev_dbg(mmu_info->dev, "l1 pt %p mapped at %llx\n", pt, dma); | |
| 231 | + | |
| 232 | + return pt; | |
| 233 | + | |
| 234 | +err_free_page: | |
| 235 | + free_page((unsigned long)pt); | |
| 236 | + return NULL; | |
| 237 | +} | |
| 238 | + | |
| 239 | +static u32 *alloc_l2_pt(struct ipu6_mmu_info *mmu_info) | |
| 240 | +{ | |
| 241 | + u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); | |
| 242 | + unsigned int i; | |
| 243 | + | |
| 244 | + if (!pt) | |
| 245 | + return NULL; | |
| 246 | + | |
| 247 | + dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt); | |
| 248 | + | |
| 249 | + for (i = 0; i < ISP_L1PT_PTES; i++) | |
| 250 | + pt[i] = mmu_info->dummy_page_pteval; | |
| 251 | + | |
| 252 | + return pt; | |
| 253 | +} | |
| 254 | + | |
| 255 | +static int l2_map(struct ipu6_mmu_info *mmu_info, unsigned long iova, | |
| 256 | + phys_addr_t paddr, size_t size) | |
| 257 | +{ | |
| 258 | + u32 l1_idx = iova >> ISP_L1PT_SHIFT; | |
| 259 | + u32 iova_start = iova; | |
| 260 | + u32 *l2_pt, *l2_virt; | |
| 261 | + unsigned int l2_idx; | |
| 262 | + unsigned long flags; | |
| 263 | + dma_addr_t dma; | |
| 264 | + u32 l1_entry; | |
| 265 | + | |
| 266 | + dev_dbg(mmu_info->dev, | |
| 267 | + "mapping l2 page table for l1 index %u (iova %8.8x)\n", | |
| 268 | + l1_idx, (u32)iova); | |
| 269 | + | |
| 270 | + spin_lock_irqsave(&mmu_info->lock, flags); | |
| 271 | + l1_entry = mmu_info->l1_pt[l1_idx]; | |
| 272 | + if (l1_entry == mmu_info->dummy_l2_pteval) { | |
| 273 | + l2_virt = mmu_info->l2_pts[l1_idx]; | |
| 274 | + if (likely(!l2_virt)) { | |
| 275 | + l2_virt = alloc_l2_pt(mmu_info); | |
| 276 | + if (!l2_virt) { | |
| 277 | + spin_unlock_irqrestore(&mmu_info->lock, flags); | |
| 278 | + return -ENOMEM; | |
| 279 | + } | |
| 280 | + } | |
| 281 | + | |
| 282 | + dma = map_single(mmu_info, l2_virt); | |
| 283 | + if (!dma) { | |
| 284 | + dev_err(mmu_info->dev, "Failed to map l2pt page\n"); | |
| 285 | + free_page((unsigned long)l2_virt); | |
| 286 | + spin_unlock_irqrestore(&mmu_info->lock, flags); | |
| 287 | + return -EINVAL; | |
| 288 | + } | |
| 289 | + | |
| 290 | + l1_entry = dma >> ISP_PADDR_SHIFT; | |
| 291 | + | |
| 292 | + dev_dbg(mmu_info->dev, "page for l1_idx %u %p allocated\n", | |
| 293 | + l1_idx, l2_virt); | |
| 294 | + mmu_info->l1_pt[l1_idx] = l1_entry; | |
| 295 | + mmu_info->l2_pts[l1_idx] = l2_virt; | |
| 296 | + clflush_cache_range((void *)&mmu_info->l1_pt[l1_idx], | |
| 297 | + sizeof(mmu_info->l1_pt[l1_idx])); | |
| 298 | + } | |
| 299 | + | |
| 300 | + l2_pt = mmu_info->l2_pts[l1_idx]; | |
| 301 | + | |
| 302 | + dev_dbg(mmu_info->dev, "l2_pt at %p with dma 0x%x\n", l2_pt, l1_entry); | |
| 303 | + | |
| 304 | + paddr = ALIGN(paddr, ISP_PAGE_SIZE); | |
| 305 | + | |
| 306 | + l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; | |
| 307 | + | |
| 308 | + dev_dbg(mmu_info->dev, "l2_idx %u, phys 0x%8.8x\n", l2_idx, | |
| 309 | + l2_pt[l2_idx]); | |
| 310 | + if (l2_pt[l2_idx] != mmu_info->dummy_page_pteval) { | |
| 311 | + spin_unlock_irqrestore(&mmu_info->lock, flags); | |
| 312 | + return -EINVAL; | |
| 313 | + } | |
| 314 | + | |
| 315 | + l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT; | |
| 316 | + | |
| 317 | + clflush_cache_range((void *)&l2_pt[l2_idx], sizeof(l2_pt[l2_idx])); | |
| 318 | + spin_unlock_irqrestore(&mmu_info->lock, flags); | |
| 319 | + | |
| 320 | + dev_dbg(mmu_info->dev, "l2 index %u mapped as 0x%8.8x\n", l2_idx, | |
| 321 | + l2_pt[l2_idx]); | |
| 322 | + | |
| 323 | + return 0; | |
| 324 | +} | |
| 325 | + | |
| 326 | +static int __ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova, | |
| 327 | + phys_addr_t paddr, size_t size) | |
| 328 | +{ | |
| 329 | + u32 iova_start = round_down(iova, ISP_PAGE_SIZE); | |
| 330 | + u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE); | |
| 331 | + | |
| 332 | + dev_dbg(mmu_info->dev, | |
| 333 | + "mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr 0x%10.10llx\n", | |
| 334 | + iova_start, iova_end, size, paddr); | |
| 335 | + | |
| 336 | + return l2_map(mmu_info, iova_start, paddr, size); | |
| 337 | +} | |
| 338 | + | |
| 339 | +static size_t l2_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova, | |
| 340 | + phys_addr_t dummy, size_t size) | |
| 341 | +{ | |
| 342 | + u32 l1_idx = iova >> ISP_L1PT_SHIFT; | |
| 343 | + u32 iova_start = iova; | |
| 344 | + unsigned int l2_idx; | |
| 345 | + size_t unmapped = 0; | |
| 346 | + unsigned long flags; | |
| 347 | + u32 *l2_pt; | |
| 348 | + | |
| 349 | + dev_dbg(mmu_info->dev, "unmapping l2 page table for l1 index %u (iova 0x%8.8lx)\n", | |
| 350 | + l1_idx, iova); | |
| 351 | + | |
| 352 | + spin_lock_irqsave(&mmu_info->lock, flags); | |
| 353 | + if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) { | |
| 354 | + spin_unlock_irqrestore(&mmu_info->lock, flags); | |
| 355 | + dev_err(mmu_info->dev, | |
| 356 | + "unmap iova 0x%8.8lx l1 idx %u which was not mapped\n", | |
| 357 | + iova, l1_idx); | |
| 358 | + return 0; | |
| 359 | + } | |
| 360 | + | |
| 361 | + for (l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; | |
| 362 | + (iova_start & ISP_L1PT_MASK) + (l2_idx << ISP_PAGE_SHIFT) | |
| 363 | + < iova_start + size && l2_idx < ISP_L2PT_PTES; l2_idx++) { | |
| 364 | + l2_pt = mmu_info->l2_pts[l1_idx]; | |
| 365 | + dev_dbg(mmu_info->dev, | |
| 366 | + "unmap l2 index %u with pteval 0x%10.10llx\n", | |
| 367 | + l2_idx, TBL_PHYS_ADDR(l2_pt[l2_idx])); | |
| 368 | + l2_pt[l2_idx] = mmu_info->dummy_page_pteval; | |
| 369 | + | |
| 370 | + clflush_cache_range((void *)&l2_pt[l2_idx], | |
| 371 | + sizeof(l2_pt[l2_idx])); | |
| 372 | + unmapped++; | |
| 373 | + } | |
| 374 | + spin_unlock_irqrestore(&mmu_info->lock, flags); | |
| 375 | + | |
| 376 | + return unmapped << ISP_PAGE_SHIFT; | |
| 377 | +} | |
| 378 | + | |
| 379 | +static size_t __ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, | |
| 380 | + unsigned long iova, size_t size) | |
| 381 | +{ | |
| 382 | + return l2_unmap(mmu_info, iova, 0, size); | |
| 383 | +} | |
| 384 | + | |
| 385 | +static int allocate_trash_buffer(struct ipu6_mmu *mmu) | |
| 386 | +{ | |
| 387 | + unsigned int n_pages = PHYS_PFN(PAGE_ALIGN(IPU6_MMUV2_TRASH_RANGE)); | |
| 388 | + struct iova *iova; | |
| 389 | + unsigned int i; | |
| 390 | + dma_addr_t dma; | |
| 391 | + unsigned long iova_addr; | |
| 392 | + int ret; | |
| 393 | + | |
| 394 | + /* Allocate 8MB in iova range */ | |
| 395 | + iova = alloc_iova(&mmu->dmap->iovad, n_pages, | |
| 396 | + PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); | |
| 397 | + if (!iova) { | |
| 398 | + dev_err(mmu->dev, "cannot allocate iova range for trash\n"); | |
| 399 | + return -ENOMEM; | |
| 400 | + } | |
| 401 | + | |
| 402 | + dma = dma_map_page(mmu->dmap->mmu_info->dev, mmu->trash_page, 0, | |
| 403 | + PAGE_SIZE, DMA_BIDIRECTIONAL); | |
| 404 | + if (dma_mapping_error(mmu->dmap->mmu_info->dev, dma)) { | |
| 405 | + dev_err(mmu->dmap->mmu_info->dev, "Failed to map trash page\n"); | |
| 406 | + ret = -ENOMEM; | |
| 407 | + goto out_free_iova; | |
| 408 | + } | |
| 409 | + | |
| 410 | + mmu->pci_trash_page = dma; | |
| 411 | + | |
| 412 | + /* | |
| 413 | + * Map the 8MB iova address range to the same physical trash page | |
| 414 | + * mmu->trash_page which is already reserved at the probe | |
| 415 | + */ | |
| 416 | + iova_addr = iova->pfn_lo; | |
| 417 | + for (i = 0; i < n_pages; i++) { | |
| 418 | + ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), | |
| 419 | + mmu->pci_trash_page, PAGE_SIZE); | |
| 420 | + if (ret) { | |
| 421 | + dev_err(mmu->dev, | |
| 422 | + "mapping trash buffer range failed\n"); | |
| 423 | + goto out_unmap; | |
| 424 | + } | |
| 425 | + | |
| 426 | + iova_addr++; | |
| 427 | + } | |
| 428 | + | |
| 429 | + mmu->iova_trash_page = PFN_PHYS(iova->pfn_lo); | |
| 430 | + dev_dbg(mmu->dev, "iova trash buffer for MMUID: %d is %u\n", | |
| 431 | + mmu->mmid, (unsigned int)mmu->iova_trash_page); | |
| 432 | + return 0; | |
| 433 | + | |
| 434 | +out_unmap: | |
| 435 | + ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), | |
| 436 | + PFN_PHYS(iova_size(iova))); | |
| 437 | + dma_unmap_page(mmu->dmap->mmu_info->dev, mmu->pci_trash_page, | |
| 438 | + PAGE_SIZE, DMA_BIDIRECTIONAL); | |
| 439 | +out_free_iova: | |
| 440 | + __free_iova(&mmu->dmap->iovad, iova); | |
| 441 | + return ret; | |
| 442 | +} | |
| 443 | + | |
| 444 | +int ipu6_mmu_hw_init(struct ipu6_mmu *mmu) | |
| 445 | +{ | |
| 446 | + struct ipu6_mmu_info *mmu_info; | |
| 447 | + unsigned long flags; | |
| 448 | + unsigned int i; | |
| 449 | + | |
| 450 | + mmu_info = mmu->dmap->mmu_info; | |
| 451 | + | |
| 452 | + /* Initialise the each MMU HW block */ | |
| 453 | + for (i = 0; i < mmu->nr_mmus; i++) { | |
| 454 | + struct ipu6_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; | |
| 455 | + unsigned int j; | |
| 456 | + u16 block_addr; | |
| 457 | + | |
| 458 | + /* Write page table address per MMU */ | |
| 459 | + writel((phys_addr_t)mmu_info->l1_pt_dma, | |
| 460 | + mmu->mmu_hw[i].base + REG_L1_PHYS); | |
| 461 | + | |
| 462 | + /* Set info bits per MMU */ | |
| 463 | + writel(mmu->mmu_hw[i].info_bits, | |
| 464 | + mmu->mmu_hw[i].base + REG_INFO); | |
| 465 | + | |
| 466 | + /* Configure MMU TLB stream configuration for L1 */ | |
| 467 | + for (j = 0, block_addr = 0; j < mmu_hw->nr_l1streams; | |
| 468 | + block_addr += mmu->mmu_hw[i].l1_block_sz[j], j++) { | |
| 469 | + if (block_addr > IPU6_MAX_LI_BLOCK_ADDR) { | |
| 470 | + dev_err(mmu->dev, "invalid L1 configuration\n"); | |
| 471 | + return -EINVAL; | |
| 472 | + } | |
| 473 | + | |
| 474 | + /* Write block start address for each streams */ | |
| 475 | + writel(block_addr, mmu_hw->base + | |
| 476 | + mmu_hw->l1_stream_id_reg_offset + 4 * j); | |
| 477 | + } | |
| 478 | + | |
| 479 | + /* Configure MMU TLB stream configuration for L2 */ | |
| 480 | + for (j = 0, block_addr = 0; j < mmu_hw->nr_l2streams; | |
| 481 | + block_addr += mmu->mmu_hw[i].l2_block_sz[j], j++) { | |
| 482 | + if (block_addr > IPU6_MAX_L2_BLOCK_ADDR) { | |
| 483 | + dev_err(mmu->dev, "invalid L2 configuration\n"); | |
| 484 | + return -EINVAL; | |
| 485 | + } | |
| 486 | + | |
| 487 | + writel(block_addr, mmu_hw->base + | |
| 488 | + mmu_hw->l2_stream_id_reg_offset + 4 * j); | |
| 489 | + } | |
| 490 | + } | |
| 491 | + | |
| 492 | + if (!mmu->trash_page) { | |
| 493 | + int ret; | |
| 494 | + | |
| 495 | + mmu->trash_page = alloc_page(GFP_KERNEL); | |
| 496 | + if (!mmu->trash_page) { | |
| 497 | + dev_err(mmu->dev, "insufficient memory for trash buffer\n"); | |
| 498 | + return -ENOMEM; | |
| 499 | + } | |
| 500 | + | |
| 501 | + ret = allocate_trash_buffer(mmu); | |
| 502 | + if (ret) { | |
| 503 | + __free_page(mmu->trash_page); | |
| 504 | + mmu->trash_page = NULL; | |
| 505 | + dev_err(mmu->dev, "trash buffer allocation failed\n"); | |
| 506 | + return ret; | |
| 507 | + } | |
| 508 | + } | |
| 509 | + | |
| 510 | + spin_lock_irqsave(&mmu->ready_lock, flags); | |
| 511 | + mmu->ready = true; | |
| 512 | + spin_unlock_irqrestore(&mmu->ready_lock, flags); | |
| 513 | + | |
| 514 | + return 0; | |
| 515 | +} | |
| 516 | +EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_init, INTEL_IPU6); | |
| 517 | + | |
| 518 | +static struct ipu6_mmu_info *ipu6_mmu_alloc(struct ipu6_device *isp) | |
| 519 | +{ | |
| 520 | + struct ipu6_mmu_info *mmu_info; | |
| 521 | + int ret; | |
| 522 | + | |
| 523 | + mmu_info = kzalloc(sizeof(*mmu_info), GFP_KERNEL); | |
| 524 | + if (!mmu_info) | |
| 525 | + return NULL; | |
| 526 | + | |
| 527 | + mmu_info->aperture_start = 0; | |
| 528 | + mmu_info->aperture_end = DMA_BIT_MASK(isp->secure_mode ? | |
| 529 | + IPU6_MMU_ADDR_BITS : | |
| 530 | + IPU6_MMU_ADDR_BITS_NON_SECURE); | |
| 531 | + mmu_info->pgsize_bitmap = SZ_4K; | |
| 532 | + mmu_info->dev = &isp->pdev->dev; | |
| 533 | + | |
| 534 | + ret = get_dummy_page(mmu_info); | |
| 535 | + if (ret) | |
| 536 | + goto err_free_info; | |
| 537 | + | |
| 538 | + ret = alloc_dummy_l2_pt(mmu_info); | |
| 539 | + if (ret) | |
| 540 | + goto err_free_dummy_page; | |
| 541 | + | |
| 542 | + mmu_info->l2_pts = vzalloc(ISP_L2PT_PTES * sizeof(*mmu_info->l2_pts)); | |
| 543 | + if (!mmu_info->l2_pts) | |
| 544 | + goto err_free_dummy_l2_pt; | |
| 545 | + | |
| 546 | + /* | |
| 547 | + * We always map the L1 page table (a single page as well as | |
| 548 | + * the L2 page tables). | |
| 549 | + */ | |
| 550 | + mmu_info->l1_pt = alloc_l1_pt(mmu_info); | |
| 551 | + if (!mmu_info->l1_pt) | |
| 552 | + goto err_free_l2_pts; | |
| 553 | + | |
| 554 | + spin_lock_init(&mmu_info->lock); | |
| 555 | + | |
| 556 | + dev_dbg(mmu_info->dev, "domain initialised\n"); | |
| 557 | + | |
| 558 | + return mmu_info; | |
| 559 | + | |
| 560 | +err_free_l2_pts: | |
| 561 | + vfree(mmu_info->l2_pts); | |
| 562 | +err_free_dummy_l2_pt: | |
| 563 | + free_dummy_l2_pt(mmu_info); | |
| 564 | +err_free_dummy_page: | |
| 565 | + free_dummy_page(mmu_info); | |
| 566 | +err_free_info: | |
| 567 | + kfree(mmu_info); | |
| 568 | + | |
| 569 | + return NULL; | |
| 570 | +} | |
| 571 | + | |
| 572 | +void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu) | |
| 573 | +{ | |
| 574 | + unsigned long flags; | |
| 575 | + | |
| 576 | + spin_lock_irqsave(&mmu->ready_lock, flags); | |
| 577 | + mmu->ready = false; | |
| 578 | + spin_unlock_irqrestore(&mmu->ready_lock, flags); | |
| 579 | +} | |
| 580 | +EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_cleanup, INTEL_IPU6); | |
| 581 | + | |
| 582 | +static struct ipu6_dma_mapping *alloc_dma_mapping(struct ipu6_device *isp) | |
| 583 | +{ | |
| 584 | + struct ipu6_dma_mapping *dmap; | |
| 585 | + | |
| 586 | + dmap = kzalloc(sizeof(*dmap), GFP_KERNEL); | |
| 587 | + if (!dmap) | |
| 588 | + return NULL; | |
| 589 | + | |
| 590 | + dmap->mmu_info = ipu6_mmu_alloc(isp); | |
| 591 | + if (!dmap->mmu_info) { | |
| 592 | + kfree(dmap); | |
| 593 | + return NULL; | |
| 594 | + } | |
| 595 | + | |
| 596 | + init_iova_domain(&dmap->iovad, SZ_4K, 1); | |
| 597 | + dmap->mmu_info->dmap = dmap; | |
| 598 | + | |
| 599 | + dev_dbg(&isp->pdev->dev, "alloc mapping\n"); | |
| 600 | + | |
| 601 | + iova_cache_get(); | |
| 602 | + | |
| 603 | + return dmap; | |
| 604 | +} | |
| 605 | + | |
| 606 | +phys_addr_t ipu6_mmu_iova_to_phys(struct ipu6_mmu_info *mmu_info, | |
| 607 | + dma_addr_t iova) | |
| 608 | +{ | |
| 609 | + phys_addr_t phy_addr; | |
| 610 | + unsigned long flags; | |
| 611 | + u32 *l2_pt; | |
| 612 | + | |
| 613 | + spin_lock_irqsave(&mmu_info->lock, flags); | |
| 614 | + l2_pt = mmu_info->l2_pts[iova >> ISP_L1PT_SHIFT]; | |
| 615 | + phy_addr = (phys_addr_t)l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT]; | |
| 616 | + phy_addr <<= ISP_PAGE_SHIFT; | |
| 617 | + spin_unlock_irqrestore(&mmu_info->lock, flags); | |
| 618 | + | |
| 619 | + return phy_addr; | |
| 620 | +} | |
| 621 | + | |
| 622 | +static size_t ipu6_mmu_pgsize(unsigned long pgsize_bitmap, | |
| 623 | + unsigned long addr_merge, size_t size) | |
| 624 | +{ | |
| 625 | + unsigned int pgsize_idx; | |
| 626 | + size_t pgsize; | |
| 627 | + | |
| 628 | + /* Max page size that still fits into 'size' */ | |
| 629 | + pgsize_idx = __fls(size); | |
| 630 | + | |
| 631 | + if (likely(addr_merge)) { | |
| 632 | + /* Max page size allowed by address */ | |
| 633 | + unsigned int align_pgsize_idx = __ffs(addr_merge); | |
| 634 | + | |
| 635 | + pgsize_idx = min(pgsize_idx, align_pgsize_idx); | |
| 636 | + } | |
| 637 | + | |
| 638 | + pgsize = (1UL << (pgsize_idx + 1)) - 1; | |
| 639 | + pgsize &= pgsize_bitmap; | |
| 640 | + | |
| 641 | + WARN_ON(!pgsize); | |
| 642 | + | |
| 643 | + /* pick the biggest page */ | |
| 644 | + pgsize_idx = __fls(pgsize); | |
| 645 | + pgsize = 1UL << pgsize_idx; | |
| 646 | + | |
| 647 | + return pgsize; | |
| 648 | +} | |
| 649 | + | |
| 650 | +size_t ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova, | |
| 651 | + size_t size) | |
| 652 | +{ | |
| 653 | + size_t unmapped_page, unmapped = 0; | |
| 654 | + unsigned int min_pagesz; | |
| 655 | + | |
| 656 | + /* find out the minimum page size supported */ | |
| 657 | + min_pagesz = 1 << __ffs(mmu_info->pgsize_bitmap); | |
| 658 | + | |
| 659 | + /* | |
| 660 | + * The virtual address and the size of the mapping must be | |
| 661 | + * aligned (at least) to the size of the smallest page supported | |
| 662 | + * by the hardware | |
| 663 | + */ | |
| 664 | + if (!IS_ALIGNED(iova | size, min_pagesz)) { | |
| 665 | + dev_err(NULL, "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n", | |
| 666 | + iova, size, min_pagesz); | |
| 667 | + return -EINVAL; | |
| 668 | + } | |
| 669 | + | |
| 670 | + /* | |
| 671 | + * Keep iterating until we either unmap 'size' bytes (or more) | |
| 672 | + * or we hit an area that isn't mapped. | |
| 673 | + */ | |
| 674 | + while (unmapped < size) { | |
| 675 | + size_t pgsize = ipu6_mmu_pgsize(mmu_info->pgsize_bitmap, | |
| 676 | + iova, size - unmapped); | |
| 677 | + | |
| 678 | + unmapped_page = __ipu6_mmu_unmap(mmu_info, iova, pgsize); | |
| 679 | + if (!unmapped_page) | |
| 680 | + break; | |
| 681 | + | |
| 682 | + dev_dbg(mmu_info->dev, "unmapped: iova 0x%lx size 0x%zx\n", | |
| 683 | + iova, unmapped_page); | |
| 684 | + | |
| 685 | + iova += unmapped_page; | |
| 686 | + unmapped += unmapped_page; | |
| 687 | + } | |
| 688 | + | |
| 689 | + return unmapped; | |
| 690 | +} | |
| 691 | + | |
| 692 | +int ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova, | |
| 693 | + phys_addr_t paddr, size_t size) | |
| 694 | +{ | |
| 695 | + unsigned long orig_iova = iova; | |
| 696 | + unsigned int min_pagesz; | |
| 697 | + size_t orig_size = size; | |
| 698 | + int ret = 0; | |
| 699 | + | |
| 700 | + if (mmu_info->pgsize_bitmap == 0UL) | |
| 701 | + return -ENODEV; | |
| 702 | + | |
| 703 | + /* find out the minimum page size supported */ | |
| 704 | + min_pagesz = 1 << __ffs(mmu_info->pgsize_bitmap); | |
| 705 | + | |
| 706 | + /* | |
| 707 | + * both the virtual address and the physical one, as well as | |
| 708 | + * the size of the mapping, must be aligned (at least) to the | |
| 709 | + * size of the smallest page supported by the hardware | |
| 710 | + */ | |
| 711 | + if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) { | |
| 712 | + dev_err(mmu_info->dev, | |
| 713 | + "unaligned: iova %lx pa %pa size %zx min_pagesz %x\n", | |
| 714 | + iova, &paddr, size, min_pagesz); | |
| 715 | + return -EINVAL; | |
| 716 | + } | |
| 717 | + | |
| 718 | + dev_dbg(mmu_info->dev, "map: iova 0x%lx pa %pa size 0x%zx\n", | |
| 719 | + iova, &paddr, size); | |
| 720 | + | |
| 721 | + while (size) { | |
| 722 | + size_t pgsize = ipu6_mmu_pgsize(mmu_info->pgsize_bitmap, | |
| 723 | + iova | paddr, size); | |
| 724 | + | |
| 725 | + dev_dbg(mmu_info->dev, | |
| 726 | + "mapping: iova 0x%lx pa %pa pgsize 0x%zx\n", | |
| 727 | + iova, &paddr, pgsize); | |
| 728 | + | |
| 729 | + ret = __ipu6_mmu_map(mmu_info, iova, paddr, pgsize); | |
| 730 | + if (ret) | |
| 731 | + break; | |
| 732 | + | |
| 733 | + iova += pgsize; | |
| 734 | + paddr += pgsize; | |
| 735 | + size -= pgsize; | |
| 736 | + } | |
| 737 | + | |
| 738 | + /* unroll mapping in case something went wrong */ | |
| 739 | + if (ret) | |
| 740 | + ipu6_mmu_unmap(mmu_info, orig_iova, orig_size - size); | |
| 741 | + | |
| 742 | + return ret; | |
| 743 | +} | |
| 744 | + | |
| 745 | +static void ipu6_mmu_destroy(struct ipu6_mmu *mmu) | |
| 746 | +{ | |
| 747 | + struct ipu6_dma_mapping *dmap = mmu->dmap; | |
| 748 | + struct ipu6_mmu_info *mmu_info = dmap->mmu_info; | |
| 749 | + struct iova *iova; | |
| 750 | + u32 l1_idx; | |
| 751 | + | |
| 752 | + if (mmu->iova_trash_page) { | |
| 753 | + iova = find_iova(&dmap->iovad, PHYS_PFN(mmu->iova_trash_page)); | |
| 754 | + if (iova) { | |
| 755 | + /* unmap and free the trash buffer iova */ | |
| 756 | + ipu6_mmu_unmap(mmu_info, PFN_PHYS(iova->pfn_lo), | |
| 757 | + PFN_PHYS(iova_size(iova))); | |
| 758 | + __free_iova(&dmap->iovad, iova); | |
| 759 | + } else { | |
| 760 | + dev_err(mmu->dev, "trash buffer iova not found.\n"); | |
| 761 | + } | |
| 762 | + | |
| 763 | + mmu->iova_trash_page = 0; | |
| 764 | + dma_unmap_page(mmu_info->dev, mmu->pci_trash_page, | |
| 765 | + PAGE_SIZE, DMA_BIDIRECTIONAL); | |
| 766 | + mmu->pci_trash_page = 0; | |
| 767 | + __free_page(mmu->trash_page); | |
| 768 | + } | |
| 769 | + | |
| 770 | + for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) { | |
| 771 | + if (mmu_info->l1_pt[l1_idx] != mmu_info->dummy_l2_pteval) { | |
| 772 | + dma_unmap_single(mmu_info->dev, | |
| 773 | + TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]), | |
| 774 | + PAGE_SIZE, DMA_BIDIRECTIONAL); | |
| 775 | + free_page((unsigned long)mmu_info->l2_pts[l1_idx]); | |
| 776 | + } | |
| 777 | + } | |
| 778 | + | |
| 779 | + vfree(mmu_info->l2_pts); | |
| 780 | + free_dummy_page(mmu_info); | |
| 781 | + dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma), | |
| 782 | + PAGE_SIZE, DMA_BIDIRECTIONAL); | |
| 783 | + free_page((unsigned long)mmu_info->dummy_l2_pt); | |
| 784 | + free_page((unsigned long)mmu_info->l1_pt); | |
| 785 | + kfree(mmu_info); | |
| 786 | +} | |
| 787 | + | |
| 788 | +struct ipu6_mmu *ipu6_mmu_init(struct device *dev, | |
| 789 | + void __iomem *base, int mmid, | |
| 790 | + const struct ipu6_hw_variants *hw) | |
| 791 | +{ | |
| 792 | + struct ipu6_device *isp = pci_get_drvdata(to_pci_dev(dev)); | |
| 793 | + struct ipu6_mmu_pdata *pdata; | |
| 794 | + struct ipu6_mmu *mmu; | |
| 795 | + unsigned int i; | |
| 796 | + | |
| 797 | + if (hw->nr_mmus > IPU6_MMU_MAX_DEVICES) | |
| 798 | + return ERR_PTR(-EINVAL); | |
| 799 | + | |
| 800 | + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); | |
| 801 | + if (!pdata) | |
| 802 | + return ERR_PTR(-ENOMEM); | |
| 803 | + | |
| 804 | + for (i = 0; i < hw->nr_mmus; i++) { | |
| 805 | + struct ipu6_mmu_hw *pdata_mmu = &pdata->mmu_hw[i]; | |
| 806 | + const struct ipu6_mmu_hw *src_mmu = &hw->mmu_hw[i]; | |
| 807 | + | |
| 808 | + if (src_mmu->nr_l1streams > IPU6_MMU_MAX_TLB_L1_STREAMS || | |
| 809 | + src_mmu->nr_l2streams > IPU6_MMU_MAX_TLB_L2_STREAMS) | |
| 810 | + return ERR_PTR(-EINVAL); | |
| 811 | + | |
| 812 | + *pdata_mmu = *src_mmu; | |
| 813 | + pdata_mmu->base = base + src_mmu->offset; | |
| 814 | + } | |
| 815 | + | |
| 816 | + mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL); | |
| 817 | + if (!mmu) | |
| 818 | + return ERR_PTR(-ENOMEM); | |
| 819 | + | |
| 820 | + mmu->mmid = mmid; | |
| 821 | + mmu->mmu_hw = pdata->mmu_hw; | |
| 822 | + mmu->nr_mmus = hw->nr_mmus; | |
| 823 | + mmu->tlb_invalidate = tlb_invalidate; | |
| 824 | + mmu->ready = false; | |
| 825 | + INIT_LIST_HEAD(&mmu->vma_list); | |
| 826 | + spin_lock_init(&mmu->ready_lock); | |
| 827 | + | |
| 828 | + mmu->dmap = alloc_dma_mapping(isp); | |
| 829 | + if (!mmu->dmap) { | |
| 830 | + dev_err(dev, "can't alloc dma mapping\n"); | |
| 831 | + return ERR_PTR(-ENOMEM); | |
| 832 | + } | |
| 833 | + | |
| 834 | + return mmu; | |
| 835 | +} | |
| 836 | + | |
| 837 | +void ipu6_mmu_cleanup(struct ipu6_mmu *mmu) | |
| 838 | +{ | |
| 839 | + struct ipu6_dma_mapping *dmap = mmu->dmap; | |
| 840 | + | |
| 841 | + ipu6_mmu_destroy(mmu); | |
| 842 | + mmu->dmap = NULL; | |
| 843 | + iova_cache_put(); | |
| 844 | + put_iova_domain(&dmap->iovad); | |
| 845 | + kfree(dmap); | |
| 846 | +} | |
| @@ -0,0 +1,73 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2013--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_MMU_H | |
| 5 | +#define IPU6_MMU_H | |
| 6 | + | |
| 7 | +#define ISYS_MMID 1 | |
| 8 | +#define PSYS_MMID 0 | |
| 9 | + | |
| 10 | +#include <linux/list.h> | |
| 11 | +#include <linux/spinlock_types.h> | |
| 12 | +#include <linux/types.h> | |
| 13 | + | |
| 14 | +struct device; | |
| 15 | +struct page; | |
| 16 | +struct ipu6_hw_variants; | |
| 17 | + | |
| 18 | +struct ipu6_mmu_info { | |
| 19 | + struct device *dev; | |
| 20 | + | |
| 21 | + u32 *l1_pt; | |
| 22 | + u32 l1_pt_dma; | |
| 23 | + u32 **l2_pts; | |
| 24 | + | |
| 25 | + u32 *dummy_l2_pt; | |
| 26 | + u32 dummy_l2_pteval; | |
| 27 | + void *dummy_page; | |
| 28 | + u32 dummy_page_pteval; | |
| 29 | + | |
| 30 | + dma_addr_t aperture_start; | |
| 31 | + dma_addr_t aperture_end; | |
| 32 | + unsigned long pgsize_bitmap; | |
| 33 | + | |
| 34 | + spinlock_t lock; /* Serialize access to users */ | |
| 35 | + struct ipu6_dma_mapping *dmap; | |
| 36 | +}; | |
| 37 | + | |
| 38 | +struct ipu6_mmu { | |
| 39 | + struct list_head node; | |
| 40 | + | |
| 41 | + struct ipu6_mmu_hw *mmu_hw; | |
| 42 | + unsigned int nr_mmus; | |
| 43 | + unsigned int mmid; | |
| 44 | + | |
| 45 | + phys_addr_t pgtbl; | |
| 46 | + struct device *dev; | |
| 47 | + | |
| 48 | + struct ipu6_dma_mapping *dmap; | |
| 49 | + struct list_head vma_list; | |
| 50 | + | |
| 51 | + struct page *trash_page; | |
| 52 | + dma_addr_t pci_trash_page; /* IOVA from PCI DMA services (parent) */ | |
| 53 | + dma_addr_t iova_trash_page; /* IOVA for IPU6 child nodes to use */ | |
| 54 | + | |
| 55 | + bool ready; | |
| 56 | + spinlock_t ready_lock; /* Serialize access to bool ready */ | |
| 57 | + | |
| 58 | + void (*tlb_invalidate)(struct ipu6_mmu *mmu); | |
| 59 | +}; | |
| 60 | + | |
| 61 | +struct ipu6_mmu *ipu6_mmu_init(struct device *dev, | |
| 62 | + void __iomem *base, int mmid, | |
| 63 | + const struct ipu6_hw_variants *hw); | |
| 64 | +void ipu6_mmu_cleanup(struct ipu6_mmu *mmu); | |
| 65 | +int ipu6_mmu_hw_init(struct ipu6_mmu *mmu); | |
| 66 | +void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu); | |
| 67 | +int ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova, | |
| 68 | + phys_addr_t paddr, size_t size); | |
| 69 | +size_t ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova, | |
| 70 | + size_t size); | |
| 71 | +phys_addr_t ipu6_mmu_iova_to_phys(struct ipu6_mmu_info *mmu_info, | |
| 72 | + dma_addr_t iova); | |
| 73 | +#endif | |
| @@ -0,0 +1,226 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2023--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_PLATFORM_BUTTRESS_REGS_H | |
| 5 | +#define IPU6_PLATFORM_BUTTRESS_REGS_H | |
| 6 | + | |
| 7 | +#include <linux/bits.h> | |
| 8 | + | |
| 9 | +/* IS_WORKPOINT_REQ */ | |
| 10 | +#define IPU6_BUTTRESS_REG_IS_FREQ_CTL 0x34 | |
| 11 | +/* PS_WORKPOINT_REQ */ | |
| 12 | +#define IPU6_BUTTRESS_REG_PS_FREQ_CTL 0x38 | |
| 13 | + | |
| 14 | +/* should be tuned for real silicon */ | |
| 15 | +#define IPU6_IS_FREQ_CTL_DEFAULT_RATIO 0x08 | |
| 16 | +#define IPU6SE_IS_FREQ_CTL_DEFAULT_RATIO 0x0a | |
| 17 | +#define IPU6_PS_FREQ_CTL_DEFAULT_RATIO 0x0d | |
| 18 | + | |
| 19 | +#define IPU6_IS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO 0x10 | |
| 20 | +#define IPU6_PS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO 0x0708 | |
| 21 | + | |
| 22 | +#define IPU6_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 3 | |
| 23 | +#define IPU6_BUTTRESS_PWR_STATE_IS_PWR_MASK GENMASK(4, 3) | |
| 24 | + | |
| 25 | +#define IPU6_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 6 | |
| 26 | +#define IPU6_BUTTRESS_PWR_STATE_PS_PWR_MASK GENMASK(7, 6) | |
| 27 | + | |
| 28 | +#define IPU6_BUTTRESS_PWR_STATE_DN_DONE 0x0 | |
| 29 | +#define IPU6_BUTTRESS_PWR_STATE_UP_PROCESS 0x1 | |
| 30 | +#define IPU6_BUTTRESS_PWR_STATE_DN_PROCESS 0x2 | |
| 31 | +#define IPU6_BUTTRESS_PWR_STATE_UP_DONE 0x3 | |
| 32 | + | |
| 33 | +#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_0 0x270 | |
| 34 | +#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_1 0x274 | |
| 35 | +#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_2 0x278 | |
| 36 | +#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_3 0x27c | |
| 37 | +#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_4 0x280 | |
| 38 | +#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_5 0x284 | |
| 39 | +#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_6 0x288 | |
| 40 | +#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_7 0x28c | |
| 41 | + | |
| 42 | +#define BUTTRESS_REG_WDT 0x8 | |
| 43 | +#define BUTTRESS_REG_BTRS_CTRL 0xc | |
| 44 | +#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0 BIT(0) | |
| 45 | +#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1 BIT(1) | |
| 46 | +#define BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND GENMASK(9, 8) | |
| 47 | + | |
| 48 | +#define BUTTRESS_REG_FW_RESET_CTL 0x30 | |
| 49 | +#define BUTTRESS_FW_RESET_CTL_START BIT(0) | |
| 50 | +#define BUTTRESS_FW_RESET_CTL_DONE BIT(1) | |
| 51 | + | |
| 52 | +#define BUTTRESS_REG_IS_FREQ_CTL 0x34 | |
| 53 | +#define BUTTRESS_REG_PS_FREQ_CTL 0x38 | |
| 54 | + | |
| 55 | +#define BUTTRESS_FREQ_CTL_START BIT(31) | |
| 56 | +#define BUTTRESS_FREQ_CTL_ICCMAX_LEVEL GENMASK(19, 16) | |
| 57 | +#define BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK GENMASK(15, 8) | |
| 58 | +#define BUTTRESS_FREQ_CTL_RATIO_MASK GENMASK(7, 0) | |
| 59 | + | |
| 60 | +#define BUTTRESS_REG_PWR_STATE 0x5c | |
| 61 | + | |
| 62 | +#define BUTTRESS_PWR_STATE_RESET 0x0 | |
| 63 | +#define BUTTRESS_PWR_STATE_PWR_ON_DONE 0x1 | |
| 64 | +#define BUTTRESS_PWR_STATE_PWR_RDY 0x3 | |
| 65 | +#define BUTTRESS_PWR_STATE_PWR_IDLE 0x4 | |
| 66 | + | |
| 67 | +#define BUTTRESS_PWR_STATE_HH_STATUS_MASK GENMASK(12, 11) | |
| 68 | + | |
| 69 | +enum { | |
| 70 | + BUTTRESS_PWR_STATE_HH_STATE_IDLE, | |
| 71 | + BUTTRESS_PWR_STATE_HH_STATE_IN_PRGS, | |
| 72 | + BUTTRESS_PWR_STATE_HH_STATE_DONE, | |
| 73 | + BUTTRESS_PWR_STATE_HH_STATE_ERR, | |
| 74 | +}; | |
| 75 | + | |
| 76 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_MASK GENMASK(23, 19) | |
| 77 | + | |
| 78 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IDLE 0x0 | |
| 79 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PLL_CMP 0x1 | |
| 80 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK 0x2 | |
| 81 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PG_ACK 0x3 | |
| 82 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_ASSRT_CYCLES 0x4 | |
| 83 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES1 0x5 | |
| 84 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES2 0x6 | |
| 85 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DEASSRT_CYCLES 0x7 | |
| 86 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_FUSE_WR_CMP 0x8 | |
| 87 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_BRK_POINT 0x9 | |
| 88 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IS_RDY 0xa | |
| 89 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_HALT_HALTED 0xb | |
| 90 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DURATION_CNT3 0xc | |
| 91 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK_PD 0xd | |
| 92 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_PD_BRK_POINT 0xe | |
| 93 | +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PD_PG_ACK0 0xf | |
| 94 | + | |
| 95 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_MASK GENMASK(28, 24) | |
| 96 | + | |
| 97 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_IDLE 0x0 | |
| 98 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_IP_RDY 0x1 | |
| 99 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_PRE_CNT_EXH 0x2 | |
| 100 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_VGI_PWRGOOD 0x3 | |
| 101 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_POST_CNT_EXH 0x4 | |
| 102 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WR_PLL_RATIO 0x5 | |
| 103 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_CMP 0x6 | |
| 104 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_CLKACK 0x7 | |
| 105 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_ASSRT_CYCLES 0x8 | |
| 106 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES1 0x9 | |
| 107 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES2 0xa | |
| 108 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_DEASSRT_CYCLES 0xb | |
| 109 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PU_BRK_PNT 0xc | |
| 110 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_FUSE_ACCPT 0xd | |
| 111 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PS_PWR_UP 0xf | |
| 112 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_4_HALTED 0x10 | |
| 113 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RESET_CNT3 0x11 | |
| 114 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_CLKACK 0x12 | |
| 115 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_OFF_IND 0x13 | |
| 116 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PH4 0x14 | |
| 117 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PLL_CMP 0x15 | |
| 118 | +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_CLKACK 0x16 | |
| 119 | + | |
| 120 | +#define BUTTRESS_REG_SECURITY_CTL 0x300 | |
| 121 | +#define BUTTRESS_REG_SKU 0x314 | |
| 122 | +#define BUTTRESS_REG_SECURITY_TOUCH 0x318 | |
| 123 | +#define BUTTRESS_REG_CAMERA_MASK 0x84 | |
| 124 | + | |
| 125 | +#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE BIT(16) | |
| 126 | +#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK GENMASK(4, 0) | |
| 127 | + | |
| 128 | +#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE BIT(0) | |
| 129 | +#define BUTTRESS_SECURITY_CTL_AUTH_DONE BIT(1) | |
| 130 | +#define BUTTRESS_SECURITY_CTL_AUTH_FAILED BIT(3) | |
| 131 | + | |
| 132 | +#define BUTTRESS_REG_FW_SOURCE_BASE_LO 0x78 | |
| 133 | +#define BUTTRESS_REG_FW_SOURCE_BASE_HI 0x7C | |
| 134 | +#define BUTTRESS_REG_FW_SOURCE_SIZE 0x80 | |
| 135 | + | |
| 136 | +#define BUTTRESS_REG_ISR_STATUS 0x90 | |
| 137 | +#define BUTTRESS_REG_ISR_ENABLED_STATUS 0x94 | |
| 138 | +#define BUTTRESS_REG_ISR_ENABLE 0x98 | |
| 139 | +#define BUTTRESS_REG_ISR_CLEAR 0x9C | |
| 140 | + | |
| 141 | +#define BUTTRESS_ISR_IS_IRQ BIT(0) | |
| 142 | +#define BUTTRESS_ISR_PS_IRQ BIT(1) | |
| 143 | +#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE BIT(2) | |
| 144 | +#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH BIT(3) | |
| 145 | +#define BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING BIT(4) | |
| 146 | +#define BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING BIT(5) | |
| 147 | +#define BUTTRESS_ISR_CSE_CSR_SET BIT(6) | |
| 148 | +#define BUTTRESS_ISR_ISH_CSR_SET BIT(7) | |
| 149 | +#define BUTTRESS_ISR_SPURIOUS_CMP BIT(8) | |
| 150 | +#define BUTTRESS_ISR_WATCHDOG_EXPIRED BIT(9) | |
| 151 | +#define BUTTRESS_ISR_PUNIT_2_IUNIT_IRQ BIT(10) | |
| 152 | +#define BUTTRESS_ISR_SAI_VIOLATION BIT(11) | |
| 153 | +#define BUTTRESS_ISR_HW_ASSERTION BIT(12) | |
| 154 | +#define BUTTRESS_ISR_IS_CORRECTABLE_MEM_ERR BIT(13) | |
| 155 | +#define BUTTRESS_ISR_IS_FATAL_MEM_ERR BIT(14) | |
| 156 | +#define BUTTRESS_ISR_IS_NON_FATAL_MEM_ERR BIT(15) | |
| 157 | +#define BUTTRESS_ISR_PS_CORRECTABLE_MEM_ERR BIT(16) | |
| 158 | +#define BUTTRESS_ISR_PS_FATAL_MEM_ERR BIT(17) | |
| 159 | +#define BUTTRESS_ISR_PS_NON_FATAL_MEM_ERR BIT(18) | |
| 160 | +#define BUTTRESS_ISR_PS_FAST_THROTTLE BIT(19) | |
| 161 | +#define BUTTRESS_ISR_UFI_ERROR BIT(20) | |
| 162 | + | |
| 163 | +#define BUTTRESS_REG_IU2CSEDB0 0x100 | |
| 164 | + | |
| 165 | +#define BUTTRESS_IU2CSEDB0_BUSY BIT(31) | |
| 166 | +#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL 2 | |
| 167 | + | |
| 168 | +#define BUTTRESS_REG_IU2CSEDATA0 0x104 | |
| 169 | + | |
| 170 | +#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD 1 | |
| 171 | +#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN 2 | |
| 172 | +#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_REPLACE 3 | |
| 173 | +#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH 16 | |
| 174 | + | |
| 175 | +#define BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE BIT(0) | |
| 176 | +#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE BIT(1) | |
| 177 | +#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_REPLACE_DONE BIT(2) | |
| 178 | +#define BUTTRESS_CSE2IUDATA0_IPC_UPDATE_SECURE_TOUCH_DONE BIT(4) | |
| 179 | + | |
| 180 | +#define BUTTRESS_REG_IU2CSECSR 0x108 | |
| 181 | + | |
| 182 | +#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 BIT(0) | |
| 183 | +#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 BIT(1) | |
| 184 | +#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE BIT(2) | |
| 185 | +#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ BIT(3) | |
| 186 | +#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID BIT(4) | |
| 187 | +#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ BIT(5) | |
| 188 | + | |
| 189 | +#define BUTTRESS_REG_CSE2IUDB0 0x304 | |
| 190 | +#define BUTTRESS_REG_CSE2IUCSR 0x30C | |
| 191 | +#define BUTTRESS_REG_CSE2IUDATA0 0x308 | |
| 192 | + | |
| 193 | +/* 0x20 == NACK, 0xf == unknown command */ | |
| 194 | +#define BUTTRESS_CSE2IUDATA0_IPC_NACK 0xf20 | |
| 195 | +#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK GENMASK(15, 0) | |
| 196 | + | |
| 197 | +#define BUTTRESS_REG_ISH2IUCSR 0x50 | |
| 198 | +#define BUTTRESS_REG_ISH2IUDB0 0x54 | |
| 199 | +#define BUTTRESS_REG_ISH2IUDATA0 0x58 | |
| 200 | + | |
| 201 | +#define BUTTRESS_REG_IU2ISHDB0 0x10C | |
| 202 | +#define BUTTRESS_REG_IU2ISHDATA0 0x110 | |
| 203 | +#define BUTTRESS_REG_IU2ISHDATA1 0x114 | |
| 204 | +#define BUTTRESS_REG_IU2ISHCSR 0x118 | |
| 205 | + | |
| 206 | +#define BUTTRESS_REG_FABRIC_CMD 0x88 | |
| 207 | + | |
| 208 | +#define BUTTRESS_FABRIC_CMD_START_TSC_SYNC BIT(0) | |
| 209 | +#define BUTTRESS_FABRIC_CMD_IS_DRAIN BIT(4) | |
| 210 | + | |
| 211 | +#define BUTTRESS_REG_TSW_CTL 0x120 | |
| 212 | +#define BUTTRESS_TSW_CTL_SOFT_RESET BIT(8) | |
| 213 | + | |
| 214 | +#define BUTTRESS_REG_TSC_LO 0x164 | |
| 215 | +#define BUTTRESS_REG_TSC_HI 0x168 | |
| 216 | + | |
| 217 | +#define BUTTRESS_IRQS (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \ | |
| 218 | + BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \ | |
| 219 | + BUTTRESS_ISR_IS_IRQ | BUTTRESS_ISR_PS_IRQ) | |
| 220 | + | |
| 221 | +#define BUTTRESS_EVENT (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \ | |
| 222 | + BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING | \ | |
| 223 | + BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \ | |
| 224 | + BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH | \ | |
| 225 | + BUTTRESS_ISR_SAI_VIOLATION) | |
| 226 | +#endif /* IPU6_PLATFORM_BUTTRESS_REGS_H */ | |
| @@ -0,0 +1,172 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2023--2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_PLATFORM_ISYS_CSI2_REG_H | |
| 5 | +#define IPU6_PLATFORM_ISYS_CSI2_REG_H | |
| 6 | + | |
| 7 | +#include <linux/bits.h> | |
| 8 | + | |
| 9 | +#define CSI_REG_BASE 0x220000 | |
| 10 | +#define CSI_REG_PORT_BASE(id) (CSI_REG_BASE + (id) * 0x1000) | |
| 11 | + | |
| 12 | +/* CSI Port Genral Purpose Registers */ | |
| 13 | +#define CSI_REG_PORT_GPREG_SRST 0x0 | |
| 14 | +#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST 0x4 | |
| 15 | +#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL 0x8 | |
| 16 | + | |
| 17 | +/* | |
| 18 | + * Port IRQs mapping events: | |
| 19 | + * IRQ0 - CSI_FE event | |
| 20 | + * IRQ1 - CSI_SYNC | |
| 21 | + * IRQ2 - S2M_SIDS0TO7 | |
| 22 | + * IRQ3 - S2M_SIDS8TO15 | |
| 23 | + */ | |
| 24 | +#define CSI_PORT_REG_BASE_IRQ_CSI 0x80 | |
| 25 | +#define CSI_PORT_REG_BASE_IRQ_CSI_SYNC 0xA0 | |
| 26 | +#define CSI_PORT_REG_BASE_IRQ_S2M_SIDS0TOS7 0xC0 | |
| 27 | +#define CSI_PORT_REG_BASE_IRQ_S2M_SIDS8TOS15 0xE0 | |
| 28 | + | |
| 29 | +#define CSI_PORT_REG_BASE_IRQ_EDGE_OFFSET 0x0 | |
| 30 | +#define CSI_PORT_REG_BASE_IRQ_MASK_OFFSET 0x4 | |
| 31 | +#define CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET 0x8 | |
| 32 | +#define CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET 0xc | |
| 33 | +#define CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET 0x10 | |
| 34 | +#define CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET 0x14 | |
| 35 | + | |
| 36 | +#define IPU6SE_CSI_RX_ERROR_IRQ_MASK GENMASK(18, 0) | |
| 37 | +#define IPU6_CSI_RX_ERROR_IRQ_MASK GENMASK(19, 0) | |
| 38 | + | |
| 39 | +#define CSI_RX_NUM_ERRORS_IN_IRQ 20 | |
| 40 | +#define CSI_RX_NUM_IRQ 32 | |
| 41 | + | |
| 42 | +#define IPU_CSI_RX_IRQ_FS_VC(chn) (1 << ((chn) * 2)) | |
| 43 | +#define IPU_CSI_RX_IRQ_FE_VC(chn) (2 << ((chn) * 2)) | |
| 44 | + | |
| 45 | +/* PPI2CSI */ | |
| 46 | +#define CSI_REG_PPI2CSI_ENABLE 0x200 | |
| 47 | +#define CSI_REG_PPI2CSI_CONFIG_PPI_INTF 0x204 | |
| 48 | +#define PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK GENMASK(4, 3) | |
| 49 | +#define CSI_REG_PPI2CSI_CONFIG_CSI_FEATURE 0x208 | |
| 50 | + | |
| 51 | +enum CSI_PPI2CSI_CTRL { | |
| 52 | + CSI_PPI2CSI_DISABLE = 0, | |
| 53 | + CSI_PPI2CSI_ENABLE = 1, | |
| 54 | +}; | |
| 55 | + | |
| 56 | +/* CSI_FE */ | |
| 57 | +#define CSI_REG_CSI_FE_ENABLE 0x280 | |
| 58 | +#define CSI_REG_CSI_FE_MODE 0x284 | |
| 59 | +#define CSI_REG_CSI_FE_MUX_CTRL 0x288 | |
| 60 | +#define CSI_REG_CSI_FE_SYNC_CNTR_SEL 0x290 | |
| 61 | + | |
| 62 | +enum CSI_FE_ENABLE_TYPE { | |
| 63 | + CSI_FE_DISABLE = 0, | |
| 64 | + CSI_FE_ENABLE = 1, | |
| 65 | +}; | |
| 66 | + | |
| 67 | +enum CSI_FE_MODE_TYPE { | |
| 68 | + CSI_FE_DPHY_MODE = 0, | |
| 69 | + CSI_FE_CPHY_MODE = 1, | |
| 70 | +}; | |
| 71 | + | |
| 72 | +enum CSI_FE_INPUT_SELECTOR { | |
| 73 | + CSI_SENSOR_INPUT = 0, | |
| 74 | + CSI_MIPIGEN_INPUT = 1, | |
| 75 | +}; | |
| 76 | + | |
| 77 | +enum CSI_FE_SYNC_CNTR_SEL_TYPE { | |
| 78 | + CSI_CNTR_SENSOR_LINE_ID = BIT(0), | |
| 79 | + CSI_CNTR_INT_LINE_PKT_ID = ~CSI_CNTR_SENSOR_LINE_ID, | |
| 80 | + CSI_CNTR_SENSOR_FRAME_ID = BIT(1), | |
| 81 | + CSI_CNTR_INT_FRAME_PKT_ID = ~CSI_CNTR_SENSOR_FRAME_ID, | |
| 82 | +}; | |
| 83 | + | |
| 84 | +/* CSI HUB General Purpose Registers */ | |
| 85 | +#define CSI_REG_HUB_GPREG_SRST (CSI_REG_BASE + 0x18000) | |
| 86 | +#define CSI_REG_HUB_GPREG_SLV_REG_SRST (CSI_REG_BASE + 0x18004) | |
| 87 | + | |
| 88 | +#define CSI_REG_HUB_DRV_ACCESS_PORT(id) (CSI_REG_BASE + 0x18018 + (id) * 4) | |
| 89 | +#define CSI_REG_HUB_FW_ACCESS_PORT_OFS 0x17000 | |
| 90 | +#define CSI_REG_HUB_FW_ACCESS_PORT_V6OFS 0x16000 | |
| 91 | +#define CSI_REG_HUB_FW_ACCESS_PORT(ofs, id) \ | |
| 92 | + (CSI_REG_BASE + (ofs) + (id) * 4) | |
| 93 | + | |
| 94 | +enum CSI_PORT_CLK_GATING_SWITCH { | |
| 95 | + CSI_PORT_CLK_GATING_OFF = 0, | |
| 96 | + CSI_PORT_CLK_GATING_ON = 1, | |
| 97 | +}; | |
| 98 | + | |
| 99 | +#define CSI_REG_BASE_HUB_IRQ 0x18200 | |
| 100 | + | |
| 101 | +#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE 0x238200 | |
| 102 | +#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK 0x238204 | |
| 103 | +#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS 0x238208 | |
| 104 | +#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR 0x23820c | |
| 105 | +#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE 0x238210 | |
| 106 | +#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE 0x238214 | |
| 107 | + | |
| 108 | +#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_EDGE 0x238220 | |
| 109 | +#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_MASK 0x238224 | |
| 110 | +#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_STATUS 0x238228 | |
| 111 | +#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_CLEAR 0x23822c | |
| 112 | +#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_ENABLE 0x238230 | |
| 113 | +#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_LEVEL_NOT_PULSE 0x238234 | |
| 114 | + | |
| 115 | +/* MTL IPU6V6 irq ctrl0 & ctrl1 */ | |
| 116 | +#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE 0x238700 | |
| 117 | +#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK 0x238704 | |
| 118 | +#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS 0x238708 | |
| 119 | +#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR 0x23870c | |
| 120 | +#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE 0x238710 | |
| 121 | +#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE 0x238714 | |
| 122 | + | |
| 123 | +#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_EDGE 0x238720 | |
| 124 | +#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_MASK 0x238724 | |
| 125 | +#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_STATUS 0x238728 | |
| 126 | +#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_CLEAR 0x23872c | |
| 127 | +#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_ENABLE 0x238730 | |
| 128 | +#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_LEVEL_NOT_PULSE 0x238734 | |
| 129 | + | |
| 130 | +/* | |
| 131 | + * 3:0 CSI_PORT.irq_out[3:0] CSI_PORT_CTRL0 IRQ outputs (4bits) | |
| 132 | + * [0] CSI_PORT.IRQ_CTRL0_csi | |
| 133 | + * [1] CSI_PORT.IRQ_CTRL1_csi_sync | |
| 134 | + * [2] CSI_PORT.IRQ_CTRL2_s2m_sids0to7 | |
| 135 | + * [3] CSI_PORT.IRQ_CTRL3_s2m_sids8to15 | |
| 136 | + */ | |
| 137 | +#define IPU6_ISYS_UNISPART_IRQ_CSI2(port) \ | |
| 138 | + (0x3 << ((port) * IPU6_CSI_IRQ_NUM_PER_PIPE)) | |
| 139 | + | |
| 140 | +/* | |
| 141 | + * ipu6se support 2 front ends, 2 port per front end, 4 ports 0..3 | |
| 142 | + * sip0 - 0, 1 | |
| 143 | + * sip1 - 2, 3 | |
| 144 | + * 0 and 2 support 4 data lanes, 1 and 3 support 2 data lanes | |
| 145 | + * all offset are base from isys base address | |
| 146 | + */ | |
| 147 | + | |
| 148 | +#define CSI2_HUB_GPREG_SIP_SRST(sip) (0x238038 + (sip) * 4) | |
| 149 | +#define CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip) (0x238050 + (sip) * 4) | |
| 150 | + | |
| 151 | +#define CSI2_HUB_GPREG_DPHY_TIMER_INCR 0x238040 | |
| 152 | +#define CSI2_HUB_GPREG_HPLL_FREQ 0x238044 | |
| 153 | +#define CSI2_HUB_GPREG_IS_CLK_RATIO 0x238048 | |
| 154 | +#define CSI2_HUB_GPREG_HPLL_FREQ_ISCLK_RATE_OVERRIDE 0x23804c | |
| 155 | +#define CSI2_HUB_GPREG_PORT_CLKGATING_DISABLE 0x238058 | |
| 156 | +#define CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL 0x23805c | |
| 157 | +#define CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL 0x238088 | |
| 158 | +#define CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL 0x2380a4 | |
| 159 | +#define CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL 0x2380d0 | |
| 160 | + | |
| 161 | +#define CSI2_SIP_TOP_CSI_RX_BASE(sip) (0x23805c + (sip) * 0x48) | |
| 162 | +#define CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port) (0x23805c + ((port) / 2) * 0x48) | |
| 163 | +#define CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port) (0x238088 + ((port) / 2) * 0x48) | |
| 164 | + | |
| 165 | +/* offset from port base */ | |
| 166 | +#define CSI2_SIP_TOP_CSI_RX_PORT_CONTROL 0x0 | |
| 167 | +#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE 0x4 | |
| 168 | +#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE 0x8 | |
| 169 | +#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(lane) (0xc + (lane) * 8) | |
| 170 | +#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(lane) (0x10 + (lane) * 8) | |
| 171 | + | |
| 172 | +#endif /* IPU6_ISYS_CSI2_REG_H */ | |
| @@ -0,0 +1,179 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (C) 2018 - 2024 Intel Corporation */ | |
| 3 | + | |
| 4 | +#ifndef IPU6_PLATFORM_REGS_H | |
| 5 | +#define IPU6_PLATFORM_REGS_H | |
| 6 | + | |
| 7 | +#include <linux/bits.h> | |
| 8 | + | |
| 9 | +/* | |
| 10 | + * IPU6 uses uniform address within IPU6, therefore all subsystem registers | |
| 11 | + * locates in one single space starts from 0 but in different sctions with | |
| 12 | + * different addresses, the subsystem offsets are defined to 0 as the | |
| 13 | + * register definition will have the address offset to 0. | |
| 14 | + */ | |
| 15 | +#define IPU6_UNIFIED_OFFSET 0 | |
| 16 | + | |
| 17 | +#define IPU6_ISYS_IOMMU0_OFFSET 0x2e0000 | |
| 18 | +#define IPU6_ISYS_IOMMU1_OFFSET 0x2e0500 | |
| 19 | +#define IPU6_ISYS_IOMMUI_OFFSET 0x2e0a00 | |
| 20 | + | |
| 21 | +#define IPU6_PSYS_IOMMU0_OFFSET 0x1b0000 | |
| 22 | +#define IPU6_PSYS_IOMMU1_OFFSET 0x1b0700 | |
| 23 | +#define IPU6_PSYS_IOMMU1R_OFFSET 0x1b0e00 | |
| 24 | +#define IPU6_PSYS_IOMMUI_OFFSET 0x1b1500 | |
| 25 | + | |
| 26 | +/* the offset from IOMMU base register */ | |
| 27 | +#define IPU6_MMU_L1_STREAM_ID_REG_OFFSET 0x0c | |
| 28 | +#define IPU6_MMU_L2_STREAM_ID_REG_OFFSET 0x4c | |
| 29 | +#define IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET 0x8c | |
| 30 | + | |
| 31 | +#define IPU6_MMU_INFO_OFFSET 0x8 | |
| 32 | + | |
| 33 | +#define IPU6_ISYS_SPC_OFFSET 0x210000 | |
| 34 | + | |
| 35 | +#define IPU6SE_PSYS_SPC_OFFSET 0x110000 | |
| 36 | +#define IPU6_PSYS_SPC_OFFSET 0x118000 | |
| 37 | + | |
| 38 | +#define IPU6_ISYS_DMEM_OFFSET 0x200000 | |
| 39 | +#define IPU6_PSYS_DMEM_OFFSET 0x100000 | |
| 40 | + | |
| 41 | +#define IPU6_REG_ISYS_UNISPART_IRQ_EDGE 0x27c000 | |
| 42 | +#define IPU6_REG_ISYS_UNISPART_IRQ_MASK 0x27c004 | |
| 43 | +#define IPU6_REG_ISYS_UNISPART_IRQ_STATUS 0x27c008 | |
| 44 | +#define IPU6_REG_ISYS_UNISPART_IRQ_CLEAR 0x27c00c | |
| 45 | +#define IPU6_REG_ISYS_UNISPART_IRQ_ENABLE 0x27c010 | |
| 46 | +#define IPU6_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE 0x27c014 | |
| 47 | +#define IPU6_REG_ISYS_UNISPART_SW_IRQ_REG 0x27c414 | |
| 48 | +#define IPU6_REG_ISYS_UNISPART_SW_IRQ_MUX_REG 0x27c418 | |
| 49 | +#define IPU6_ISYS_UNISPART_IRQ_CSI0 BIT(2) | |
| 50 | +#define IPU6_ISYS_UNISPART_IRQ_CSI1 BIT(3) | |
| 51 | +#define IPU6_ISYS_UNISPART_IRQ_SW BIT(22) | |
| 52 | + | |
| 53 | +#define IPU6_REG_ISYS_ISL_TOP_IRQ_EDGE 0x2b0200 | |
| 54 | +#define IPU6_REG_ISYS_ISL_TOP_IRQ_MASK 0x2b0204 | |
| 55 | +#define IPU6_REG_ISYS_ISL_TOP_IRQ_STATUS 0x2b0208 | |
| 56 | +#define IPU6_REG_ISYS_ISL_TOP_IRQ_CLEAR 0x2b020c | |
| 57 | +#define IPU6_REG_ISYS_ISL_TOP_IRQ_ENABLE 0x2b0210 | |
| 58 | +#define IPU6_REG_ISYS_ISL_TOP_IRQ_LEVEL_NOT_PULSE 0x2b0214 | |
| 59 | + | |
| 60 | +#define IPU6_REG_ISYS_CMPR_TOP_IRQ_EDGE 0x2d2100 | |
| 61 | +#define IPU6_REG_ISYS_CMPR_TOP_IRQ_MASK 0x2d2104 | |
| 62 | +#define IPU6_REG_ISYS_CMPR_TOP_IRQ_STATUS 0x2d2108 | |
| 63 | +#define IPU6_REG_ISYS_CMPR_TOP_IRQ_CLEAR 0x2d210c | |
| 64 | +#define IPU6_REG_ISYS_CMPR_TOP_IRQ_ENABLE 0x2d2110 | |
| 65 | +#define IPU6_REG_ISYS_CMPR_TOP_IRQ_LEVEL_NOT_PULSE 0x2d2114 | |
| 66 | + | |
| 67 | +/* CDC Burst collector thresholds for isys - 3 FIFOs i = 0..2 */ | |
| 68 | +#define IPU6_REG_ISYS_CDC_THRESHOLD(i) (0x27c400 + ((i) * 4)) | |
| 69 | + | |
| 70 | +#define IPU6_CSI_IRQ_NUM_PER_PIPE 4 | |
| 71 | +#define IPU6SE_ISYS_CSI_PORT_NUM 4 | |
| 72 | +#define IPU6_ISYS_CSI_PORT_NUM 8 | |
| 73 | + | |
| 74 | +#define IPU6_ISYS_CSI_PORT_IRQ(irq_num) BIT(irq_num) | |
| 75 | + | |
| 76 | +/* PKG DIR OFFSET in IMR in secure mode */ | |
| 77 | +#define IPU6_PKG_DIR_IMR_OFFSET 0x40 | |
| 78 | + | |
| 79 | +#define IPU6_ISYS_REG_SPC_STATUS_CTRL 0x0 | |
| 80 | + | |
| 81 | +#define IPU6_ISYS_SPC_STATUS_START BIT(1) | |
| 82 | +#define IPU6_ISYS_SPC_STATUS_RUN BIT(3) | |
| 83 | +#define IPU6_ISYS_SPC_STATUS_READY BIT(5) | |
| 84 | +#define IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE BIT(12) | |
| 85 | +#define IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH BIT(13) | |
| 86 | + | |
| 87 | +#define IPU6_PSYS_REG_SPC_STATUS_CTRL 0x0 | |
| 88 | +#define IPU6_PSYS_REG_SPC_START_PC 0x4 | |
| 89 | +#define IPU6_PSYS_REG_SPC_ICACHE_BASE 0x10 | |
| 90 | +#define IPU6_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER 0x14 | |
| 91 | + | |
| 92 | +#define IPU6_PSYS_SPC_STATUS_START BIT(1) | |
| 93 | +#define IPU6_PSYS_SPC_STATUS_RUN BIT(3) | |
| 94 | +#define IPU6_PSYS_SPC_STATUS_READY BIT(5) | |
| 95 | +#define IPU6_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE BIT(12) | |
| 96 | +#define IPU6_PSYS_SPC_STATUS_ICACHE_PREFETCH BIT(13) | |
| 97 | + | |
| 98 | +#define IPU6_PSYS_REG_SPP0_STATUS_CTRL 0x20000 | |
| 99 | + | |
| 100 | +#define IPU6_INFO_ENABLE_SNOOP BIT(0) | |
| 101 | +#define IPU6_INFO_DEC_FORCE_FLUSH BIT(1) | |
| 102 | +#define IPU6_INFO_DEC_PASS_THROUGH BIT(2) | |
| 103 | +#define IPU6_INFO_ZLW BIT(3) | |
| 104 | +#define IPU6_INFO_REQUEST_DESTINATION_IOSF BIT(9) | |
| 105 | +#define IPU6_INFO_IMR_BASE BIT(10) | |
| 106 | +#define IPU6_INFO_IMR_DESTINED BIT(11) | |
| 107 | + | |
| 108 | +#define IPU6_INFO_REQUEST_DESTINATION_PRIMARY IPU6_INFO_REQUEST_DESTINATION_IOSF | |
| 109 | + | |
| 110 | +/* | |
| 111 | + * s2m_pixel_soc_pixel_remapping is dedicated for the enabling of the | |
| 112 | + * pixel s2m remp ability.Remap here means that s2m rearange the order | |
| 113 | + * of the pixels in each 4 pixels group. | |
| 114 | + * For examle, mirroring remping means that if input's 4 first pixels | |
| 115 | + * are 1 2 3 4 then in output we should see 4 3 2 1 in this 4 first pixels. | |
| 116 | + * 0xE4 is from s2m MAS document. It means no remapping. | |
| 117 | + */ | |
| 118 | +#define S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING 0xe4 | |
| 119 | +/* | |
| 120 | + * csi_be_soc_pixel_remapping is for the enabling of the pixel remapping. | |
| 121 | + * This remapping is exactly like the stream2mmio remapping. | |
| 122 | + */ | |
| 123 | +#define CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING 0xe4 | |
| 124 | + | |
| 125 | +#define IPU6_REG_DMA_TOP_AB_GROUP1_BASE_ADDR 0x1ae000 | |
| 126 | +#define IPU6_REG_DMA_TOP_AB_GROUP2_BASE_ADDR 0x1af000 | |
| 127 | +#define IPU6_REG_DMA_TOP_AB_RING_MIN_OFFSET(n) (0x4 + (n) * 0xc) | |
| 128 | +#define IPU6_REG_DMA_TOP_AB_RING_MAX_OFFSET(n) (0x8 + (n) * 0xc) | |
| 129 | +#define IPU6_REG_DMA_TOP_AB_RING_ACCESS_OFFSET(n) (0xc + (n) * 0xc) | |
| 130 | + | |
| 131 | +enum ipu6_device_ab_group1_target_id { | |
| 132 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R0_SPC_DMEM, | |
| 133 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R1_SPC_DMEM, | |
| 134 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R2_SPC_DMEM, | |
| 135 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R3_SPC_STATUS_REG, | |
| 136 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R4_SPC_MASTER_BASE_ADDR, | |
| 137 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R5_SPC_PC_STALL, | |
| 138 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R6_SPC_EQ, | |
| 139 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R7_SPC_RESERVED, | |
| 140 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R8_SPC_RESERVED, | |
| 141 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R9_SPP0, | |
| 142 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R10_SPP1, | |
| 143 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R11_CENTRAL_R1, | |
| 144 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R12_IRQ, | |
| 145 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R13_CENTRAL_R2, | |
| 146 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R14_DMA, | |
| 147 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R15_DMA, | |
| 148 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R16_GP, | |
| 149 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R17_ZLW_INSERTER, | |
| 150 | + IPU6_DEVICE_AB_GROUP1_TARGET_ID_R18_AB, | |
| 151 | +}; | |
| 152 | + | |
| 153 | +enum nci_ab_access_mode { | |
| 154 | + NCI_AB_ACCESS_MODE_RW, /* read & write */ | |
| 155 | + NCI_AB_ACCESS_MODE_RO, /* read only */ | |
| 156 | + NCI_AB_ACCESS_MODE_WO, /* write only */ | |
| 157 | + NCI_AB_ACCESS_MODE_NA, /* No access at all */ | |
| 158 | +}; | |
| 159 | + | |
| 160 | +/* IRQ-related registers in PSYS */ | |
| 161 | +#define IPU6_REG_PSYS_GPDEV_IRQ_EDGE 0x1aa200 | |
| 162 | +#define IPU6_REG_PSYS_GPDEV_IRQ_MASK 0x1aa204 | |
| 163 | +#define IPU6_REG_PSYS_GPDEV_IRQ_STATUS 0x1aa208 | |
| 164 | +#define IPU6_REG_PSYS_GPDEV_IRQ_CLEAR 0x1aa20c | |
| 165 | +#define IPU6_REG_PSYS_GPDEV_IRQ_ENABLE 0x1aa210 | |
| 166 | +#define IPU6_REG_PSYS_GPDEV_IRQ_LEVEL_NOT_PULSE 0x1aa214 | |
| 167 | +/* There are 8 FW interrupts, n = 0..7 */ | |
| 168 | +#define IPU6_PSYS_GPDEV_FWIRQ0 5 | |
| 169 | +#define IPU6_PSYS_GPDEV_FWIRQ1 6 | |
| 170 | +#define IPU6_PSYS_GPDEV_FWIRQ2 7 | |
| 171 | +#define IPU6_PSYS_GPDEV_FWIRQ3 8 | |
| 172 | +#define IPU6_PSYS_GPDEV_FWIRQ4 9 | |
| 173 | +#define IPU6_PSYS_GPDEV_FWIRQ5 10 | |
| 174 | +#define IPU6_PSYS_GPDEV_FWIRQ6 11 | |
| 175 | +#define IPU6_PSYS_GPDEV_FWIRQ7 12 | |
| 176 | +#define IPU6_PSYS_GPDEV_IRQ_FWIRQ(n) BIT(n) | |
| 177 | +#define IPU6_REG_PSYS_GPDEV_FWIRQ(n) (4 * (n) + 0x1aa100) | |
| 178 | + | |
| 179 | +#endif /* IPU6_PLATFORM_REGS_H */ | |
| @@ -0,0 +1,856 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2013--2024 Intel Corporation | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/bitfield.h> | |
| 7 | +#include <linux/bits.h> | |
| 8 | +#include <linux/dma-mapping.h> | |
| 9 | +#include <linux/err.h> | |
| 10 | +#include <linux/firmware.h> | |
| 11 | +#include <linux/kernel.h> | |
| 12 | +#include <linux/interrupt.h> | |
| 13 | +#include <linux/io.h> | |
| 14 | +#include <linux/list.h> | |
| 15 | +#include <linux/module.h> | |
| 16 | +#include <linux/pci-ats.h> | |
| 17 | +#include <linux/pm_runtime.h> | |
| 18 | +#include <linux/property.h> | |
| 19 | +#include <linux/scatterlist.h> | |
| 20 | +#include <linux/slab.h> | |
| 21 | +#include <linux/types.h> | |
| 22 | + | |
| 23 | +#include <media/ipu-bridge.h> | |
| 24 | +#include <media/ipu6-pci-table.h> | |
| 25 | + | |
| 26 | +#include "ipu6.h" | |
| 27 | +#include "ipu6-bus.h" | |
| 28 | +#include "ipu6-buttress.h" | |
| 29 | +#include "ipu6-cpd.h" | |
| 30 | +#include "ipu6-isys.h" | |
| 31 | +#include "ipu6-mmu.h" | |
| 32 | +#include "ipu6-platform-buttress-regs.h" | |
| 33 | +#include "ipu6-platform-isys-csi2-reg.h" | |
| 34 | +#include "ipu6-platform-regs.h" | |
| 35 | + | |
| 36 | +#define IPU6_PCI_BAR 0 | |
| 37 | + | |
| 38 | +struct ipu6_cell_program { | |
| 39 | + u32 magic_number; | |
| 40 | + | |
| 41 | + u32 blob_offset; | |
| 42 | + u32 blob_size; | |
| 43 | + | |
| 44 | + u32 start[3]; | |
| 45 | + | |
| 46 | + u32 icache_source; | |
| 47 | + u32 icache_target; | |
| 48 | + u32 icache_size; | |
| 49 | + | |
| 50 | + u32 pmem_source; | |
| 51 | + u32 pmem_target; | |
| 52 | + u32 pmem_size; | |
| 53 | + | |
| 54 | + u32 data_source; | |
| 55 | + u32 data_target; | |
| 56 | + u32 data_size; | |
| 57 | + | |
| 58 | + u32 bss_target; | |
| 59 | + u32 bss_size; | |
| 60 | + | |
| 61 | + u32 cell_id; | |
| 62 | + u32 regs_addr; | |
| 63 | + | |
| 64 | + u32 cell_pmem_data_bus_address; | |
| 65 | + u32 cell_dmem_data_bus_address; | |
| 66 | + u32 cell_pmem_control_bus_address; | |
| 67 | + u32 cell_dmem_control_bus_address; | |
| 68 | + | |
| 69 | + u32 next; | |
| 70 | + u32 dummy[2]; | |
| 71 | +}; | |
| 72 | + | |
| 73 | +static struct ipu6_isys_internal_pdata isys_ipdata = { | |
| 74 | + .hw_variant = { | |
| 75 | + .offset = IPU6_UNIFIED_OFFSET, | |
| 76 | + .nr_mmus = 3, | |
| 77 | + .mmu_hw = { | |
| 78 | + { | |
| 79 | + .offset = IPU6_ISYS_IOMMU0_OFFSET, | |
| 80 | + .info_bits = IPU6_INFO_REQUEST_DESTINATION_IOSF, | |
| 81 | + .nr_l1streams = 16, | |
| 82 | + .l1_block_sz = { | |
| 83 | + 3, 8, 2, 2, 2, 2, 2, 2, 1, 1, | |
| 84 | + 1, 1, 1, 1, 1, 1 | |
| 85 | + }, | |
| 86 | + .nr_l2streams = 16, | |
| 87 | + .l2_block_sz = { | |
| 88 | + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
| 89 | + 2, 2, 2, 2, 2, 2 | |
| 90 | + }, | |
| 91 | + .insert_read_before_invalidate = false, | |
| 92 | + .l1_stream_id_reg_offset = | |
| 93 | + IPU6_MMU_L1_STREAM_ID_REG_OFFSET, | |
| 94 | + .l2_stream_id_reg_offset = | |
| 95 | + IPU6_MMU_L2_STREAM_ID_REG_OFFSET, | |
| 96 | + }, | |
| 97 | + { | |
| 98 | + .offset = IPU6_ISYS_IOMMU1_OFFSET, | |
| 99 | + .info_bits = 0, | |
| 100 | + .nr_l1streams = 16, | |
| 101 | + .l1_block_sz = { | |
| 102 | + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
| 103 | + 2, 2, 2, 1, 1, 4 | |
| 104 | + }, | |
| 105 | + .nr_l2streams = 16, | |
| 106 | + .l2_block_sz = { | |
| 107 | + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
| 108 | + 2, 2, 2, 2, 2, 2 | |
| 109 | + }, | |
| 110 | + .insert_read_before_invalidate = false, | |
| 111 | + .l1_stream_id_reg_offset = | |
| 112 | + IPU6_MMU_L1_STREAM_ID_REG_OFFSET, | |
| 113 | + .l2_stream_id_reg_offset = | |
| 114 | + IPU6_MMU_L2_STREAM_ID_REG_OFFSET, | |
| 115 | + }, | |
| 116 | + { | |
| 117 | + .offset = IPU6_ISYS_IOMMUI_OFFSET, | |
| 118 | + .info_bits = 0, | |
| 119 | + .nr_l1streams = 0, | |
| 120 | + .nr_l2streams = 0, | |
| 121 | + .insert_read_before_invalidate = false, | |
| 122 | + }, | |
| 123 | + }, | |
| 124 | + .cdc_fifos = 3, | |
| 125 | + .cdc_fifo_threshold = {6, 8, 2}, | |
| 126 | + .dmem_offset = IPU6_ISYS_DMEM_OFFSET, | |
| 127 | + .spc_offset = IPU6_ISYS_SPC_OFFSET, | |
| 128 | + }, | |
| 129 | + .isys_dma_overshoot = IPU6_ISYS_OVERALLOC_MIN, | |
| 130 | +}; | |
| 131 | + | |
| 132 | +static struct ipu6_psys_internal_pdata psys_ipdata = { | |
| 133 | + .hw_variant = { | |
| 134 | + .offset = IPU6_UNIFIED_OFFSET, | |
| 135 | + .nr_mmus = 4, | |
| 136 | + .mmu_hw = { | |
| 137 | + { | |
| 138 | + .offset = IPU6_PSYS_IOMMU0_OFFSET, | |
| 139 | + .info_bits = | |
| 140 | + IPU6_INFO_REQUEST_DESTINATION_IOSF, | |
| 141 | + .nr_l1streams = 16, | |
| 142 | + .l1_block_sz = { | |
| 143 | + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
| 144 | + 2, 2, 2, 2, 2, 2 | |
| 145 | + }, | |
| 146 | + .nr_l2streams = 16, | |
| 147 | + .l2_block_sz = { | |
| 148 | + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
| 149 | + 2, 2, 2, 2, 2, 2 | |
| 150 | + }, | |
| 151 | + .insert_read_before_invalidate = false, | |
| 152 | + .l1_stream_id_reg_offset = | |
| 153 | + IPU6_MMU_L1_STREAM_ID_REG_OFFSET, | |
| 154 | + .l2_stream_id_reg_offset = | |
| 155 | + IPU6_MMU_L2_STREAM_ID_REG_OFFSET, | |
| 156 | + }, | |
| 157 | + { | |
| 158 | + .offset = IPU6_PSYS_IOMMU1_OFFSET, | |
| 159 | + .info_bits = 0, | |
| 160 | + .nr_l1streams = 32, | |
| 161 | + .l1_block_sz = { | |
| 162 | + 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
| 163 | + 2, 2, 2, 2, 2, 10, | |
| 164 | + 5, 4, 14, 6, 4, 14, 6, 4, 8, | |
| 165 | + 4, 2, 1, 1, 1, 1, 14 | |
| 166 | + }, | |
| 167 | + .nr_l2streams = 32, | |
| 168 | + .l2_block_sz = { | |
| 169 | + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
| 170 | + 2, 2, 2, 2, 2, 2, | |
| 171 | + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
| 172 | + 2, 2, 2, 2, 2, 2 | |
| 173 | + }, | |
| 174 | + .insert_read_before_invalidate = false, | |
| 175 | + .l1_stream_id_reg_offset = | |
| 176 | + IPU6_MMU_L1_STREAM_ID_REG_OFFSET, | |
| 177 | + .l2_stream_id_reg_offset = | |
| 178 | + IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET, | |
| 179 | + }, | |
| 180 | + { | |
| 181 | + .offset = IPU6_PSYS_IOMMU1R_OFFSET, | |
| 182 | + .info_bits = 0, | |
| 183 | + .nr_l1streams = 16, | |
| 184 | + .l1_block_sz = { | |
| 185 | + 1, 4, 4, 4, 4, 16, 8, 4, 32, | |
| 186 | + 16, 16, 2, 2, 2, 1, 12 | |
| 187 | + }, | |
| 188 | + .nr_l2streams = 16, | |
| 189 | + .l2_block_sz = { | |
| 190 | + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
| 191 | + 2, 2, 2, 2, 2, 2 | |
| 192 | + }, | |
664 more lines aren’t shown here.
This diff is large: its 343 lines aren’t shown here.
This diff is large: its 50 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 41 lines aren’t shown here.
This diff is large: its 850 lines aren’t shown here.
This diff is large: its 674 lines aren’t shown here.
This diff is large: its 150 lines aren’t shown here.
This diff is large: its 476 lines aren’t shown here.
This diff is large: its 52 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 4 lines aren’t shown here.
This diff is large: its 247 lines aren’t shown here.
This diff is large: its 2740 lines aren’t shown here.
This diff is large: its 54 lines aren’t shown here.
This diff is large: its 36 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 176 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 39 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 59 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 102 lines aren’t shown here.
This diff is large: its 54 lines aren’t shown here.
This diff is large: its 44 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 15 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 71 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 141 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
File renamed without changes.
This diff is large: its 53 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 327 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 110 lines aren’t shown here.
This diff is large: its 49 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 70 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 44 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 82 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 52 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 40 lines aren’t shown here.
This diff is large: its 1080 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 79 lines aren’t shown here.
This diff is large: its 77 lines aren’t shown here.
This diff is large: its 61 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 90 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 107 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 67 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 41 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 171 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 208 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 956 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 130 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 75 lines aren’t shown here.
This diff is large: its 133 lines aren’t shown here.
This diff is large: its 442 lines aren’t shown here.
This diff is large: its 34 lines aren’t shown here.
This diff is large: its 50 lines aren’t shown here.
This diff is large: its 50 lines aren’t shown here.
This diff is large: its 94 lines aren’t shown here.
This diff is large: its 283 lines aren’t shown here.
This diff is large: its 77 lines aren’t shown here.
This diff is large: its 728 lines aren’t shown here.
This diff is large: its 178 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 165 lines aren’t shown here.
This diff is large: its 40 lines aren’t shown here.
This diff is large: its 350 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 38 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 59 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 80 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 41 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 56 lines aren’t shown here.
This diff is large: its 238 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 270 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 40 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 46 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 118 lines aren’t shown here.
This diff is large: its 145 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 34 lines aren’t shown here.
This diff is large: its 105 lines aren’t shown here.