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 | .. SPDX-License-Identifier: GPL-2.0 | 1 | .. SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | ||
| 3 | ==================== | 3 | The mgb4 driver |
| 4 | mgb4 sysfs interface | 4 | =============== |
| 5 | ==================== | 5 | |
| 6 | sysfs interface | ||
| 7 | --------------- | ||
| 6 | 8 | ||
| 7 | The mgb4 driver provides a sysfs interface, that is used to configure video | 9 | The mgb4 driver provides a sysfs interface, that is used to configure video |
| 8 | stream related parameters (some of them must be set properly before the v4l2 | 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 | ``/sys/class/video4linux/videoX/device`` and module specific found under | 14 | ``/sys/class/video4linux/videoX/device`` and module specific found under |
| 13 | ``/sys/class/video4linux/videoX``. | 15 | ``/sys/class/video4linux/videoX``. |
| 14 | 16 | ||
| 15 | |||
| 16 | Global (PCI card) parameters | 17 | Global (PCI card) parameters |
| 17 | ============================ | 18 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 18 | 19 | ||
| 19 | **module_type** (R): | 20 | **module_type** (R): |
| 20 | Module type. | 21 | Module type. |
| @@ -42,9 +43,8 @@ Global (PCI card) parameters · 21 unmodified lines | |||
| 42 | 43 | ||
| 43 | where each component is a 8b number. | 44 | where each component is a 8b number. |
| 44 | 45 | ||
| 45 | |||
| 46 | Common FPDL3/GMSL input parameters | 46 | Common FPDL3/GMSL input parameters |
| 47 | ================================== | 47 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 48 | 48 | ||
| 49 | **input_id** (R): | 49 | **input_id** (R): |
| 50 | Input number ID, zero based. | 50 | Input number ID, zero based. |
| @@ -190,9 +190,8 @@ Common FPDL3/GMSL input parameters · 139 unmodified lines | |||
| 190 | *Note: This parameter can not be changed while the input v4l2 device is | 190 | *Note: This parameter can not be changed while the input v4l2 device is |
| 191 | open.* | 191 | open.* |
| 192 | 192 | ||
| 193 | |||
| 194 | Common FPDL3/GMSL output parameters | 193 | Common FPDL3/GMSL output parameters |
| 195 | =================================== | 194 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 196 | 195 | ||
| 197 | **output_id** (R): | 196 | **output_id** (R): |
| 198 | Output number ID, zero based. | 197 | Output number ID, zero based. |
| @@ -282,9 +281,8 @@ Common FPDL3/GMSL output parameters · 83 unmodified lines | |||
| 282 | Number of video lines between the end of the last valid pixel line (marked | 281 | Number of video lines between the end of the last valid pixel line (marked |
| 283 | by DE=1) and assertion of the VSYNC signal. The default value is 2. | 282 | by DE=1) and assertion of the VSYNC signal. The default value is 2. |
| 284 | 283 | ||
| 285 | |||
| 286 | FPDL3 specific input parameters | 284 | FPDL3 specific input parameters |
| 287 | =============================== | 285 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 288 | 286 | ||
| 289 | **fpdl3_input_width** (RW): | 287 | **fpdl3_input_width** (RW): |
| 290 | Number of deserializer input lines. | 288 | Number of deserializer input lines. |
| @@ -294,7 +292,7 @@ FPDL3 specific input parameters · 3 unmodified lines | |||
| 294 | | 2 - dual | 292 | | 2 - dual |
| 295 | 293 | ||
| 296 | FPDL3 specific output parameters | 294 | FPDL3 specific output parameters |
| 297 | ================================ | 295 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 298 | 296 | ||
| 299 | **fpdl3_output_width** (RW): | 297 | **fpdl3_output_width** (RW): |
| 300 | Number of serializer output lines. | 298 | Number of serializer output lines. |
| @@ -304,7 +302,7 @@ FPDL3 specific output parameters · 3 unmodified lines | |||
| 304 | | 2 - dual | 302 | | 2 - dual |
| 305 | 303 | ||
| 306 | GMSL specific input parameters | 304 | GMSL specific input parameters |
| 307 | ============================== | 305 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 308 | 306 | ||
| 309 | **gmsl_mode** (RW): | 307 | **gmsl_mode** (RW): |
| 310 | GMSL speed mode. | 308 | GMSL speed mode. |
| @@ -328,10 +326,8 @@ GMSL specific input parameters · 17 unmodified lines | |||
| 328 | | 0 - disabled | 326 | | 0 - disabled |
| 329 | | 1 - enabled (default) | 327 | | 1 - enabled (default) |
| 330 | 328 | ||
| 331 | 329 | MTD partitions | |
| 332 | ==================== | 330 | -------------- |
| 333 | mgb4 mtd partitions | ||
| 334 | ==================== | ||
| 335 | 331 | ||
| 336 | The mgb4 driver creates a MTD device with two partitions: | 332 | The mgb4 driver creates a MTD device with two partitions: |
| 337 | - mgb4-fw.X - FPGA firmware. | 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 | partition represents the whole, unpartitioned, card's FLASH memory and one should | 340 | partition represents the whole, unpartitioned, card's FLASH memory and one should |
| 345 | not fiddle with it... | 341 | not fiddle with it... |
| 346 | 342 | ||
| 347 | ==================== | 343 | IIO (triggers) |
| 348 | mgb4 iio (triggers) | 344 | -------------- |
| 349 | ==================== | ||
| 350 | 345 | ||
| 351 | The mgb4 driver creates an Industrial I/O (IIO) device that provides trigger and | 346 | The mgb4 driver creates an Industrial I/O (IIO) device that provides trigger and |
| 352 | signal level status capability. The following scan elements are available: | 347 | signal level status capability. The following scan elements are available: |
| @@ -16,6 +16,7 @@ Video4Linux (V4L) driver-specific documentation · 15 unmodified lines | |||
| 16 | imx | 16 | imx |
| 17 | imx7 | 17 | imx7 |
| 18 | ipu3 | 18 | ipu3 |
| 19 | ipu6-isys | ||
| 19 | ivtv | 20 | ivtv |
| 20 | mgb4 | 21 | mgb4 |
| 21 | omap3isp | 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 | They include an ISP capable of auto exposure and auto white balance. | 15 | They include an ISP capable of auto exposure and auto white balance. |
| 16 | 16 | ||
| 17 | allOf: | 17 | allOf: |
| 18 | - $ref: ../video-interface-devices.yaml# | 18 | - $ref: /schemas/media/video-interface-devices.yaml# |
| 19 | 19 | ||
| 20 | properties: | 20 | properties: |
| 21 | compatible: | 21 | compatible: |
| @@ -19,7 +19,7 @@ description: · 18 unmodified lines | |||
| 19 | either through a parallel interface or through MIPI CSI-2. | 19 | either through a parallel interface or through MIPI CSI-2. |
| 20 | 20 | ||
| 21 | allOf: | 21 | allOf: |
| 22 | - $ref: ../video-interface-devices.yaml# | 22 | - $ref: /schemas/media/video-interface-devices.yaml# |
| 23 | 23 | ||
| 24 | properties: | 24 | properties: |
| 25 | compatible: | 25 | compatible: |
| @@ -37,31 +37,45 @@ properties: · 36 unmodified lines | |||
| 37 | active low. | 37 | active low. |
| 38 | maxItems: 1 | 38 | maxItems: 1 |
| 39 | 39 | ||
| 40 | dovdd-supply: | 40 | DOVDD-supply: |
| 41 | description: | 41 | description: |
| 42 | Definition of the regulator used as interface power supply. | 42 | Definition of the regulator used as interface power supply. |
| 43 | 43 | ||
| 44 | avdd-supply: | 44 | AVDD-supply: |
| 45 | description: | 45 | description: |
| 46 | Definition of the regulator used as analog power supply. | 46 | Definition of the regulator used as analog power supply. |
| 47 | 47 | ||
| 48 | dvdd-supply: | 48 | DVDD-supply: |
| 49 | description: | 49 | description: |
| 50 | Definition of the regulator used as digital power supply. | 50 | Definition of the regulator used as digital power supply. |
| 51 | 51 | ||
| 52 | port: | 52 | port: |
| 53 | $ref: /schemas/graph.yaml#/properties/port | ||
| 54 | description: | 53 | description: |
| 55 | A node containing an output port node. | 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 | required: | 71 | required: |
| 58 | - compatible | 72 | - compatible |
| 59 | - reg | 73 | - reg |
| 60 | - clocks | 74 | - clocks |
| 61 | - clock-names | 75 | - clock-names |
| 62 | - dovdd-supply | 76 | - DOVDD-supply |
| 63 | - avdd-supply | 77 | - AVDD-supply |
| 64 | - dvdd-supply | 78 | - DVDD-supply |
| 65 | - reset-gpios | 79 | - reset-gpios |
| 66 | - port | 80 | - port |
| 67 | 81 | ||
| @@ -82,13 +96,14 @@ examples: · 14 unmodified lines | |||
| 82 | clock-names = "xvclk"; | 96 | clock-names = "xvclk"; |
| 83 | reset-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; | 97 | reset-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; |
| 84 | 98 | ||
| 85 | dovdd-supply = <&sw2_reg>; | 99 | DOVDD-supply = <&sw2_reg>; |
| 86 | dvdd-supply = <&sw2_reg>; | 100 | DVDD-supply = <&sw2_reg>; |
| 87 | avdd-supply = <®_peri_3p15v>; | 101 | AVDD-supply = <®_peri_3p15v>; |
| 88 | 102 | ||
| 89 | port { | 103 | port { |
| 90 | ov2680_to_mipi: endpoint { | 104 | ov2680_to_mipi: endpoint { |
| 91 | remote-endpoint = <&mipi_from_sensor>; | 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 | # Copyright (c) 2019 MediaTek Inc. | 2 | # Copyright (c) 2019 MediaTek Inc. |
| 3 | %YAML 1.2 | 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 | $schema: http://devicetree.org/meta-schemas/core.yaml# | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Omnivision OV8856 CMOS Sensor | 8 | title: Omnivision OV8856 CMOS Sensor |
| @@ -16,7 +16,7 @@ description: | · 15 unmodified lines | |||
| 16 | maximum throughput of 1.2Gbps/lane. | 16 | maximum throughput of 1.2Gbps/lane. |
| 17 | 17 | ||
| 18 | allOf: | 18 | allOf: |
| 19 | - $ref: ../video-interface-devices.yaml# | 19 | - $ref: /schemas/media/video-interface-devices.yaml# |
| 20 | 20 | ||
| 21 | properties: | 21 | properties: |
| 22 | compatible: | 22 | compatible: |
| @@ -23,6 +23,9 @@ description: |- · 22 unmodified lines | |||
| 23 | is treated the same as this as it was the original compatible string. | 23 | is treated the same as this as it was the original compatible string. |
| 24 | imx290llr is the mono version of the sensor. | 24 | imx290llr is the mono version of the sensor. |
| 25 | 25 | ||
| 26 | allOf: | ||
| 27 | - $ref: /schemas/media/video-interface-devices.yaml# | ||
| 28 | |||
| 26 | properties: | 29 | properties: |
| 27 | compatible: | 30 | compatible: |
| 28 | oneOf: | 31 | oneOf: |
| @@ -101,7 +104,7 @@ required: · 72 unmodified lines | |||
| 101 | - vdddo-supply | 104 | - vdddo-supply |
| 102 | - port | 105 | - port |
| 103 | 106 | ||
| 104 | additionalProperties: false | 107 | unevaluatedProperties: false |
| 105 | 108 | ||
| 106 | examples: | 109 | examples: |
| 107 | - | | 110 | - | |
| @@ -18,7 +18,7 @@ description: |- · 17 unmodified lines | |||
| 18 | available via CSI-2 serial data output (two or four lanes). | 18 | available via CSI-2 serial data output (two or four lanes). |
| 19 | 19 | ||
| 20 | allOf: | 20 | allOf: |
| 21 | - $ref: ../video-interface-devices.yaml# | 21 | - $ref: /schemas/media/video-interface-devices.yaml# |
| 22 | 22 | ||
| 23 | properties: | 23 | properties: |
| 24 | compatible: | 24 | compatible: |
| @@ -84,6 +84,7 @@ allOf: · 83 unmodified lines | |||
| 84 | properties: | 84 | properties: |
| 85 | port@0: | 85 | port@0: |
| 86 | description: MIPI CSI-2 RX | 86 | description: MIPI CSI-2 RX |
| 87 | port@1: false | ||
| 87 | required: | 88 | required: |
| 88 | - port@0 | 89 | - port@0 |
| 89 | 90 | ||
| @@ -31,6 +31,11 @@ properties: · 30 unmodified lines | |||
| 31 | reg: | 31 | reg: |
| 32 | maxItems: 1 | 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 | interrupts: | 39 | interrupts: |
| 35 | description: | | 40 | description: | |
| 36 | There are 4 slots available in the IP, which the driver may use | 41 | There are 4 slots available in the IP, which the driver may use |
| @@ -49,6 +54,7 @@ properties: · 12 unmodified lines | |||
| 49 | required: | 54 | required: |
| 50 | - compatible | 55 | - compatible |
| 51 | - reg | 56 | - reg |
| 57 | - clocks | ||
| 52 | - interrupts | 58 | - interrupts |
| 53 | - power-domains | 59 | - power-domains |
| 54 | 60 | ||
| @@ -56,12 +62,15 @@ additionalProperties: false · 1 unmodified line | |||
| 56 | 62 | ||
| 57 | examples: | 63 | examples: |
| 58 | - | | 64 | - | |
| 65 | #include <dt-bindings/clock/imx8-lpcg.h> | ||
| 59 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 66 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| 60 | #include <dt-bindings/firmware/imx/rsrc.h> | 67 | #include <dt-bindings/firmware/imx/rsrc.h> |
| 61 | 68 | ||
| 62 | jpegdec: jpegdec@58400000 { | 69 | jpegdec: jpegdec@58400000 { |
| 63 | compatible = "nxp,imx8qxp-jpgdec"; | 70 | compatible = "nxp,imx8qxp-jpgdec"; |
| 64 | reg = <0x58400000 0x00050000 >; | 71 | reg = <0x58400000 0x00050000 >; |
| 72 | clocks = <&img_jpeg_dec_lpcg IMX_LPCG_CLK_0>, | ||
| 73 | <&img_jpeg_dec_lpcg IMX_LPCG_CLK_4>; | ||
| 65 | interrupts = <GIC_SPI 309 IRQ_TYPE_LEVEL_HIGH>, | 74 | interrupts = <GIC_SPI 309 IRQ_TYPE_LEVEL_HIGH>, |
| 66 | <GIC_SPI 310 IRQ_TYPE_LEVEL_HIGH>, | 75 | <GIC_SPI 310 IRQ_TYPE_LEVEL_HIGH>, |
| 67 | <GIC_SPI 311 IRQ_TYPE_LEVEL_HIGH>, | 76 | <GIC_SPI 311 IRQ_TYPE_LEVEL_HIGH>, |
| @@ -76,6 +85,8 @@ examples: · 8 unmodified lines | |||
| 76 | jpegenc: jpegenc@58450000 { | 85 | jpegenc: jpegenc@58450000 { |
| 77 | compatible = "nxp,imx8qm-jpgenc", "nxp,imx8qxp-jpgenc"; | 86 | compatible = "nxp,imx8qm-jpgenc", "nxp,imx8qxp-jpgenc"; |
| 78 | reg = <0x58450000 0x00050000 >; | 87 | reg = <0x58450000 0x00050000 >; |
| 88 | clocks = <&img_jpeg_enc_lpcg IMX_LPCG_CLK_0>, | ||
| 89 | <&img_jpeg__lpcg IMX_LPCG_CLK_4>; | ||
| 79 | interrupts = <GIC_SPI 305 IRQ_TYPE_LEVEL_HIGH>, | 90 | interrupts = <GIC_SPI 305 IRQ_TYPE_LEVEL_HIGH>, |
| 80 | <GIC_SPI 306 IRQ_TYPE_LEVEL_HIGH>, | 91 | <GIC_SPI 306 IRQ_TYPE_LEVEL_HIGH>, |
| 81 | <GIC_SPI 307 IRQ_TYPE_LEVEL_HIGH>, | 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 | vimc-devel | 26 | vimc-devel |
| 27 | zoran | 27 | zoran |
| 28 | ccs/ccs | 28 | ccs/ccs |
| 29 | ipu6 | ||
| 29 | 30 | ||
| 30 | 31 | ||
| 31 | Digital TV drivers | 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 | ``ENOMEM`` | 70 | ``ENOMEM`` |
| 71 | Insufficient kernel memory was available. | 71 | Insufficient kernel memory was available. |
| 72 | 72 | ||
| 73 | ``ENXIO`` | 73 | ``ENODEV`` |
| 74 | No device corresponding to this device special file exists. | 74 | Device not found or was removed. |
| @@ -91,7 +91,7 @@ appropriately. · 90 unmodified lines | |||
| 91 | - The caller has no permission to access the device. | 91 | - The caller has no permission to access the device. |
| 92 | 92 | ||
| 93 | - - ``EBUSY`` | 93 | - - ``EBUSY`` |
| 94 | - The the device driver is already in use. | 94 | - The device driver is already in use. |
| 95 | 95 | ||
| 96 | - - ``EMFILE`` | 96 | - - ``EMFILE`` |
| 97 | - The process already has the maximum number of files open. | 97 | - The process already has the maximum number of files open. |
| @@ -25,6 +25,13 @@ Glossary · 24 unmodified lines | |||
| 25 | 25 | ||
| 26 | See :ref:`cec`. | 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 | Device Driver | 35 | Device Driver |
| 29 | Part of the Linux Kernel that implements support for a hardware | 36 | Part of the Linux Kernel that implements support for a hardware |
| 30 | component. | 37 | component. |
| @@ -173,6 +180,11 @@ Glossary · 142 unmodified lines | |||
| 173 | An integrated circuit that integrates all components of a computer | 180 | An integrated circuit that integrates all components of a computer |
| 174 | or other electronic systems. | 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 | V4L2 API | 188 | V4L2 API |
| 177 | **V4L2 userspace API** | 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 | and applications must set the remainder of the :c:type:`v4l2_format` structure | 47 | and applications must set the remainder of the :c:type:`v4l2_format` structure |
| 48 | to 0. | 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 | .. c:type:: v4l2_meta_format | 56 | .. c:type:: v4l2_meta_format |
| 51 | 57 | ||
| 52 | .. tabularcolumns:: |p{1.4cm}|p{2.4cm}|p{13.5cm}| | 58 | .. tabularcolumns:: |p{1.4cm}|p{2.4cm}|p{13.5cm}| |
| @@ -65,3 +71,18 @@ to 0. · 12 unmodified lines | |||
| 65 | - ``buffersize`` | 71 | - ``buffersize`` |
| 66 | - Maximum buffer size in bytes required for data. The value is set by the | 72 | - Maximum buffer size in bytes required for data. The value is set by the |
| 67 | driver. | 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 | subdev-formats | 507 | subdev-formats |
| 508 | 508 | ||
| 509 | .. _subdev-routing: | ||
| 510 | |||
| 509 | Streams, multiplexed media pads and internal routing | 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 | pads. | 529 | pads. |
| 528 | 530 | ||
| 529 | Subdevice drivers that support multiplexed streams are compatible with | 531 | Subdevice drivers that support multiplexed streams are compatible with |
| 530 | non-multiplexed subdev drivers, but, of course, require a routing configuration | 532 | non-multiplexed subdev drivers. However, if the driver at the sink end of a link |
| 531 | where the link between those two types of drivers contains only a single | 533 | does not support streams, then only stream 0 of source end may be captured. |
| 532 | stream. | 534 | There may be additional limitations specific to the sink device. |
| 533 | 535 | ||
| 534 | Understanding streams | 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 | are independent of similar configurations on other streams. This is | 572 | are independent of similar configurations on other streams. This is |
| 571 | subject to change in the future. | 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 | Configuring streams | 598 | Configuring streams |
| 574 | ^^^^^^^^^^^^^^^^^^^ | 599 | ^^^^^^^^^^^^^^^^^^^ |
| 575 | 600 | ||
| @@ -65,8 +65,8 @@ EBUSY · 64 unmodified lines | |||
| 65 | The driver does not support multiple opens and the device is already | 65 | The driver does not support multiple opens and the device is already |
| 66 | in use. | 66 | in use. |
| 67 | 67 | ||
| 68 | ENXIO | 68 | ENODEV |
| 69 | No device corresponding to this device special file exists. | 69 | Device not found or was removed. |
| 70 | 70 | ||
| 71 | ENOMEM | 71 | ENOMEM |
| 72 | Not enough kernel memory was available to complete the request. | 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 | :maxdepth: 1 | 13 | :maxdepth: 1 |
| 14 | 14 | ||
| 15 | metafmt-d4xx | 15 | metafmt-d4xx |
| 16 | metafmt-generic | ||
| 16 | metafmt-intel-ipu3 | 17 | metafmt-intel-ipu3 |
| 17 | metafmt-rkisp1 | 18 | metafmt-rkisp1 |
| 18 | metafmt-uvc | 19 | metafmt-uvc |
| 20 | metafmt-vivid | ||
| 19 | metafmt-vsp1-hgo | 21 | metafmt-vsp1-hgo |
| 20 | metafmt-vsp1-hgt | 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 | buffers[i].start[j] = mmap(NULL, buffer.m.planes[j].length, | 188 | buffers[i].start[j] = mmap(NULL, buffer.m.planes[j].length, |
| 189 | PROT_READ | PROT_WRITE, /* recommended */ | 189 | PROT_READ | PROT_WRITE, /* recommended */ |
| 190 | MAP_SHARED, /* recommended */ | 190 | MAP_SHARED, /* recommended */ |
| 191 | fd, buffer.m.planes[j].m.offset); | 191 | fd, buffer.m.planes[j].m.mem_offset); |
| 192 | 192 | ||
| 193 | if (MAP_FAILED == buffers[i].start[j]) { | 193 | if (MAP_FAILED == buffers[i].start[j]) { |
| 194 | /* If you do not exit here you should unmap() and free() | 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 | - Byte 2 | 36 | - Byte 2 |
| 37 | - Byte 3 | 37 | - Byte 3 |
| 38 | - Byte 4 | 38 | - Byte 4 |
| 39 | - Byte 5 | ||
| 40 | - Byte 6 | ||
| 39 | 41 | ||
| 40 | * .. _V4L2-PIX-FMT-GREY: | 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 | * .. _V4L2-PIX-FMT-IPU3-Y10: | 55 | * .. _V4L2-PIX-FMT-IPU3-Y10: |
| 52 | 56 | ||
| @@ -58,6 +62,8 @@ are often referred to as greyscale formats. · 5 unmodified lines | |||
| 58 | - Y'\ :sub:`2`\ [3:0] Y'\ :sub:`1`\ [9:6] | 62 | - Y'\ :sub:`2`\ [3:0] Y'\ :sub:`1`\ [9:6] |
| 59 | - Y'\ :sub:`3`\ [1:0] Y'\ :sub:`2`\ [9:4] | 63 | - Y'\ :sub:`3`\ [1:0] Y'\ :sub:`2`\ [9:4] |
| 60 | - Y'\ :sub:`3`\ [9:2] | 64 | - Y'\ :sub:`3`\ [9:2] |
| 65 | - ... | ||
| 66 | - ... | ||
| 61 | 67 | ||
| 62 | * .. _V4L2-PIX-FMT-Y10: | 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 | * .. _V4L2-PIX-FMT-Y10BPACK: | 81 | * .. _V4L2-PIX-FMT-Y10BPACK: |
| 74 | 82 | ||
| @@ -80,6 +88,8 @@ are often referred to as greyscale formats. · 5 unmodified lines | |||
| 80 | - Y'\ :sub:`1`\ [3:0] Y'\ :sub:`2`\ [9:6] | 88 | - Y'\ :sub:`1`\ [3:0] Y'\ :sub:`2`\ [9:6] |
| 81 | - Y'\ :sub:`2`\ [5:0] Y'\ :sub:`3`\ [9:8] | 89 | - Y'\ :sub:`2`\ [5:0] Y'\ :sub:`3`\ [9:8] |
| 82 | - Y'\ :sub:`3`\ [7:0] | 90 | - Y'\ :sub:`3`\ [7:0] |
| 91 | - ... | ||
| 92 | - ... | ||
| 83 | 93 | ||
| 84 | * .. _V4L2-PIX-FMT-Y10P: | 94 | * .. _V4L2-PIX-FMT-Y10P: |
| 85 | 95 | ||
| @@ -91,6 +101,8 @@ are often referred to as greyscale formats. · 5 unmodified lines | |||
| 91 | - Y'\ :sub:`2`\ [9:2] | 101 | - Y'\ :sub:`2`\ [9:2] |
| 92 | - Y'\ :sub:`3`\ [9:2] | 102 | - Y'\ :sub:`3`\ [9:2] |
| 93 | - Y'\ :sub:`3`\ [1:0] Y'\ :sub:`2`\ [1:0] Y'\ :sub:`1`\ [1:0] Y'\ :sub:`0`\ [1:0] | 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 | * .. _V4L2-PIX-FMT-Y12: | 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 | * .. _V4L2-PIX-FMT-Y012: | 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 | * .. _V4L2-PIX-FMT-Y14: | 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 | * .. _V4L2-PIX-FMT-Y16: | 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 | * .. _V4L2-PIX-FMT-Y16-BE: | 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 | .. raw:: latex | 198 | .. raw:: latex |
| 151 | 199 | ||
| @@ -33,7 +33,7 @@ Media Bus Formats · 32 unmodified lines | |||
| 33 | * - __u32 | 33 | * - __u32 |
| 34 | - ``field`` | 34 | - ``field`` |
| 35 | - Field order, from enum :c:type:`v4l2_field`. See | 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 | * - __u32 | 37 | * - __u32 |
| 38 | - ``colorspace`` | 38 | - ``colorspace`` |
| 39 | - Image colorspace, from enum :c:type:`v4l2_colorspace`. | 39 | - Image colorspace, from enum :c:type:`v4l2_colorspace`. |
| @@ -45,7 +45,7 @@ Media Bus Formats · 5 unmodified lines | |||
| 45 | conversion is supported by setting the flag | 45 | conversion is supported by setting the flag |
| 46 | V4L2_SUBDEV_MBUS_CODE_CSC_COLORSPACE in the corresponding struct | 46 | V4L2_SUBDEV_MBUS_CODE_CSC_COLORSPACE in the corresponding struct |
| 47 | :c:type:`v4l2_subdev_mbus_code_enum` during enumeration. | 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 | * - union { | 49 | * - union { |
| 50 | - (anonymous) | 50 | - (anonymous) |
| 51 | * - __u16 | 51 | * - __u16 |
| @@ -61,7 +61,7 @@ Media Bus Formats · 9 unmodified lines | |||
| 61 | that ycbcr_enc conversion is supported by setting the flag | 61 | that ycbcr_enc conversion is supported by setting the flag |
| 62 | V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC in the corresponding struct | 62 | V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC in the corresponding struct |
| 63 | :c:type:`v4l2_subdev_mbus_code_enum` during enumeration. | 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 | * - __u16 | 65 | * - __u16 |
| 66 | - ``hsv_enc`` | 66 | - ``hsv_enc`` |
| 67 | - HSV encoding, from enum :c:type:`v4l2_hsv_encoding`. | 67 | - HSV encoding, from enum :c:type:`v4l2_hsv_encoding`. |
| @@ -75,7 +75,7 @@ Media Bus Formats · 7 unmodified lines | |||
| 75 | that hsv_enc conversion is supported by setting the flag | 75 | that hsv_enc conversion is supported by setting the flag |
| 76 | V4L2_SUBDEV_MBUS_CODE_CSC_HSV_ENC in the corresponding struct | 76 | V4L2_SUBDEV_MBUS_CODE_CSC_HSV_ENC in the corresponding struct |
| 77 | :c:type:`v4l2_subdev_mbus_code_enum` during enumeration. | 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 | * - __u16 | 81 | * - __u16 |
| @@ -90,8 +90,8 @@ Media Bus Formats · 8 unmodified lines | |||
| 90 | The driver indicates that quantization conversion is supported by | 90 | The driver indicates that quantization conversion is supported by |
| 91 | setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION in the | 91 | setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION in the |
| 92 | corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` | 92 | corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` |
| 93 | during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. | 93 | during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for |
| 94 | 94 | metadata mbus codes. | |
| 95 | * - __u16 | 95 | * - __u16 |
| 96 | - ``xfer_func`` | 96 | - ``xfer_func`` |
| 97 | - Transfer function, from enum :c:type:`v4l2_xfer_func`. | 97 | - Transfer function, from enum :c:type:`v4l2_xfer_func`. |
| @@ -104,7 +104,8 @@ Media Bus Formats · 6 unmodified lines | |||
| 104 | The driver indicates that the transfer function conversion is supported by | 104 | The driver indicates that the transfer function conversion is supported by |
| 105 | setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_XFER_FUNC in the | 105 | setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_XFER_FUNC in the |
| 106 | corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` | 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 | * - __u16 | 109 | * - __u16 |
| 109 | - ``flags`` | 110 | - ``flags`` |
| 110 | - flags See: :ref:v4l2-mbus-framefmt-flags | 111 | - flags See: :ref:v4l2-mbus-framefmt-flags |
| @@ -8306,3 +8307,257 @@ The following table lists the existing metadata formats. · 8195 unmodified lines | |||
| 8306 | both sides of the link and the bus format is a fixed | 8307 | both sides of the link and the bus format is a fixed |
| 8307 | metadata format that is not configurable from userspace. | 8308 | metadata format that is not configurable from userspace. |
| 8308 | Width and height will be set to 0 for this format. | 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 | vidioc-query-dv-timings | 62 | vidioc-query-dv-timings |
| 63 | vidioc-querystd | 63 | vidioc-querystd |
| 64 | vidioc-reqbufs | 64 | vidioc-reqbufs |
| 65 | vidioc-remove-bufs | ||
| 65 | vidioc-s-hw-freq-seek | 66 | vidioc-s-hw-freq-seek |
| 66 | vidioc-streamon | 67 | vidioc-streamon |
| 67 | vidioc-subdev-enum-frame-interval | 68 | vidioc-subdev-enum-frame-interval |
| @@ -227,6 +227,13 @@ the ``mbus_code`` field is handled differently: · 226 unmodified lines | |||
| 227 | The application can ask to configure the quantization of the capture | 227 | The application can ask to configure the quantization of the capture |
| 228 | device when calling the :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl with | 228 | device when calling the :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl with |
| 229 | :ref:`V4L2_PIX_FMT_FLAG_SET_CSC <v4l2-pix-fmt-flag-set-csc>` set. | 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 | Return Value | 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 | .. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF: | 121 | .. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF: |
| 122 | .. _V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS: | 122 | .. _V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS: |
| 123 | .. _V4L2-BUF-CAP-SUPPORTS-MAX-NUM-BUFFERS: | 123 | .. _V4L2-BUF-CAP-SUPPORTS-MAX-NUM-BUFFERS: |
| 124 | .. _V4L2-BUF-CAP-SUPPORTS-REMOVE-BUFS: | ||
| 124 | 125 | ||
| 125 | .. raw:: latex | 126 | .. raw:: latex |
| 126 | 127 | ||
| @@ -37,9 +37,9 @@ Description · 36 unmodified lines | |||
| 37 | 37 | ||
| 38 | .. note:: | 38 | .. note:: |
| 39 | 39 | ||
| 40 | This is an :ref:`obsolete` interface and may be removed | 40 | This is an :ref:`obsolete` interface and may be removed in the future. It is |
| 41 | in the future. It is superseded by | 41 | superseded by :ref:`the selection API <VIDIOC_SUBDEV_G_SELECTION>`. No new |
| 42 | :ref:`the selection API <VIDIOC_SUBDEV_G_SELECTION>`. | 42 | extensions to the :c:type:`v4l2_subdev_crop` structure will be accepted. |
| 43 | 43 | ||
| 44 | To retrieve the current crop rectangle applications set the ``pad`` | 44 | To retrieve the current crop rectangle applications set the ``pad`` |
| 45 | field of a struct :c:type:`v4l2_subdev_crop` to the | 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 | Drivers report their current routing tables using the | 43 | Drivers report their current routing tables using the |
| 44 | ``VIDIOC_SUBDEV_G_ROUTING`` ioctl and application may enable or disable routes | 44 | ``VIDIOC_SUBDEV_G_ROUTING`` ioctl and application may enable or disable routes |
| 45 | with the ``VIDIOC_SUBDEV_S_ROUTING`` ioctl, by adding or removing routes and | 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 | 46 | setting or clearing flags of the ``flags`` field of a struct |
| 47 | struct :c:type:`v4l2_subdev_route`. | 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 | All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called. |
| 50 | means that the userspace must reconfigure all streams after calling the ioctl | 51 | This means that the userspace must reconfigure all stream formats and selections |
| 51 | with e.g. ``VIDIOC_SUBDEV_S_FMT``. | 52 | after calling the ioctl with e.g. ``VIDIOC_SUBDEV_S_FMT``. |
| 52 | 53 | ||
| 53 | Only subdevices which have both sink and source pads can support routing. | 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 | The ``len_routes`` field indicates the number of routes that can fit in the |
| 56 | provided ``num_routes`` is not big enough to contain all the available routes | 57 | ``routes`` array allocated by userspace. It is set by applications for both |
| 57 | the subdevice exposes, drivers return the ENOSPC error code and adjust the | 58 | ioctls to indicate how many routes the kernel can return, and is never modified |
| 58 | value of the ``num_routes`` field. Application should then reserve enough memory | 59 | by the kernel. |
| 59 | for all the route entries and call ``VIDIOC_SUBDEV_G_ROUTING`` again. | 60 | |
| 60 | 61 | The ``num_routes`` field indicates the number of routes in the routing | |
| 61 | On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the | 62 | table. For ``VIDIOC_SUBDEV_S_ROUTING``, it is set by userspace to the number of |
| 62 | ``num_routes`` field to reflect the actual number of routes returned. | 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 | .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| | 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 | - ``which`` | 90 | - ``which`` |
| 75 | - Routing table to be accessed, from enum | 91 | - Routing table to be accessed, from enum |
| 76 | :ref:`v4l2_subdev_format_whence <v4l2-subdev-format-whence>`. | 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 | * - struct :c:type:`v4l2_subdev_route` | 96 | * - struct :c:type:`v4l2_subdev_route` |
| 78 | - ``routes[]`` | 97 | - ``routes[]`` |
| 79 | - Array of struct :c:type:`v4l2_subdev_route` entries | 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 | - ``num_routes`` | 100 | - ``num_routes`` |
| 82 | - Number of entries of the routes array | 101 | - Number of entries of the routes array |
| 83 | * - __u32 | 102 | * - __u32 |
| 84 | - ``reserved``\ [5] | 103 | - ``reserved``\ [11] |
| 85 | - Reserved for future extensions. Applications and drivers must set | 104 | - Reserved for future extensions. Applications and drivers must set |
| 86 | the array to zero. | 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 | appropriately. The generic error codes are described at the | 154 | appropriately. The generic error codes are described at the |
| 136 | :ref:`Generic Error Codes <gen-errors>` chapter. | 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 | EINVAL | 157 | EINVAL |
| 143 | The sink or source pad identifiers reference a non-existing pad or reference | 158 | The sink or source pad identifiers reference a non-existing pad or reference |
| 144 | pads of different types (ie. the sink_pad identifiers refers to a source | 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 | replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags | 215 | replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags |
| 216 | replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags | 216 | replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags |
| 217 | replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags | 217 | replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags |
| 218 | replace define V4L2_FMT_FLAG_META_LINE_BASED fmtdesc-flags | ||
| 218 | 219 | ||
| 219 | # V4L2 timecode types | 220 | # V4L2 timecode types |
| 220 | replace define V4L2_TC_TYPE_24FPS timecode-type | 221 | replace define V4L2_TC_TYPE_24FPS timecode-type |
| @@ -4110,6 +4110,13 @@ N: bcm113* · 4109 unmodified lines | |||
| 4110 | N: bcm216* | 4110 | N: bcm216* |
| 4111 | N: kona | 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 | BROADCOM BCM47XX MIPS ARCHITECTURE | 4120 | BROADCOM BCM47XX MIPS ARCHITECTURE |
| 4114 | M: Hauke Mehrtens <hauke@hauke-m.de> | 4121 | M: Hauke Mehrtens <hauke@hauke-m.de> |
| 4115 | M: Rafał Miłecki <zajec5@gmail.com> | 4122 | M: Rafał Miłecki <zajec5@gmail.com> |
| @@ -11091,6 +11098,16 @@ F: Documentation/admin-guide/media/ipu3_rcb.svg · 6975 unmodified lines | |||
| 11091 | F: Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst | 11098 | F: Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst |
| 11092 | F: drivers/staging/media/ipu3/ | 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 | INTEL ISHTP ECLITE DRIVER | 11111 | INTEL ISHTP ECLITE DRIVER |
| 11095 | M: Sumesh K Naduvalath <sumesh.k.naduvalath@intel.com> | 11112 | M: Sumesh K Naduvalath <sumesh.k.naduvalath@intel.com> |
| 11096 | L: platform-driver-x86@vger.kernel.org | 11113 | L: platform-driver-x86@vger.kernel.org |
| @@ -16539,7 +16556,7 @@ M: Sakari Ailus <sakari.ailus@linux.intel.com> · 5442 unmodified lines | |||
| 16539 | L: linux-media@vger.kernel.org | 16556 | L: linux-media@vger.kernel.org |
| 16540 | S: Maintained | 16557 | S: Maintained |
| 16541 | T: git git://linuxtv.org/media_tree.git | 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 | F: drivers/media/i2c/ov8856.c | 16560 | F: drivers/media/i2c/ov8856.c |
| 16544 | 16561 | ||
| 16545 | OMNIVISION OV8858 SENSOR DRIVER | 16562 | OMNIVISION OV8858 SENSOR DRIVER |
| @@ -490,6 +490,15 @@ int cec_thread_func(void *_adap) · 489 unmodified lines | |||
| 490 | goto unlock; | 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 | if (adap->transmit_in_progress && timeout) { | 502 | if (adap->transmit_in_progress && timeout) { |
| 494 | /* | 503 | /* |
| 495 | * If we timeout, then log that. Normally this does | 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 | struct cec_data *data; | 781 | struct cec_data *data; |
| 773 | bool is_raw = msg_is_raw(msg); | 782 | bool is_raw = msg_is_raw(msg); |
| 783 | int err; | ||
| 774 | 784 | ||
| 775 | if (adap->devnode.unregistered) | 785 | if (adap->devnode.unregistered) |
| 776 | return -ENODEV; | 786 | return -ENODEV; |
| @@ -935,11 +945,13 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, · 158 unmodified lines | |||
| 935 | * Release the lock and wait, retake the lock afterwards. | 945 | * Release the lock and wait, retake the lock afterwards. |
| 936 | */ | 946 | */ |
| 937 | mutex_unlock(&adap->lock); | 947 | mutex_unlock(&adap->lock); |
| 938 | wait_for_completion_killable(&data->c); | 948 | err = wait_for_completion_killable(&data->c); |
| 939 | if (!data->completed) | 949 | cancel_delayed_work_sync(&data->work); |
| 940 | cancel_delayed_work_sync(&data->work); | ||
| 941 | mutex_lock(&adap->lock); | 950 | mutex_lock(&adap->lock); |
| 942 | 951 | ||
| 952 | if (err) | ||
| 953 | adap->transmit_in_progress_aborted = true; | ||
| 954 | |||
| 943 | /* Cancel the transmit if it was interrupted */ | 955 | /* Cancel the transmit if it was interrupted */ |
| 944 | if (!data->completed) { | 956 | if (!data->completed) { |
| 945 | if (data->msg.tx_status & CEC_TX_STATUS_OK) | 957 | if (data->msg.tx_status & CEC_TX_STATUS_OK) |
| @@ -1575,9 +1587,12 @@ unconfigure: · 629 unmodified lines | |||
| 1575 | */ | 1587 | */ |
| 1576 | static void cec_claim_log_addrs(struct cec_adapter *adap, bool block) | 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 | return; | 1592 | return; |
| 1580 | 1593 | ||
| 1594 | adap->is_claiming_log_addrs = true; | ||
| 1595 | |||
| 1581 | init_completion(&adap->config_completion); | 1596 | init_completion(&adap->config_completion); |
| 1582 | 1597 | ||
| 1583 | /* Ready to kick off the thread */ | 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 | wait_for_completion(&adap->config_completion); | 1607 | wait_for_completion(&adap->config_completion); |
| 1593 | mutex_lock(&adap->lock); | 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 | CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU | | 178 | CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU | |
| 179 | CEC_LOG_ADDRS_FL_CDC_ONLY; | 179 | CEC_LOG_ADDRS_FL_CDC_ONLY; |
| 180 | mutex_lock(&adap->lock); | 180 | mutex_lock(&adap->lock); |
| 181 | if (!adap->is_configuring && | 181 | if (!adap->is_claiming_log_addrs && !adap->is_configuring && |
| 182 | (!log_addrs.num_log_addrs || !adap->is_configured) && | 182 | (!log_addrs.num_log_addrs || !adap->is_configured) && |
| 183 | !cec_is_busy(adap, fh)) { | 183 | !cec_is_busy(adap, fh)) { |
| 184 | err = __cec_s_log_addrs(adap, &log_addrs, block); | 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 | list_del_init(&data->xfer_list); | 664 | list_del_init(&data->xfer_list); |
| 665 | } | 665 | } |
| 666 | mutex_unlock(&adap->lock); | 666 | mutex_unlock(&adap->lock); |
| 667 | |||
| 668 | mutex_lock(&fh->lock); | ||
| 667 | while (!list_empty(&fh->msgs)) { | 669 | while (!list_empty(&fh->msgs)) { |
| 668 | struct cec_msg_entry *entry = | 670 | struct cec_msg_entry *entry = |
| 669 | list_first_entry(&fh->msgs, struct cec_msg_entry, list); | 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 | kfree(entry); | 683 | kfree(entry); |
| 682 | } | 684 | } |
| 683 | } | 685 | } |
| 686 | mutex_unlock(&fh->lock); | ||
| 684 | kfree(fh); | 687 | kfree(fh); |
| 685 | 688 | ||
| 686 | cec_put_device(devnode); | 689 | cec_put_device(devnode); |
| @@ -62,12 +62,12 @@ int cec_get_device(struct cec_devnode *devnode) · 61 unmodified lines | |||
| 62 | */ | 62 | */ |
| 63 | mutex_lock(&devnode->lock); | 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 | * already or if it is not registered anymore. | 66 | * already or if it is not registered anymore. |
| 67 | */ | 67 | */ |
| 68 | if (!devnode->registered) { | 68 | if (!devnode->registered) { |
| 69 | mutex_unlock(&devnode->lock); | 69 | mutex_unlock(&devnode->lock); |
| 70 | return -ENXIO; | 70 | return -ENODEV; |
| 71 | } | 71 | } |
| 72 | /* and increase the device refcount */ | 72 | /* and increase the device refcount */ |
| 73 | get_device(&devnode->dev); | 73 | get_device(&devnode->dev); |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | |||
| 8 | 8 | ||
| 9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
| 10 | #include <linux/module.h> | 10 | #include <linux/module.h> |
| 11 | #include <linux/mod_devicetable.h> | ||
| 11 | #include <linux/platform_device.h> | 12 | #include <linux/platform_device.h> |
| 12 | #include <linux/dmi.h> | 13 | #include <linux/dmi.h> |
| 13 | #include <linux/pci.h> | 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 | static struct platform_driver cros_ec_cec_driver = { | 583 | static struct platform_driver cros_ec_cec_driver = { |
| 577 | .probe = cros_ec_cec_probe, | 584 | .probe = cros_ec_cec_probe, |
| 578 | .remove_new = cros_ec_cec_remove, | 585 | .remove_new = cros_ec_cec_remove, |
| @@ -580,6 +587,7 @@ static struct platform_driver cros_ec_cec_driver = { · 1 unmodified line | |||
| 580 | .name = DRV_NAME, | 587 | .name = DRV_NAME, |
| 581 | .pm = &cros_ec_cec_pm_ops, | 588 | .pm = &cros_ec_cec_pm_ops, |
| 582 | }, | 589 | }, |
| 590 | .id_table = cros_ec_cec_id, | ||
| 583 | }; | 591 | }; |
| 584 | 592 | ||
| 585 | module_platform_driver(cros_ec_cec_driver); | 593 | module_platform_driver(cros_ec_cec_driver); |
| @@ -587,4 +595,3 @@ module_platform_driver(cros_ec_cec_driver); · 1 unmodified line | |||
| 587 | MODULE_DESCRIPTION("CEC driver for ChromeOS ECs"); | 595 | MODULE_DESCRIPTION("CEC driver for ChromeOS ECs"); |
| 588 | MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); | 596 | MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); |
| 589 | MODULE_LICENSE("GPL"); | 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 | xacm = 0; | 122 | xacm = 0; |
| 123 | 123 | ||
| 124 | /* set horizontal filter parameters (CXY = CXUV) */ | 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 | cxuv = cxy; | 126 | cxuv = cxy; |
| 127 | 127 | ||
| 128 | /* calculate and set horizontal fine scale (xsci) */ | 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 | xacm = 0; | 151 | xacm = 0; |
| 152 | /* get best match in the table of attenuations | 152 | /* get best match in the table of attenuations |
| 153 | for horizontal scaling */ | 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 | for (i = 0; h_attenuation[i] != 0; i++) { | 156 | for (i = 0; h_attenuation[i] != 0; i++) { |
| 157 | if (h_attenuation[i] >= h_atten) | 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 | /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */ | 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 | /* get best match in the table of attenuations for vertical scaling */ | 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 | for (i = 0; v_attenuation[i] != 0; i++) { | 291 | for (i = 0; v_attenuation[i] != 0; i++) { |
| 292 | if (v_attenuation[i] >= v_atten) | 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 | static void vb2_queue_add_buffer(struct vb2_queue *q, struct vb2_buffer *vb, unsigned int index) | 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 | q->bufs[index] = vb; | 426 | q->bufs[index] = vb; |
| 427 | vb->index = index; | 427 | vb->index = index; |
| 428 | vb->vb2_queue = q; | 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 | static void vb2_queue_remove_buffer(struct vb2_buffer *vb) | 436 | static void vb2_queue_remove_buffer(struct vb2_buffer *vb) |
| 436 | { | 437 | { |
| 438 | clear_bit(vb->index, vb->vb2_queue->bufs_bitmap); | ||
| 437 | vb->vb2_queue->bufs[vb->index] = NULL; | 439 | vb->vb2_queue->bufs[vb->index] = NULL; |
| 438 | vb->vb2_queue = NULL; | 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 | * __vb2_queue_alloc() - allocate vb2 buffer structures and (for MMAP type) | 444 | * __vb2_queue_alloc() - allocate vb2 buffer structures and (for MMAP type) |
| 443 | * video buffer memory for all buffers/planes on the queue and initializes the | 445 | * video buffer memory for all buffers/planes on the queue and initializes the |
| 444 | * queue | 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 | * Returns the number of buffers successfully allocated. | 450 | * Returns the number of buffers successfully allocated. |
| 447 | */ | 451 | */ |
| 448 | static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, | 452 | static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, |
| 449 | unsigned int num_buffers, unsigned int num_planes, | 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 | unsigned int buffer, plane; | 457 | unsigned int buffer, plane; |
| 454 | struct vb2_buffer *vb; | 458 | struct vb2_buffer *vb; |
| 459 | unsigned long index = q->max_num_buffers; | ||
| 455 | int ret; | 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 | * in the queue is below q->max_num_buffers | 464 | * in the queue is below q->max_num_buffers |
| 460 | */ | 465 | */ |
| 461 | num_buffers = min_t(unsigned int, num_buffers, | 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 | for (buffer = 0; buffer < num_buffers; ++buffer) { | 487 | for (buffer = 0; buffer < num_buffers; ++buffer) { |
| 465 | /* Allocate vb2 buffer structures */ | 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 | vb->planes[plane].min_length = plane_sizes[plane]; | 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 | call_void_bufop(q, init_buffer, vb); | 506 | call_void_bufop(q, init_buffer, vb); |
| 484 | 507 | ||
| 485 | /* Allocate video buffer memory for the MMAP type */ | 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 | struct vb2_buffer *vb; | 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; | 551 | for (i = start; i < start + count; i++) { |
| 529 | ++buffer) { | 552 | vb = vb2_get_buffer(q, i); |
| 530 | vb = vb2_get_buffer(q, buffer); | ||
| 531 | if (!vb) | 553 | if (!vb) |
| 532 | continue; | 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 | * related information, if no buffers are left return the queue to an | 568 | * related information, if no buffers are left return the queue to an |
| 547 | * uninitialized state. Might be called even if the queue has already been freed. | 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; | 573 | unsigned int i; |
| 552 | unsigned int q_num_buffers = vb2_get_num_buffers(q); | ||
| 553 | 574 | ||
| 554 | lockdep_assert_held(&q->mmap_lock); | 575 | lockdep_assert_held(&q->mmap_lock); |
| 555 | 576 | ||
| 556 | /* Call driver-provided cleanup function for each buffer, if provided */ | 577 | /* Call driver-provided cleanup function for each buffer, if provided */ |
| 557 | for (buffer = q_num_buffers - buffers; buffer < q_num_buffers; | 578 | for (i = start; i < start + count; i++) { |
| 558 | ++buffer) { | 579 | struct vb2_buffer *vb = vb2_get_buffer(q, i); |
| 559 | struct vb2_buffer *vb = vb2_get_buffer(q, buffer); | ||
| 560 | 580 | ||
| 561 | if (vb && vb->planes[0].mem_priv) | 581 | if (vb && vb->planes[0].mem_priv) |
| 562 | call_void_vb_qop(vb, buf_cleanup, vb); | 582 | call_void_vb_qop(vb, buf_cleanup, vb); |
| 563 | } | 583 | } |
| 564 | 584 | ||
| 565 | /* Release video buffer memory */ | 585 | /* Release video buffer memory */ |
| 566 | __vb2_free_mem(q, buffers); | 586 | __vb2_free_mem(q, start, count); |
| 567 | 587 | ||
| 568 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 588 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
| 569 | /* | 589 | /* |
| 570 | * Check that all the calls were balanced during the life-time of this | 590 | * Check that all the calls were balanced during the life-time of this |
| 571 | * queue. If not then dump the counters to the kernel log. | 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 | bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming || | 594 | bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming || |
| 575 | q->cnt_prepare_streaming != q->cnt_unprepare_streaming || | 595 | q->cnt_prepare_streaming != q->cnt_unprepare_streaming || |
| 576 | q->cnt_wait_prepare != q->cnt_wait_finish; | 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 | q->cnt_stop_streaming = 0; | 616 | q->cnt_stop_streaming = 0; |
| 597 | q->cnt_unprepare_streaming = 0; | 617 | q->cnt_unprepare_streaming = 0; |
| 598 | } | 618 | } |
| 599 | for (buffer = 0; buffer < vb2_get_num_buffers(q); buffer++) { | 619 | for (i = start; i < start + count; i++) { |
| 600 | struct vb2_buffer *vb = vb2_get_buffer(q, buffer); | 620 | struct vb2_buffer *vb = vb2_get_buffer(q, i); |
| 601 | bool unbalanced; | 621 | bool unbalanced; |
| 602 | 622 | ||
| 603 | if (!vb) | 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 | if (unbalanced) { | 635 | if (unbalanced) { |
| 616 | pr_info("unbalanced counters for queue %p, buffer %d:\n", | 636 | pr_info("unbalanced counters for queue %p, buffer %d:\n", |
| 617 | q, buffer); | 637 | q, i); |
| 618 | if (vb->cnt_buf_init != vb->cnt_buf_cleanup) | 638 | if (vb->cnt_buf_init != vb->cnt_buf_cleanup) |
| 619 | pr_info(" buf_init: %u buf_cleanup: %u\n", | 639 | pr_info(" buf_init: %u buf_cleanup: %u\n", |
| 620 | vb->cnt_buf_init, vb->cnt_buf_cleanup); | 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 | #endif | 668 | #endif |
| 649 | 669 | ||
| 650 | /* Free vb2 buffers */ | 670 | /* Free vb2 buffers */ |
| 651 | for (buffer = q_num_buffers - buffers; buffer < q_num_buffers; | 671 | for (i = start; i < start + count; i++) { |
| 652 | ++buffer) { | 672 | struct vb2_buffer *vb = vb2_get_buffer(q, i); |
| 653 | struct vb2_buffer *vb = vb2_get_buffer(q, buffer); | ||
| 654 | 673 | ||
| 655 | if (!vb) | 674 | if (!vb) |
| 656 | continue; | 675 | continue; |
| @@ -659,7 +678,6 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) · 2 unmodified lines | |||
| 659 | kfree(vb); | 678 | kfree(vb); |
| 660 | } | 679 | } |
| 661 | 680 | ||
| 662 | q->num_buffers -= buffers; | ||
| 663 | if (!vb2_get_num_buffers(q)) { | 681 | if (!vb2_get_num_buffers(q)) { |
| 664 | q->memory = VB2_MEMORY_UNKNOWN; | 682 | q->memory = VB2_MEMORY_UNKNOWN; |
| 665 | INIT_LIST_HEAD(&q->queued_list); | 683 | INIT_LIST_HEAD(&q->queued_list); |
| @@ -691,7 +709,7 @@ EXPORT_SYMBOL(vb2_buffer_in_use); · 25 unmodified lines | |||
| 691 | static bool __buffers_in_use(struct vb2_queue *q) | 709 | static bool __buffers_in_use(struct vb2_queue *q) |
| 692 | { | 710 | { |
| 693 | unsigned int buffer; | 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 | struct vb2_buffer *vb = vb2_get_buffer(q, buffer); | 713 | struct vb2_buffer *vb = vb2_get_buffer(q, buffer); |
| 696 | 714 | ||
| 697 | if (!vb) | 715 | if (!vb) |
| @@ -813,6 +831,32 @@ static bool verify_coherency_flags(struct vb2_queue *q, bool non_coherent_mem) · 115 unmodified lines | |||
| 813 | return true; | 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 | int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, | 860 | int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, |
| 817 | unsigned int flags, unsigned int *count) | 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 | unsigned int q_num_bufs = vb2_get_num_buffers(q); | 864 | unsigned int q_num_bufs = vb2_get_num_buffers(q); |
| 821 | unsigned plane_sizes[VB2_MAX_PLANES] = { }; | 865 | unsigned plane_sizes[VB2_MAX_PLANES] = { }; |
| 822 | bool non_coherent_mem = flags & V4L2_MEMORY_FLAG_NON_COHERENT; | 866 | bool non_coherent_mem = flags & V4L2_MEMORY_FLAG_NON_COHERENT; |
| 823 | unsigned int i; | 867 | unsigned int i, first_index; |
| 824 | int ret = 0; | 868 | int ret = 0; |
| 825 | 869 | ||
| 826 | if (q->streaming) { | 870 | if (q->streaming) { |
| @@ -851,9 +895,10 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 24 unmodified lines | |||
| 851 | * queued without ever calling STREAMON. | 895 | * queued without ever calling STREAMON. |
| 852 | */ | 896 | */ |
| 853 | __vb2_queue_cancel(q); | 897 | __vb2_queue_cancel(q); |
| 854 | __vb2_queue_free(q, q_num_bufs); | 898 | __vb2_queue_free(q, 0, q->max_num_buffers); |
| 855 | mutex_unlock(&q->mmap_lock); | 899 | mutex_unlock(&q->mmap_lock); |
| 856 | 900 | ||
| 901 | q->is_busy = 0; | ||
| 857 | /* | 902 | /* |
| 858 | * In case of REQBUFS(0) return immediately without calling | 903 | * In case of REQBUFS(0) return immediately without calling |
| 859 | * driver's queue_setup() callback and allocating resources. | 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 | * Make sure the requested values and current defaults are sane. | 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 | num_buffers = min_t(unsigned int, num_buffers, q->max_num_buffers); | 914 | num_buffers = min_t(unsigned int, num_buffers, q->max_num_buffers); |
| 870 | memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); | 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 | * in the queue_setup op. | 918 | * in the queue_setup op. |
| 874 | */ | 919 | */ |
| 875 | mutex_lock(&q->mmap_lock); | 920 | mutex_lock(&q->mmap_lock); |
| 876 | if (!q->bufs) | 921 | ret = vb2_core_allocated_buffers_storage(q); |
| 877 | q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL); | ||
| 878 | if (!q->bufs) | ||
| 879 | ret = -ENOMEM; | ||
| 880 | q->memory = memory; | 922 | q->memory = memory; |
| 881 | mutex_unlock(&q->mmap_lock); | 923 | mutex_unlock(&q->mmap_lock); |
| 882 | if (ret) | 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 | /* Finally, allocate buffers and video memory */ | 949 | /* Finally, allocate buffers and video memory */ |
| 908 | allocated_buffers = | 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 | if (allocated_buffers == 0) { | 952 | if (allocated_buffers == 0) { |
| 953 | /* There shouldn't be any buffers allocated, so first_index == 0 */ | ||
| 954 | WARN_ON(first_index); | ||
| 911 | dprintk(q, 1, "memory allocation failed\n"); | 955 | dprintk(q, 1, "memory allocation failed\n"); |
| 912 | ret = -ENOMEM; | 956 | ret = -ENOMEM; |
| 913 | goto error; | 957 | goto error; |
| @@ -917,7 +961,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 3 unmodified lines | |||
| 917 | * There is no point in continuing if we can't allocate the minimum | 961 | * There is no point in continuing if we can't allocate the minimum |
| 918 | * number of buffers needed by this vb2_queue. | 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 | ret = -ENOMEM; | 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 | mutex_lock(&q->mmap_lock); | 992 | mutex_lock(&q->mmap_lock); |
| 949 | q->num_buffers = allocated_buffers; | ||
| 950 | 993 | ||
| 951 | if (ret < 0) { | 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 | * from already queued buffers and it will reset q->memory to | 997 | * from already queued buffers and it will reset q->memory to |
| 955 | * VB2_MEMORY_UNKNOWN. | 998 | * VB2_MEMORY_UNKNOWN. |
| 956 | */ | 999 | */ |
| 957 | __vb2_queue_free(q, allocated_buffers); | 1000 | __vb2_queue_free(q, first_index, allocated_buffers); |
| 958 | mutex_unlock(&q->mmap_lock); | 1001 | mutex_unlock(&q->mmap_lock); |
| 959 | return ret; | 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 | *count = allocated_buffers; | 1010 | *count = allocated_buffers; |
| 968 | q->waiting_for_buffers = !q->is_output; | 1011 | q->waiting_for_buffers = !q->is_output; |
| 1012 | q->is_busy = 1; | ||
| 969 | 1013 | ||
| 970 | return 0; | 1014 | return 0; |
| 971 | 1015 | ||
| @@ -973,6 +1017,7 @@ error: · 1 unmodified line | |||
| 973 | mutex_lock(&q->mmap_lock); | 1017 | mutex_lock(&q->mmap_lock); |
| 974 | q->memory = VB2_MEMORY_UNKNOWN; | 1018 | q->memory = VB2_MEMORY_UNKNOWN; |
| 975 | mutex_unlock(&q->mmap_lock); | 1019 | mutex_unlock(&q->mmap_lock); |
| 1020 | vb2_core_free_buffers_storage(q); | ||
| 976 | return ret; | 1021 | return ret; |
| 977 | } | 1022 | } |
| 978 | EXPORT_SYMBOL_GPL(vb2_core_reqbufs); | 1023 | EXPORT_SYMBOL_GPL(vb2_core_reqbufs); |
| @@ -980,7 +1025,8 @@ EXPORT_SYMBOL_GPL(vb2_core_reqbufs); · 1 unmodified line | |||
| 980 | int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, | 1025 | int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, |
| 981 | unsigned int flags, unsigned int *count, | 1026 | unsigned int flags, unsigned int *count, |
| 982 | unsigned int requested_planes, | 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 | unsigned int num_planes = 0, num_buffers, allocated_buffers; | 1031 | unsigned int num_planes = 0, num_buffers, allocated_buffers; |
| 986 | unsigned plane_sizes[VB2_MAX_PLANES] = { }; | 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 | * value in the queue_setup op. | 1051 | * value in the queue_setup op. |
| 1006 | */ | 1052 | */ |
| 1007 | mutex_lock(&q->mmap_lock); | 1053 | mutex_lock(&q->mmap_lock); |
| 1054 | ret = vb2_core_allocated_buffers_storage(q); | ||
| 1008 | q->memory = memory; | 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 | mutex_unlock(&q->mmap_lock); | 1056 | mutex_unlock(&q->mmap_lock); |
| 1014 | if (ret) | 1057 | if (ret) |
| 1015 | return ret; | 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 | /* Finally, allocate buffers and video memory */ | 1086 | /* Finally, allocate buffers and video memory */ |
| 1044 | allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers, | 1087 | allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers, |
| 1045 | num_planes, plane_sizes); | 1088 | num_planes, plane_sizes, first_index); |
| 1046 | if (allocated_buffers == 0) { | 1089 | if (allocated_buffers == 0) { |
| 1047 | dprintk(q, 1, "memory allocation failed\n"); | 1090 | dprintk(q, 1, "memory allocation failed\n"); |
| 1048 | ret = -ENOMEM; | 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 | mutex_lock(&q->mmap_lock); | 1117 | mutex_lock(&q->mmap_lock); |
| 1075 | q->num_buffers += allocated_buffers; | ||
| 1076 | 1118 | ||
| 1077 | if (ret < 0) { | 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 | * from already queued buffers and it will reset q->memory to | 1122 | * from already queued buffers and it will reset q->memory to |
| 1081 | * VB2_MEMORY_UNKNOWN. | 1123 | * VB2_MEMORY_UNKNOWN. |
| 1082 | */ | 1124 | */ |
| 1083 | __vb2_queue_free(q, allocated_buffers); | 1125 | __vb2_queue_free(q, *first_index, allocated_buffers); |
| 1084 | mutex_unlock(&q->mmap_lock); | 1126 | mutex_unlock(&q->mmap_lock); |
| 1085 | return -ENOMEM; | 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 | * to the userspace. | 1133 | * to the userspace. |
| 1092 | */ | 1134 | */ |
| 1093 | *count = allocated_buffers; | 1135 | *count = allocated_buffers; |
| 1136 | q->is_busy = 1; | ||
| 1094 | 1137 | ||
| 1095 | return 0; | 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 | EXPORT_SYMBOL_GPL(vb2_core_prepare_buf); | 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 | * vb2_start_streaming() - Attempt to start streaming. | 1733 | * vb2_start_streaming() - Attempt to start streaming. |
| 1653 | * @q: videobuf2 queue | 1734 | * @q: videobuf2 queue |
| @@ -1694,7 +1775,7 @@ static int vb2_start_streaming(struct vb2_queue *q) · 40 unmodified lines | |||
| 1694 | * Forcefully reclaim buffers if the driver did not | 1775 | * Forcefully reclaim buffers if the driver did not |
| 1695 | * correctly return them to vb2. | 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 | vb = vb2_get_buffer(q, i); | 1779 | vb = vb2_get_buffer(q, i); |
| 1699 | 1780 | ||
| 1700 | if (!vb) | 1781 | if (!vb) |
| @@ -2100,7 +2181,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) · 399 unmodified lines | |||
| 2100 | * to vb2 in stop_streaming(). | 2181 | * to vb2 in stop_streaming(). |
| 2101 | */ | 2182 | */ |
| 2102 | if (WARN_ON(atomic_read(&q->owned_by_drv_count))) { | 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 | struct vb2_buffer *vb = vb2_get_buffer(q, i); | 2185 | struct vb2_buffer *vb = vb2_get_buffer(q, i); |
| 2105 | 2186 | ||
| 2106 | if (!vb) | 2187 | if (!vb) |
| @@ -2144,7 +2225,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) · 37 unmodified lines | |||
| 2144 | * call to __fill_user_buffer() after buf_finish(). That order can't | 2225 | * call to __fill_user_buffer() after buf_finish(). That order can't |
| 2145 | * be changed, so we can't move the buf_finish() to __vb2_dqbuf(). | 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 | struct vb2_buffer *vb; | 2229 | struct vb2_buffer *vb; |
| 2149 | struct media_request *req; | 2230 | struct media_request *req; |
| 2150 | 2231 | ||
| @@ -2503,7 +2584,7 @@ int vb2_core_queue_init(struct vb2_queue *q) · 352 unmodified lines | |||
| 2503 | WARN_ON(!q->ops->buf_queue)) | 2584 | WARN_ON(!q->ops->buf_queue)) |
| 2504 | return -EINVAL; | 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 | WARN_ON(q->min_queued_buffers > q->max_num_buffers)) | 2588 | WARN_ON(q->min_queued_buffers > q->max_num_buffers)) |
| 2508 | return -EINVAL; | 2589 | return -EINVAL; |
| 2509 | 2590 | ||
| @@ -2521,6 +2602,25 @@ int vb2_core_queue_init(struct vb2_queue *q) · 11 unmodified lines | |||
| 2521 | if (WARN_ON(q->supports_requests && q->min_queued_buffers)) | 2602 | if (WARN_ON(q->supports_requests && q->min_queued_buffers)) |
| 2522 | return -EINVAL; | 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 | INIT_LIST_HEAD(&q->queued_list); | 2624 | INIT_LIST_HEAD(&q->queued_list); |
| 2525 | INIT_LIST_HEAD(&q->done_list); | 2625 | INIT_LIST_HEAD(&q->done_list); |
| 2526 | spin_lock_init(&q->done_lock); | 2626 | spin_lock_init(&q->done_lock); |
| @@ -2552,9 +2652,9 @@ void vb2_core_queue_release(struct vb2_queue *q) · 25 unmodified lines | |||
| 2552 | __vb2_cleanup_fileio(q); | 2652 | __vb2_cleanup_fileio(q); |
| 2553 | __vb2_queue_cancel(q); | 2653 | __vb2_queue_cancel(q); |
| 2554 | mutex_lock(&q->mmap_lock); | 2654 | mutex_lock(&q->mmap_lock); |
| 2555 | __vb2_queue_free(q, vb2_get_num_buffers(q)); | 2655 | __vb2_queue_free(q, 0, q->max_num_buffers); |
| 2556 | kfree(q->bufs); | 2656 | vb2_core_free_buffers_storage(q); |
| 2557 | q->bufs = NULL; | 2657 | q->is_busy = 0; |
| 2558 | mutex_unlock(&q->mmap_lock); | 2658 | mutex_unlock(&q->mmap_lock); |
| 2559 | } | 2659 | } |
| 2560 | EXPORT_SYMBOL_GPL(vb2_core_queue_release); | 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 | struct vb2_fileio_data *fileio; | 2813 | struct vb2_fileio_data *fileio; |
| 2714 | struct vb2_buffer *vb; | 2814 | struct vb2_buffer *vb; |
| 2715 | int i, ret; | 2815 | int i, ret; |
| 2716 | unsigned int count = 0; | ||
| 2717 | 2816 | ||
| 2718 | /* | 2817 | /* |
| 2719 | * Sanity check | 2818 | * Sanity check |
| @@ -2734,18 +2833,8 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) · 14 unmodified lines | |||
| 2734 | if (q->streaming || vb2_get_num_buffers(q) > 0) | 2833 | if (q->streaming || vb2_get_num_buffers(q) > 0) |
| 2735 | return -EBUSY; | 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 | dprintk(q, 3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", | 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 | q->fileio_write_immediately); | 2838 | q->fileio_write_immediately); |
| 2750 | 2839 | ||
| 2751 | fileio = kzalloc(sizeof(*fileio), GFP_KERNEL); | 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 | * Request buffers and use MMAP type to force driver | 2848 | * Request buffers and use MMAP type to force driver |
| 2760 | * to allocate buffers by itself. | 2849 | * to allocate buffers by itself. |
| 2761 | */ | 2850 | */ |
| 2762 | fileio->count = count; | 2851 | fileio->count = q->min_reqbufs_allocation; |
| 2763 | fileio->memory = VB2_MEMORY_MMAP; | 2852 | fileio->memory = VB2_MEMORY_MMAP; |
| 2764 | fileio->type = q->type; | 2853 | fileio->type = q->type; |
| 2765 | q->fileio = fileio; | 2854 | q->fileio = fileio; |
| 2766 | ret = vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); | 2855 | ret = vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); |
| 2767 | if (ret) | 2856 | if (ret) |
| 2768 | goto err_kfree; | 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 | * Userspace can never add or delete buffers later, so there | 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 | *flags &= V4L2_MEMORY_FLAG_NON_COHERENT; | 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 | if (q->io_modes & VB2_MMAP) | 689 | if (q->io_modes & VB2_MMAP) |
| 690 | *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP; | 690 | *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP; |
| 691 | if (q->io_modes & VB2_USERPTR) | 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 | for (i = 0; i < requested_planes; i++) | 795 | for (i = 0; i < requested_planes; i++) |
| 796 | if (requested_sizes[i] == 0) | 796 | if (requested_sizes[i] == 0) |
| 797 | return -EINVAL; | 797 | return -EINVAL; |
| 798 | return ret ? ret : vb2_core_create_bufs(q, create->memory, | 798 | if (ret) |
| 799 | create->flags, | 799 | return ret; |
| 800 | &create->count, | 800 | |
| 801 | requested_planes, | 801 | return vb2_core_create_bufs(q, create->memory, |
| 802 | requested_sizes); | 802 | create->flags, |
| 803 | &create->count, | ||
| 804 | requested_planes, | ||
| 805 | requested_sizes, | ||
| 806 | &create->index); | ||
| 803 | } | 807 | } |
| 804 | EXPORT_SYMBOL_GPL(vb2_create_bufs); | 808 | EXPORT_SYMBOL_GPL(vb2_create_bufs); |
| 805 | 809 | ||
| @@ -997,6 +1001,24 @@ EXPORT_SYMBOL_GPL(vb2_poll); · 191 unmodified lines | |||
| 997 | 1001 | ||
| 998 | /* vb2 ioctl helpers */ | 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 | int vb2_ioctl_reqbufs(struct file *file, void *priv, | 1022 | int vb2_ioctl_reqbufs(struct file *file, void *priv, |
| 1001 | struct v4l2_requestbuffers *p) | 1023 | struct v4l2_requestbuffers *p) |
| 1002 | { | 1024 | { |
| @@ -956,7 +956,7 @@ int dvb_usercopy(struct file *file, · 955 unmodified lines | |||
| 956 | int (*func)(struct file *file, | 956 | int (*func)(struct file *file, |
| 957 | unsigned int cmd, void *arg)) | 957 | unsigned int cmd, void *arg)) |
| 958 | { | 958 | { |
| 959 | char sbuf[128]; | 959 | char sbuf[128] = {}; |
| 960 | void *mbuf = NULL; | 960 | void *mbuf = NULL; |
| 961 | void *parg = NULL; | 961 | void *parg = NULL; |
| 962 | int err = -EINVAL; | 962 | int err = -EINVAL; |
| @@ -174,6 +174,6 @@ struct as10x_register_addr { · 173 unmodified lines | |||
| 174 | uint32_t addr; | 174 | uint32_t addr; |
| 175 | /* register mode access */ | 175 | /* register mode access */ |
| 176 | uint8_t mode; | 176 | uint8_t mode; |
| 177 | }; | 177 | } __packed; |
| 178 | 178 | ||
| 179 | #endif | 179 | #endif |
| @@ -5,4 +5,4 @@ config DVB_CXD2880 · 4 unmodified lines | |||
| 5 | depends on DVB_CORE && SPI | 5 | depends on DVB_CORE && SPI |
| 6 | default m if !MEDIA_SUBDRV_AUTOSELECT | 6 | default m if !MEDIA_SUBDRV_AUTOSELECT |
| 7 | help | 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 | #include <linux/kernel.h> | 34 | #include <linux/kernel.h> |
| 35 | #include <linux/errno.h> | 35 | #include <linux/errno.h> |
| 36 | #include <linux/firmware.h> | ||
| 37 | #include <linux/i2c.h> | 36 | #include <linux/i2c.h> |
| 38 | 37 | ||
| 39 | /* | 38 | /* |
| @@ -1910,7 +1909,6 @@ struct drx_demod_instance { · 1870 unmodified lines | |||
| 1910 | /* generic demodulator data */ | 1909 | /* generic demodulator data */ |
| 1911 | 1910 | ||
| 1912 | struct i2c_adapter *i2c; | 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 | #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ | 56 | #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ |
| 57 | 57 | ||
| 58 | #include <linux/module.h> | 58 | #include <linux/module.h> |
| 59 | #include <linux/firmware.h> | ||
| 59 | #include <linux/init.h> | 60 | #include <linux/init.h> |
| 60 | #include <linux/string.h> | 61 | #include <linux/string.h> |
| 61 | #include <linux/slab.h> | 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 | /* Read block from I2C **************************************************** */ | 1446 | /* Read block from I2C **************************************************** */ |
| 1446 | do { | 1447 | do { |
| 1447 | u16 todo = (datasize < DRXDAP_MAX_RCHUNKSIZE ? | 1448 | u16 todo = min(datasize, DRXDAP_MAX_RCHUNKSIZE); |
| 1448 | datasize : DRXDAP_MAX_RCHUNKSIZE); | ||
| 1449 | 1449 | ||
| 1450 | bufx = 0; | 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 | Address must be rewritten because HI is reset after data transport and | 1659 | Address must be rewritten because HI is reset after data transport and |
| 1660 | expects an address. | 1660 | expects an address. |
| 1661 | */ | 1661 | */ |
| 1662 | todo = (block_size < datasize ? block_size : datasize); | 1662 | todo = min(block_size, datasize); |
| 1663 | if (todo == 0) { | 1663 | if (todo == 0) { |
| 1664 | u16 overhead_size_i2c_addr = 0; | 1664 | u16 overhead_size_i2c_addr = 0; |
| 1665 | u16 data_block_size = 0; | 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 | first_err = st; | 1681 | first_err = st; |
| 1682 | } | 1682 | } |
| 1683 | bufx = 0; | 1683 | bufx = 0; |
| 1684 | todo = | 1684 | todo = min(data_block_size, datasize); |
| 1685 | (data_block_size < | ||
| 1686 | datasize ? data_block_size : datasize); | ||
| 1687 | } | 1685 | } |
| 1688 | memcpy(&buf[bufx], data, todo); | 1686 | memcpy(&buf[bufx], data, todo); |
| 1689 | /* write (address if can do and) data */ | 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 | u8 *mc_data = NULL; | 11748 | u8 *mc_data = NULL; |
| 11751 | unsigned size; | 11749 | unsigned size; |
| 11752 | char *mc_file; | 11750 | char *mc_file; |
| 11751 | const struct firmware *fw; | ||
| 11753 | 11752 | ||
| 11754 | /* Check arguments */ | 11753 | /* Check arguments */ |
| 11755 | if (!mc_info || !mc_info->mc_file) | 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 | mc_file = mc_info->mc_file; | 11757 | mc_file = mc_info->mc_file; |
| 11759 | 11758 | ||
| 11760 | if (!demod->firmware) { | 11759 | rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent); |
| 11761 | const struct firmware *fw = NULL; | 11760 | if (rc < 0) { |
| 11762 | 11761 | pr_err("Couldn't read firmware %s\n", mc_file); | |
| 11763 | rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent); | 11762 | return rc; |
| 11764 | if (rc < 0) { | 11763 | } |
| 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 | } | ||
| 11775 | 11764 | ||
| 11776 | pr_info("Firmware %s, size %zu\n", | 11765 | if (fw->size < 2 * sizeof(u16)) { |
| 11777 | mc_file, demod->firmware->size); | 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; | 11771 | pr_info("Firmware %s, size %zu\n", mc_file, fw->size); |
| 11781 | size = demod->firmware->size; | 11772 | |
| 11773 | mc_data_init = fw->data; | ||
| 11774 | size = fw->size; | ||
| 11782 | 11775 | ||
| 11783 | mc_data = (void *)mc_data_init; | 11776 | mc_data = (void *)mc_data_init; |
| 11784 | /* Check data */ | 11777 | /* Check data */ |
| @@ -11874,7 +11867,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, · 89 unmodified lines | |||
| 11874 | 0x0000)) { | 11867 | 0x0000)) { |
| 11875 | pr_err("error reading firmware at pos %zd\n", | 11868 | pr_err("error reading firmware at pos %zd\n", |
| 11876 | mc_data - mc_data_init); | 11869 | mc_data - mc_data_init); |
| 11877 | return -EIO; | 11870 | rc = -EIO; |
| 11871 | goto release; | ||
| 11878 | } | 11872 | } |
| 11879 | 11873 | ||
| 11880 | result = memcmp(curr_ptr, mc_data_buffer, | 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 | if (result) { | 11877 | if (result) { |
| 11884 | pr_err("error verifying firmware at pos %zd\n", | 11878 | pr_err("error verifying firmware at pos %zd\n", |
| 11885 | mc_data - mc_data_init); | 11879 | mc_data - mc_data_init); |
| 11886 | return -EIO; | 11880 | rc = -EIO; |
| 11881 | goto release; | ||
| 11887 | } | 11882 | } |
| 11888 | 11883 | ||
| 11889 | curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2)); | 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 | break; | 11888 | break; |
| 11894 | } | 11889 | } |
| 11895 | default: | 11890 | default: |
| 11896 | return -EINVAL; | 11891 | rc = -EINVAL; |
| 11892 | goto release; | ||
| 11897 | 11893 | ||
| 11898 | } | 11894 | } |
| 11899 | mc_data += mc_block_nr_bytes; | 11895 | mc_data += mc_block_nr_bytes; |
| 11900 | } | 11896 | } |
| 11901 | 11897 | ||
| 11902 | return 0; | 11898 | rc = 0; |
| 11903 | 11899 | ||
| 11904 | release: | 11900 | release: |
| 11905 | release_firmware(demod->firmware); | 11901 | release_firmware(fw); |
| 11906 | demod->firmware = NULL; | ||
| 11907 | 11902 | ||
| 11908 | return rc; | 11903 | return rc; |
| 11909 | } | 11904 | } |
| @@ -12271,7 +12266,6 @@ static void drx39xxj_release(struct dvb_frontend *fe) · 361 unmodified lines | |||
| 12271 | kfree(demod->my_ext_attr); | 12266 | kfree(demod->my_ext_attr); |
| 12272 | kfree(demod->my_common_attr); | 12267 | kfree(demod->my_common_attr); |
| 12273 | kfree(demod->my_i2c_dev_addr); | 12268 | kfree(demod->my_i2c_dev_addr); |
| 12274 | release_firmware(demod->firmware); | ||
| 12275 | kfree(demod); | 12269 | kfree(demod); |
| 12276 | kfree(state); | 12270 | kfree(state); |
| 12277 | } | 12271 | } |
| @@ -2176,6 +2176,11 @@ static int lgdt3306a_probe(struct i2c_client *client) · 2175 unmodified lines | |||
| 2176 | struct dvb_frontend *fe; | 2176 | struct dvb_frontend *fe; |
| 2177 | int ret; | 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 | config = kmemdup(client->dev.platform_data, | 2184 | config = kmemdup(client->dev.platform_data, |
| 2180 | sizeof(struct lgdt3306a_config), GFP_KERNEL); | 2185 | sizeof(struct lgdt3306a_config), GFP_KERNEL); |
| 2181 | if (config == NULL) { | 2186 | if (config == NULL) { |
| @@ -1000,6 +1000,13 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) · 999 unmodified lines | |||
| 1000 | if (ret) | 1000 | if (ret) |
| 1001 | goto err; | 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 | dev->delivery_system = c->delivery_system; | 1010 | dev->delivery_system = c->delivery_system; |
| 1004 | 1011 | ||
| 1005 | return 0; | 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 | u32 nco_count_min = 0; | 1381 | u32 nco_count_min = 0; |
| 1382 | u32 clk_type = 0; | 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 | {0x90700010, 8, 1}, {0x90700010, 9, 1}, | 1385 | {0x90700010, 8, 1}, {0x90700010, 9, 1}, |
| 1386 | {0x90700010, 10, 1}, {0x90700010, 11, 1}, | 1386 | {0x90700010, 10, 1}, {0x90700010, 11, 1}, |
| 1387 | {0x90700010, 12, 1}, {0x90700010, 13, 1}, | 1387 | {0x90700010, 12, 1}, {0x90700010, 13, 1}, |
| 1388 | {0x90700010, 14, 1}, {0x90700010, 15, 1} }; | 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 | {0x90700010, 16, 1}, {0x90700010, 17, 1}, | 1390 | {0x90700010, 16, 1}, {0x90700010, 17, 1}, |
| 1391 | {0x90700010, 18, 1}, {0x90700010, 19, 1}, | 1391 | {0x90700010, 18, 1}, {0x90700010, 19, 1}, |
| 1392 | {0x90700010, 20, 1}, {0x90700010, 21, 1}, | 1392 | {0x90700010, 20, 1}, {0x90700010, 21, 1}, |
| 1393 | {0x90700010, 22, 1}, {0x90700010, 23, 1} }; | 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 | {0x90700014, 0, 1}, {0x90700014, 1, 1}, | 1395 | {0x90700014, 0, 1}, {0x90700014, 1, 1}, |
| 1396 | {0x90700014, 2, 1}, {0x90700014, 3, 1}, | 1396 | {0x90700014, 2, 1}, {0x90700014, 3, 1}, |
| 1397 | {0x90700014, 4, 1}, {0x90700014, 5, 1}, | 1397 | {0x90700014, 4, 1}, {0x90700014, 5, 1}, |
| 1398 | {0x90700014, 6, 1}, {0x90700014, 7, 1} }; | 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 | {0x90700018, 0, 3}, {0x90700018, 4, 3}, | 1400 | {0x90700018, 0, 3}, {0x90700018, 4, 3}, |
| 1401 | {0x90700018, 8, 3}, {0x90700018, 12, 3}, | 1401 | {0x90700018, 8, 3}, {0x90700018, 12, 3}, |
| 1402 | {0x90700018, 16, 3}, {0x90700018, 20, 3}, | 1402 | {0x90700018, 16, 3}, {0x90700018, 20, 3}, |
| 1403 | {0x90700018, 24, 3}, {0x90700018, 28, 3} }; | 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 | {0x9070000C, 16, 1}, {0x9070000C, 17, 1}, | 1405 | {0x9070000C, 16, 1}, {0x9070000C, 17, 1}, |
| 1406 | {0x9070000C, 18, 1}, {0x9070000C, 19, 1}, | 1406 | {0x9070000C, 18, 1}, {0x9070000C, 19, 1}, |
| 1407 | {0x9070000C, 20, 1}, {0x9070000C, 21, 1}, | 1407 | {0x9070000C, 20, 1}, {0x9070000C, 21, 1}, |
| 1408 | {0x9070000C, 22, 1}, {0x9070000C, 23, 1} }; | 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 | {0x90700010, 0, 1}, {0x90700010, 1, 1}, | 1410 | {0x90700010, 0, 1}, {0x90700010, 1, 1}, |
| 1411 | {0x90700010, 2, 1}, {0x90700010, 3, 1}, | 1411 | {0x90700010, 2, 1}, {0x90700010, 3, 1}, |
| 1412 | {0x90700010, 4, 1}, {0x90700010, 5, 1}, | 1412 | {0x90700010, 4, 1}, {0x90700010, 5, 1}, |
| 1413 | {0x90700010, 6, 1}, {0x90700010, 7, 1} }; | 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 | {0x9070000C, 0, 1}, {0x9070000C, 1, 1}, | 1415 | {0x9070000C, 0, 1}, {0x9070000C, 1, 1}, |
| 1416 | {0x9070000C, 2, 1}, {0x9070000C, 3, 1}, | 1416 | {0x9070000C, 2, 1}, {0x9070000C, 3, 1}, |
| 1417 | {0x9070000C, 4, 1}, {0x9070000C, 5, 1}, | 1417 | {0x9070000C, 4, 1}, {0x9070000C, 5, 1}, |
| 1418 | {0x9070000C, 6, 1}, {0x9070000C, 7, 1} }; | 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 | {0x9070000C, 24, 1}, {0x9070000C, 25, 1}, | 1420 | {0x9070000C, 24, 1}, {0x9070000C, 25, 1}, |
| 1421 | {0x9070000C, 26, 1}, {0x9070000C, 27, 1}, | 1421 | {0x9070000C, 26, 1}, {0x9070000C, 27, 1}, |
| 1422 | {0x9070000C, 28, 1}, {0x9070000C, 29, 1}, | 1422 | {0x9070000C, 28, 1}, {0x9070000C, 29, 1}, |
| 1423 | {0x9070000C, 30, 1}, {0x9070000C, 31, 1} }; | 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 | {0x90700014, 8, 1}, {0x90700014, 9, 1}, | 1425 | {0x90700014, 8, 1}, {0x90700014, 9, 1}, |
| 1426 | {0x90700014, 10, 1}, {0x90700014, 11, 1}, | 1426 | {0x90700014, 10, 1}, {0x90700014, 11, 1}, |
| 1427 | {0x90700014, 12, 1}, {0x90700014, 13, 1}, | 1427 | {0x90700014, 12, 1}, {0x90700014, 13, 1}, |
| 1428 | {0x90700014, 14, 1}, {0x90700014, 15, 1} }; | 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 | {0x907001D4, 0, 1}, {0x907001D4, 1, 1}, | 1430 | {0x907001D4, 0, 1}, {0x907001D4, 1, 1}, |
| 1431 | {0x907001D4, 2, 1}, {0x907001D4, 3, 1}, | 1431 | {0x907001D4, 2, 1}, {0x907001D4, 3, 1}, |
| 1432 | {0x907001D4, 4, 1}, {0x907001D4, 5, 1}, | 1432 | {0x907001D4, 4, 1}, {0x907001D4, 5, 1}, |
| 1433 | {0x907001D4, 6, 1}, {0x907001D4, 7, 1} }; | 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 | {0x90700044, 16, 80}, {0x90700044, 16, 81}, | 1435 | {0x90700044, 16, 80}, {0x90700044, 16, 81}, |
| 1436 | {0x90700044, 16, 82}, {0x90700044, 16, 83}, | 1436 | {0x90700044, 16, 82}, {0x90700044, 16, 83}, |
| 1437 | {0x90700044, 16, 84}, {0x90700044, 16, 85}, | 1437 | {0x90700044, 16, 84}, {0x90700044, 16, 85}, |
| @@ -513,10 +513,8 @@ static int si2165_upload_firmware(struct si2165_state *state) · 512 unmodified lines | |||
| 513 | ret = 0; | 513 | ret = 0; |
| 514 | state->firmware_loaded = true; | 514 | state->firmware_loaded = true; |
| 515 | error: | 515 | error: |
| 516 | if (fw) { | 516 | release_firmware(fw); |
| 517 | release_firmware(fw); | 517 | fw = NULL; |
| 518 | fw = NULL; | ||
| 519 | } | ||
| 520 | 518 | ||
| 521 | return ret; | 519 | return ret; |
| 522 | } | 520 | } |
| @@ -1277,7 +1277,7 @@ static int stb0899_get_dev_id(struct stb0899_state *state) · 1276 unmodified lines | |||
| 1277 | dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver); | 1277 | dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver); |
| 1278 | CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str); | 1278 | CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str); |
| 1279 | fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG); | 1279 | fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG); |
| 1280 | if (! (chip_id > 0)) { | 1280 | if (!chip_id) { |
| 1281 | dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899"); | 1281 | dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899"); |
| 1282 | 1282 | ||
| 1283 | return -ENODEV; | 1283 | return -ENODEV; |
| @@ -410,6 +410,7 @@ static int tda10048_set_if(struct dvb_frontend *fe, u32 bw) · 409 unmodified lines | |||
| 410 | struct tda10048_config *config = &state->config; | 410 | struct tda10048_config *config = &state->config; |
| 411 | int i; | 411 | int i; |
| 412 | u32 if_freq_khz; | 412 | u32 if_freq_khz; |
| 413 | u64 sample_freq; | ||
| 413 | 414 | ||
| 414 | dprintk(1, "%s(bw = %d)\n", __func__, bw); | 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 | dprintk(1, "- pll_pfactor = %d\n", state->pll_pfactor); | 452 | dprintk(1, "- pll_pfactor = %d\n", state->pll_pfactor); |
| 452 | 453 | ||
| 453 | /* Calculate the sample frequency */ | 454 | /* Calculate the sample frequency */ |
| 454 | state->sample_freq = state->xtal_hz * (state->pll_mfactor + 45); | 455 | sample_freq = state->xtal_hz; |
| 455 | state->sample_freq /= (state->pll_nfactor + 1); | 456 | sample_freq *= state->pll_mfactor + 45; |
| 456 | state->sample_freq /= (state->pll_pfactor + 4); | 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 | dprintk(1, "- sample_freq = %d\n", state->sample_freq); | 460 | dprintk(1, "- sample_freq = %d\n", state->sample_freq); |
| 458 | 461 | ||
| 459 | /* Update the I/F */ | 462 | /* Update the I/F */ |
| @@ -328,7 +328,7 @@ static int CalcMainPLL(struct tda_state *state, u32 freq) · 327 unmodified lines | |||
| 328 | 328 | ||
| 329 | OscFreq = (u64) freq * (u64) Div; | 329 | OscFreq = (u64) freq * (u64) Div; |
| 330 | OscFreq *= (u64) 16384; | 330 | OscFreq *= (u64) 16384; |
| 331 | do_div(OscFreq, (u64)16000000); | 331 | do_div(OscFreq, 16000000); |
| 332 | MainDiv = OscFreq; | 332 | MainDiv = OscFreq; |
| 333 | 333 | ||
| 334 | state->m_Regs[MPD] = PostDiv & 0x77; | 334 | state->m_Regs[MPD] = PostDiv & 0x77; |
| @@ -352,7 +352,7 @@ static int CalcCalPLL(struct tda_state *state, u32 freq) · 17 unmodified lines | |||
| 352 | OscFreq = (u64)freq * (u64)Div; | 352 | OscFreq = (u64)freq * (u64)Div; |
| 353 | /* CalDiv = u32( OscFreq * 16384 / 16000000 ); */ | 353 | /* CalDiv = u32( OscFreq * 16384 / 16000000 ); */ |
| 354 | OscFreq *= (u64)16384; | 354 | OscFreq *= (u64)16384; |
| 355 | do_div(OscFreq, (u64)16000000); | 355 | do_div(OscFreq, 16000000); |
| 356 | CalDiv = OscFreq; | 356 | CalDiv = OscFreq; |
| 357 | 357 | ||
| 358 | state->m_Regs[CPD] = PostDiv; | 358 | state->m_Regs[CPD] = PostDiv; |
| @@ -195,6 +195,7 @@ config VIDEO_IMX334 · 194 unmodified lines | |||
| 195 | config VIDEO_IMX335 | 195 | config VIDEO_IMX335 |
| 196 | tristate "Sony IMX335 sensor support" | 196 | tristate "Sony IMX335 sensor support" |
| 197 | depends on OF_GPIO | 197 | depends on OF_GPIO |
| 198 | select V4L2_CCI_I2C | ||
| 198 | help | 199 | help |
| 199 | This is a Video4Linux2 sensor driver for the Sony | 200 | This is a Video4Linux2 sensor driver for the Sony |
| 200 | IMX335 camera. | 201 | IMX335 camera. |
| @@ -405,6 +406,7 @@ config VIDEO_OV2740 · 204 unmodified lines | |||
| 405 | config VIDEO_OV4689 | 406 | config VIDEO_OV4689 |
| 406 | tristate "OmniVision OV4689 sensor support" | 407 | tristate "OmniVision OV4689 sensor support" |
| 407 | depends on GPIOLIB | 408 | depends on GPIOLIB |
| 409 | select V4L2_CCI_I2C | ||
| 408 | help | 410 | help |
| 409 | This is a Video4Linux2 sensor-level driver for the OmniVision | 411 | This is a Video4Linux2 sensor-level driver for the OmniVision |
| 410 | OV4689 camera. | 412 | OV4689 camera. |
| @@ -1486,7 +1486,7 @@ static int adv7180_probe(struct i2c_client *client) · 1485 unmodified lines | |||
| 1486 | if (ret) | 1486 | if (ret) |
| 1487 | goto err_media_entity_cleanup; | 1487 | goto err_media_entity_cleanup; |
| 1488 | 1488 | ||
| 1489 | if (state->irq) { | 1489 | if (state->irq > 0) { |
| 1490 | ret = request_threaded_irq(client->irq, NULL, adv7180_irq, | 1490 | ret = request_threaded_irq(client->irq, NULL, adv7180_irq, |
| 1491 | IRQF_ONESHOT | IRQF_TRIGGER_FALLING, | 1491 | IRQF_ONESHOT | IRQF_TRIGGER_FALLING, |
| 1492 | KBUILD_MODNAME, state); | 1492 | KBUILD_MODNAME, state); |
| @@ -214,7 +214,7 @@ static int adv748x_hdmi_set_video_timings(struct adv748x_state *state, · 213 unmodified lines | |||
| 214 | * v4l2_subdev_video_ops | 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 | struct v4l2_dv_timings *timings) | 218 | struct v4l2_dv_timings *timings) |
| 219 | { | 219 | { |
| 220 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); | 220 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); |
| @@ -254,7 +254,7 @@ error: · 33 unmodified lines | |||
| 254 | return ret; | 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 | struct v4l2_dv_timings *timings) | 258 | struct v4l2_dv_timings *timings) |
| 259 | { | 259 | { |
| 260 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); | 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 | return 0; | 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 | struct v4l2_dv_timings *timings) | 273 | struct v4l2_dv_timings *timings) |
| 274 | { | 274 | { |
| 275 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); | 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 | static const struct v4l2_subdev_video_ops adv748x_video_ops_hdmi = { | 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 | .g_input_status = adv748x_hdmi_g_input_status, | 395 | .g_input_status = adv748x_hdmi_g_input_status, |
| 399 | .s_stream = adv748x_hdmi_s_stream, | 396 | .s_stream = adv748x_hdmi_s_stream, |
| 400 | .g_pixelaspect = adv748x_hdmi_g_pixelaspect, | 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 | if (!tx) | 410 | if (!tx) |
| 414 | return -ENOLINK; | 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 | return adv748x_csi2_set_pixelrate(tx, timings.bt.pixelclock); | 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 | .get_fmt = adv748x_hdmi_get_format, | 607 | .get_fmt = adv748x_hdmi_get_format, |
| 611 | .get_edid = adv748x_hdmi_get_edid, | 608 | .get_edid = adv748x_hdmi_get_edid, |
| 612 | .set_edid = adv748x_hdmi_set_edid, | 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 | .dv_timings_cap = adv748x_hdmi_dv_timings_cap, | 613 | .dv_timings_cap = adv748x_hdmi_dv_timings_cap, |
| 614 | .enum_dv_timings = adv748x_hdmi_enum_dv_timings, | 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 | struct v4l2_dv_timings cea1280x720 = V4L2_DV_BT_CEA_1280X720P30; | 734 | struct v4l2_dv_timings cea1280x720 = V4L2_DV_BT_CEA_1280X720P30; |
| 735 | int ret; | 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 | /* Initialise a default 16:9 aspect ratio */ | 739 | /* Initialise a default 16:9 aspect ratio */ |
| 740 | hdmi->aspect_ratio.numerator = 16; | 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 | return 0; | 995 | return 0; |
| 996 | } | 996 | } |
| 997 | 997 | ||
| 998 | static int adv7511_s_dv_timings(struct v4l2_subdev *sd, | 998 | static int adv7511_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, |
| 999 | struct v4l2_dv_timings *timings) | 999 | struct v4l2_dv_timings *timings) |
| 1000 | { | 1000 | { |
| 1001 | struct adv7511_state *state = get_adv7511_state(sd); | 1001 | struct adv7511_state *state = get_adv7511_state(sd); |
| 1002 | struct v4l2_bt_timings *bt = &timings->bt; | 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 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); | 1005 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); |
| 1006 | 1006 | ||
| 1007 | if (pad != 0) | ||
| 1008 | return -EINVAL; | ||
| 1009 | |||
| 1007 | /* quick sanity check */ | 1010 | /* quick sanity check */ |
| 1008 | if (!v4l2_valid_dv_timings(timings, &adv7511_timings_cap, NULL, NULL)) | 1011 | if (!v4l2_valid_dv_timings(timings, &adv7511_timings_cap, NULL, NULL)) |
| 1009 | return -EINVAL; | 1012 | return -EINVAL; |
| @@ -1042,13 +1045,16 @@ static int adv7511_s_dv_timings(struct v4l2_subdev *sd, · 32 unmodified lines | |||
| 1042 | return 0; | 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 | struct v4l2_dv_timings *timings) | 1049 | struct v4l2_dv_timings *timings) |
| 1047 | { | 1050 | { |
| 1048 | struct adv7511_state *state = get_adv7511_state(sd); | 1051 | struct adv7511_state *state = get_adv7511_state(sd); |
| 1049 | 1052 | ||
| 1050 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); | 1053 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); |
| 1051 | 1054 | ||
| 1055 | if (pad != 0) | ||
| 1056 | return -EINVAL; | ||
| 1057 | |||
| 1052 | if (!timings) | 1058 | if (!timings) |
| 1053 | return -EINVAL; | 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 | static const struct v4l2_subdev_video_ops adv7511_video_ops = { | 1085 | static const struct v4l2_subdev_video_ops adv7511_video_ops = { |
| 1080 | .s_stream = adv7511_s_stream, | 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 | /* ------------------------------ AUDIO OPS ------------------------------ */ | 1089 | /* ------------------------------ AUDIO OPS ------------------------------ */ |
| @@ -1403,6 +1407,8 @@ static const struct v4l2_subdev_pad_ops adv7511_pad_ops = { · 317 unmodified lines | |||
| 1403 | .enum_mbus_code = adv7511_enum_mbus_code, | 1407 | .enum_mbus_code = adv7511_enum_mbus_code, |
| 1404 | .get_fmt = adv7511_get_fmt, | 1408 | .get_fmt = adv7511_get_fmt, |
| 1405 | .set_fmt = adv7511_set_fmt, | 1409 | .set_fmt = adv7511_set_fmt, |
| 1410 | .s_dv_timings = adv7511_s_dv_timings, | ||
| 1411 | .g_dv_timings = adv7511_g_dv_timings, | ||
| 1406 | .enum_dv_timings = adv7511_enum_dv_timings, | 1412 | .enum_dv_timings = adv7511_enum_dv_timings, |
| 1407 | .dv_timings_cap = adv7511_dv_timings_cap, | 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 | return freq; | 1557 | return freq; |
| 1558 | } | 1558 | } |
| 1559 | 1559 | ||
| 1560 | static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, | 1560 | static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad, |
| 1561 | struct v4l2_dv_timings *timings) | 1561 | struct v4l2_dv_timings *timings) |
| 1562 | { | 1562 | { |
| 1563 | struct adv76xx_state *state = to_state(sd); | 1563 | struct adv76xx_state *state = to_state(sd); |
| 1564 | const struct adv76xx_chip_info *info = state->info; | 1564 | const struct adv76xx_chip_info *info = state->info; |
| @@ -1687,8 +1687,8 @@ found: · 122 unmodified lines | |||
| 1687 | return 0; | 1687 | return 0; |
| 1688 | } | 1688 | } |
| 1689 | 1689 | ||
| 1690 | static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, | 1690 | static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, |
| 1691 | struct v4l2_dv_timings *timings) | 1691 | struct v4l2_dv_timings *timings) |
| 1692 | { | 1692 | { |
| 1693 | struct adv76xx_state *state = to_state(sd); | 1693 | struct adv76xx_state *state = to_state(sd); |
| 1694 | struct v4l2_bt_timings *bt; | 1694 | struct v4l2_bt_timings *bt; |
| @@ -1730,8 +1730,8 @@ static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, · 35 unmodified lines | |||
| 1730 | return 0; | 1730 | return 0; |
| 1731 | } | 1731 | } |
| 1732 | 1732 | ||
| 1733 | static int adv76xx_g_dv_timings(struct v4l2_subdev *sd, | 1733 | static int adv76xx_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, |
| 1734 | struct v4l2_dv_timings *timings) | 1734 | struct v4l2_dv_timings *timings) |
| 1735 | { | 1735 | { |
| 1736 | struct adv76xx_state *state = to_state(sd); | 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 | stdi.lcf, stdi.bl, stdi.lcvs, | 2607 | stdi.lcf, stdi.bl, stdi.lcvs, |
| 2608 | stdi.interlaced ? "interlaced" : "progressive", | 2608 | stdi.interlaced ? "interlaced" : "progressive", |
| 2609 | stdi.hs_pol, stdi.vs_pol); | 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 | v4l2_info(sd, "No video detected\n"); | 2611 | v4l2_info(sd, "No video detected\n"); |
| 2612 | else | 2612 | else |
| 2613 | v4l2_print_dv_timings(sd->name, "Detected format: ", | 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 | static const struct v4l2_subdev_video_ops adv76xx_video_ops = { | 2726 | static const struct v4l2_subdev_video_ops adv76xx_video_ops = { |
| 2727 | .s_routing = adv76xx_s_routing, | 2727 | .s_routing = adv76xx_s_routing, |
| 2728 | .g_input_status = adv76xx_g_input_status, | 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 | static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = { | 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 | .set_fmt = adv76xx_set_format, | 2735 | .set_fmt = adv76xx_set_format, |
| 2739 | .get_edid = adv76xx_get_edid, | 2736 | .get_edid = adv76xx_get_edid, |
| 2740 | .set_edid = adv76xx_set_edid, | 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 | .dv_timings_cap = adv76xx_dv_timings_cap, | 2741 | .dv_timings_cap = adv76xx_dv_timings_cap, |
| 2742 | .enum_dv_timings = adv76xx_enum_dv_timings, | 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 | timings->bt.flags |= V4L2_DV_FL_CAN_DETECT_REDUCED_FPS; | 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 | struct v4l2_dv_timings *timings) | 1522 | struct v4l2_dv_timings *timings) |
| 1523 | { | 1523 | { |
| 1524 | struct adv7842_state *state = to_state(sd); | 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 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); | 1528 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); |
| 1529 | 1529 | ||
| 1530 | if (pad != 0) | ||
| 1531 | return -EINVAL; | ||
| 1532 | |||
| 1530 | memset(timings, 0, sizeof(struct v4l2_dv_timings)); | 1533 | memset(timings, 0, sizeof(struct v4l2_dv_timings)); |
| 1531 | 1534 | ||
| 1532 | /* SDP block */ | 1535 | /* SDP block */ |
| @@ -1643,7 +1646,7 @@ found: · 110 unmodified lines | |||
| 1643 | return 0; | 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 | struct v4l2_dv_timings *timings) | 1650 | struct v4l2_dv_timings *timings) |
| 1648 | { | 1651 | { |
| 1649 | struct adv7842_state *state = to_state(sd); | 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 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); | 1656 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); |
| 1654 | 1657 | ||
| 1658 | if (pad != 0) | ||
| 1659 | return -EINVAL; | ||
| 1660 | |||
| 1655 | if (state->mode == ADV7842_MODE_SDP) | 1661 | if (state->mode == ADV7842_MODE_SDP) |
| 1656 | return -ENODATA; | 1662 | return -ENODATA; |
| 1657 | 1663 | ||
| @@ -1689,11 +1695,14 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd, · 31 unmodified lines | |||
| 1689 | return 0; | 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 | struct v4l2_dv_timings *timings) | 1699 | struct v4l2_dv_timings *timings) |
| 1694 | { | 1700 | { |
| 1695 | struct adv7842_state *state = to_state(sd); | 1701 | struct adv7842_state *state = to_state(sd); |
| 1696 | 1702 | ||
| 1703 | if (pad != 0) | ||
| 1704 | return -EINVAL; | ||
| 1705 | |||
| 1697 | if (state->mode == ADV7842_MODE_SDP) | 1706 | if (state->mode == ADV7842_MODE_SDP) |
| 1698 | return -ENODATA; | 1707 | return -ENODATA; |
| 1699 | *timings = state->timings; | 1708 | *timings = state->timings; |
| @@ -2780,7 +2789,7 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) · 1080 unmodified lines | |||
| 2780 | "interlaced" : "progressive", | 2789 | "interlaced" : "progressive", |
| 2781 | hs_pol, vs_pol); | 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 | v4l2_info(sd, "No video detected\n"); | 2793 | v4l2_info(sd, "No video detected\n"); |
| 2785 | else | 2794 | else |
| 2786 | v4l2_print_dv_timings(sd->name, "Detected format: ", | 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 | memset(&state->timings, 0, sizeof(struct v4l2_dv_timings)); | 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 | return ret; | 3240 | return ret; |
| 3232 | } | 3241 | } |
| @@ -3298,9 +3307,6 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = { · 65 unmodified lines | |||
| 3298 | .s_routing = adv7842_s_routing, | 3307 | .s_routing = adv7842_s_routing, |
| 3299 | .querystd = adv7842_querystd, | 3308 | .querystd = adv7842_querystd, |
| 3300 | .g_input_status = adv7842_g_input_status, | 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 | static const struct v4l2_subdev_pad_ops adv7842_pad_ops = { | 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 | .set_fmt = adv7842_set_format, | 3315 | .set_fmt = adv7842_set_format, |
| 3310 | .get_edid = adv7842_get_edid, | 3316 | .get_edid = adv7842_get_edid, |
| 3311 | .set_edid = adv7842_set_edid, | 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 | .enum_dv_timings = adv7842_enum_dv_timings, | 3321 | .enum_dv_timings = adv7842_enum_dv_timings, |
| 3313 | .dv_timings_cap = adv7842_dv_timings_cap, | 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 | MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); | 311 | MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); |
| 312 | MODULE_AUTHOR("Jian Xu Zheng"); | 312 | MODULE_AUTHOR("Jian Xu Zheng"); |
| 313 | MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>"); | 313 | MODULE_AUTHOR("Yuning Pu"); |
| 314 | MODULE_AUTHOR("Jouni Ukkonen <jouni.ukkonen@intel.com>"); | 314 | MODULE_AUTHOR("Jouni Ukkonen"); |
| 315 | MODULE_AUTHOR("Tommi Franttila <tommi.franttila@intel.com>"); | 315 | MODULE_AUTHOR("Tommi Franttila"); |
| 316 | MODULE_DESCRIPTION("DW9714 VCM driver"); | 316 | MODULE_DESCRIPTION("DW9714 VCM driver"); |
| 317 | MODULE_LICENSE("GPL v2"); | 317 | MODULE_LICENSE("GPL v2"); |
| @@ -1475,7 +1475,7 @@ err_mutex: · 1474 unmodified lines | |||
| 1475 | return ret; | 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 | struct v4l2_subdev *subdev = i2c_get_clientdata(client); | 1480 | struct v4l2_subdev *subdev = i2c_get_clientdata(client); |
| 1481 | struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev); | 1481 | struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev); |
| @@ -1517,7 +1517,7 @@ static struct i2c_driver et8ek8_i2c_driver = { · 35 unmodified lines | |||
| 1517 | .of_match_table = et8ek8_of_table, | 1517 | .of_match_table = et8ek8_of_table, |
| 1518 | }, | 1518 | }, |
| 1519 | .probe = et8ek8_probe, | 1519 | .probe = et8ek8_probe, |
| 1520 | .remove = __exit_p(et8ek8_remove), | 1520 | .remove = et8ek8_remove, |
| 1521 | .id_table = et8ek8_id_table, | 1521 | .id_table = et8ek8_id_table, |
| 1522 | }; | 1522 | }; |
| 1523 | 1523 | ||
| @@ -3,10 +3,13 @@ · 2 unmodified lines | |||
| 3 | 3 | ||
| 4 | #include <asm/unaligned.h> | 4 | #include <asm/unaligned.h> |
| 5 | #include <linux/acpi.h> | 5 | #include <linux/acpi.h> |
| 6 | #include <linux/clk.h> | ||
| 6 | #include <linux/delay.h> | 7 | #include <linux/delay.h> |
| 8 | #include <linux/gpio/consumer.h> | ||
| 7 | #include <linux/i2c.h> | 9 | #include <linux/i2c.h> |
| 8 | #include <linux/module.h> | 10 | #include <linux/module.h> |
| 9 | #include <linux/pm_runtime.h> | 11 | #include <linux/pm_runtime.h> |
| 12 | #include <linux/regulator/consumer.h> | ||
| 10 | #include <media/v4l2-ctrls.h> | 13 | #include <media/v4l2-ctrls.h> |
| 11 | #include <media/v4l2-device.h> | 14 | #include <media/v4l2-device.h> |
| 12 | #include <media/v4l2-fwnode.h> | 15 | #include <media/v4l2-fwnode.h> |
| @@ -633,6 +636,11 @@ struct hi556 { · 620 unmodified lines | |||
| 633 | struct v4l2_ctrl *hblank; | 636 | struct v4l2_ctrl *hblank; |
| 634 | struct v4l2_ctrl *exposure; | 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 | /* Current mode */ | 644 | /* Current mode */ |
| 637 | const struct hi556_mode *cur_mode; | 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 | int ret = 0; | 1214 | int ret = 0; |
| 1207 | unsigned int i, j; | 1215 | unsigned int i, j; |
| 1208 | 1216 | ||
| 1209 | if (!fwnode) | 1217 | /* |
| 1210 | return -ENXIO; | 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 | ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); | 1230 | ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); |
| 1213 | if (ret) { | 1231 | if (ret) { |
| @@ -1220,15 +1238,6 @@ static int hi556_check_hwcfg(struct device *dev) · 6 unmodified lines | |||
| 1220 | return -EINVAL; | 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 | if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { | 1241 | if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { |
| 1233 | dev_err(dev, "number of CSI2 data lanes %d is not supported", | 1242 | dev_err(dev, "number of CSI2 data lanes %d is not supported", |
| 1234 | bus_cfg.bus.mipi_csi2.num_data_lanes); | 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 | mutex_destroy(&hi556->mutex); | 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 | static int hi556_probe(struct i2c_client *client) | 1328 | static int hi556_probe(struct i2c_client *client) |
| 1279 | { | 1329 | { |
| 1280 | struct hi556 *hi556; | 1330 | struct hi556 *hi556; |
| @@ -1294,12 +1344,35 @@ static int hi556_probe(struct i2c_client *client) · 13 unmodified lines | |||
| 1294 | 1344 | ||
| 1295 | v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops); | 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 | full_power = acpi_dev_state_d0(&client->dev); | 1364 | full_power = acpi_dev_state_d0(&client->dev); |
| 1298 | if (full_power) { | 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 | ret = hi556_identify_module(hi556); | 1372 | ret = hi556_identify_module(hi556); |
| 1300 | if (ret) { | 1373 | if (ret) { |
| 1301 | dev_err(&client->dev, "failed to find sensor: %d", ret); | 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 | v4l2_ctrl_handler_free(hi556->sd.ctrl_handler); | 1417 | v4l2_ctrl_handler_free(hi556->sd.ctrl_handler); |
| 1345 | mutex_destroy(&hi556->mutex); | 1418 | mutex_destroy(&hi556->mutex); |
| 1346 | 1419 | ||
| 1420 | probe_error_power_off: | ||
| 1421 | if (full_power) | ||
| 1422 | hi556_suspend(&client->dev); | ||
| 1423 | |||
| 1347 | return ret; | 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 | #ifdef CONFIG_ACPI | 1430 | #ifdef CONFIG_ACPI |
| 1351 | static const struct acpi_device_id hi556_acpi_ids[] = { | 1431 | static const struct acpi_device_id hi556_acpi_ids[] = { |
| 1352 | {"INT3537"}, | 1432 | {"INT3537"}, |
| @@ -1360,6 +1440,7 @@ static struct i2c_driver hi556_i2c_driver = { · 7 unmodified lines | |||
| 1360 | .driver = { | 1440 | .driver = { |
| 1361 | .name = "hi556", | 1441 | .name = "hi556", |
| 1362 | .acpi_match_table = ACPI_PTR(hi556_acpi_ids), | 1442 | .acpi_match_table = ACPI_PTR(hi556_acpi_ids), |
| 1443 | .pm = pm_sleep_ptr(&hi556_pm_ops), | ||
| 1363 | }, | 1444 | }, |
| 1364 | .probe = hi556_probe, | 1445 | .probe = hi556_probe, |
| 1365 | .remove = hi556_remove, | 1446 | .remove = hi556_remove, |
| @@ -1114,6 +1114,7 @@ free_ctrl: · 1113 unmodified lines | |||
| 1114 | v4l2_ctrl_handler_free(&imx214->ctrls); | 1114 | v4l2_ctrl_handler_free(&imx214->ctrls); |
| 1115 | error_power_off: | 1115 | error_power_off: |
| 1116 | pm_runtime_disable(imx214->dev); | 1116 | pm_runtime_disable(imx214->dev); |
| 1117 | regulator_bulk_disable(IMX214_NUM_SUPPLIES, imx214->supplies); | ||
| 1117 | 1118 | ||
| 1118 | return ret; | 1119 | return ret; |
| 1119 | } | 1120 | } |
| @@ -551,8 +551,7 @@ static int imx219_init_controls(struct imx219 *imx219) · 550 unmodified lines | |||
| 551 | 551 | ||
| 552 | if (ctrl_hdlr->error) { | 552 | if (ctrl_hdlr->error) { |
| 553 | ret = ctrl_hdlr->error; | 553 | ret = ctrl_hdlr->error; |
| 554 | dev_err(&client->dev, "%s control init failed (%d)\n", | 554 | dev_err_probe(&client->dev, ret, "Control init failed\n"); |
| 555 | __func__, ret); | ||
| 556 | goto error; | 555 | goto error; |
| 557 | } | 556 | } |
| 558 | 557 | ||
| @@ -1024,17 +1023,15 @@ static int imx219_identify_module(struct imx219 *imx219) · 465 unmodified lines | |||
| 1024 | u64 val; | 1023 | u64 val; |
| 1025 | 1024 | ||
| 1026 | ret = cci_read(imx219->regmap, IMX219_REG_CHIP_ID, &val, NULL); | 1025 | ret = cci_read(imx219->regmap, IMX219_REG_CHIP_ID, &val, NULL); |
| 1027 | if (ret) { | 1026 | if (ret) |
| 1028 | dev_err(&client->dev, "failed to read chip id %x\n", | 1027 | return dev_err_probe(&client->dev, ret, |
| 1029 | IMX219_CHIP_ID); | 1028 | "failed to read chip id %x\n", |
| 1030 | return ret; | 1029 | IMX219_CHIP_ID); |
| 1031 | } | ||
| 1032 | 1030 | ||
| 1033 | if (val != IMX219_CHIP_ID) { | 1031 | if (val != IMX219_CHIP_ID) |
| 1034 | dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", | 1032 | return dev_err_probe(&client->dev, -EIO, |
| 1035 | IMX219_CHIP_ID, val); | 1033 | "chip id mismatch: %x!=%llx\n", |
| 1036 | return -EIO; | 1034 | IMX219_CHIP_ID, val); |
| 1037 | } | ||
| 1038 | 1035 | ||
| 1039 | return 0; | 1036 | return 0; |
| 1040 | } | 1037 | } |
| @@ -1048,35 +1045,36 @@ static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) · 7 unmodified lines | |||
| 1048 | int ret = -EINVAL; | 1045 | int ret = -EINVAL; |
| 1049 | 1046 | ||
| 1050 | endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); | 1047 | endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); |
| 1051 | if (!endpoint) { | 1048 | if (!endpoint) |
| 1052 | dev_err(dev, "endpoint node not found\n"); | 1049 | return dev_err_probe(dev, -EINVAL, "endpoint node not found\n"); |
| 1053 | return -EINVAL; | ||
| 1054 | } | ||
| 1055 | 1050 | ||
| 1056 | if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { | 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 | goto error_out; | 1053 | goto error_out; |
| 1059 | } | 1054 | } |
| 1060 | 1055 | ||
| 1061 | /* Check the number of MIPI CSI2 data lanes */ | 1056 | /* Check the number of MIPI CSI2 data lanes */ |
| 1062 | if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2 && | 1057 | if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2 && |
| 1063 | ep_cfg.bus.mipi_csi2.num_data_lanes != 4) { | 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 | goto error_out; | 1061 | goto error_out; |
| 1066 | } | 1062 | } |
| 1067 | imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes; | 1063 | imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes; |
| 1068 | 1064 | ||
| 1069 | /* Check the link frequency set in device tree */ | 1065 | /* Check the link frequency set in device tree */ |
| 1070 | if (!ep_cfg.nr_of_link_frequencies) { | 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 | goto error_out; | 1069 | goto error_out; |
| 1073 | } | 1070 | } |
| 1074 | 1071 | ||
| 1075 | if (ep_cfg.nr_of_link_frequencies != 1 || | 1072 | if (ep_cfg.nr_of_link_frequencies != 1 || |
| 1076 | (ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ? | 1073 | (ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ? |
| 1077 | IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) { | 1074 | IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) { |
| 1078 | dev_err(dev, "Link frequency not supported: %lld\n", | 1075 | dev_err_probe(dev, -EINVAL, |
| 1079 | ep_cfg.link_frequencies[0]); | 1076 | "Link frequency not supported: %lld\n", |
| 1077 | ep_cfg.link_frequencies[0]); | ||
| 1080 | goto error_out; | 1078 | goto error_out; |
| 1081 | } | 1079 | } |
| 1082 | 1080 | ||
| @@ -1107,31 +1105,25 @@ static int imx219_probe(struct i2c_client *client) · 24 unmodified lines | |||
| 1107 | return -EINVAL; | 1105 | return -EINVAL; |
| 1108 | 1106 | ||
| 1109 | imx219->regmap = devm_cci_regmap_init_i2c(client, 16); | 1107 | imx219->regmap = devm_cci_regmap_init_i2c(client, 16); |
| 1110 | if (IS_ERR(imx219->regmap)) { | 1108 | if (IS_ERR(imx219->regmap)) |
| 1111 | ret = PTR_ERR(imx219->regmap); | 1109 | return dev_err_probe(dev, PTR_ERR(imx219->regmap), |
| 1112 | dev_err(dev, "failed to initialize CCI: %d\n", ret); | 1110 | "failed to initialize CCI\n"); |
| 1113 | return ret; | ||
| 1114 | } | ||
| 1115 | 1111 | ||
| 1116 | /* Get system clock (xclk) */ | 1112 | /* Get system clock (xclk) */ |
| 1117 | imx219->xclk = devm_clk_get(dev, NULL); | 1113 | imx219->xclk = devm_clk_get(dev, NULL); |
| 1118 | if (IS_ERR(imx219->xclk)) { | 1114 | if (IS_ERR(imx219->xclk)) |
| 1119 | dev_err(dev, "failed to get xclk\n"); | 1115 | return dev_err_probe(dev, PTR_ERR(imx219->xclk), |
| 1120 | return PTR_ERR(imx219->xclk); | 1116 | "failed to get xclk\n"); |
| 1121 | } | ||
| 1122 | 1117 | ||
| 1123 | imx219->xclk_freq = clk_get_rate(imx219->xclk); | 1118 | imx219->xclk_freq = clk_get_rate(imx219->xclk); |
| 1124 | if (imx219->xclk_freq != IMX219_XCLK_FREQ) { | 1119 | if (imx219->xclk_freq != IMX219_XCLK_FREQ) |
| 1125 | dev_err(dev, "xclk frequency not supported: %d Hz\n", | 1120 | return dev_err_probe(dev, -EINVAL, |
| 1126 | imx219->xclk_freq); | 1121 | "xclk frequency not supported: %d Hz\n", |
| 1127 | return -EINVAL; | 1122 | imx219->xclk_freq); |
| 1128 | } | ||
| 1129 | 1123 | ||
| 1130 | ret = imx219_get_regulators(imx219); | 1124 | ret = imx219_get_regulators(imx219); |
| 1131 | if (ret) { | 1125 | if (ret) |
| 1132 | dev_err(dev, "failed to get regulators\n"); | 1126 | return dev_err_probe(dev, ret, "failed to get regulators\n"); |
| 1133 | return ret; | ||
| 1134 | } | ||
| 1135 | 1127 | ||
| 1136 | /* Request optional enable pin */ | 1128 | /* Request optional enable pin */ |
| 1137 | imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset", | 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 | ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad); | 1176 | ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad); |
| 1185 | if (ret) { | 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 | goto error_handler_free; | 1179 | goto error_handler_free; |
| 1188 | } | 1180 | } |
| 1189 | 1181 | ||
| 1190 | imx219->sd.state_lock = imx219->ctrl_handler.lock; | 1182 | imx219->sd.state_lock = imx219->ctrl_handler.lock; |
| 1191 | ret = v4l2_subdev_init_finalize(&imx219->sd); | 1183 | ret = v4l2_subdev_init_finalize(&imx219->sd); |
| 1192 | if (ret < 0) { | 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 | goto error_media_entity; | 1186 | goto error_media_entity; |
| 1195 | } | 1187 | } |
| 1196 | 1188 | ||
| 1197 | ret = v4l2_async_register_subdev_sensor(&imx219->sd); | 1189 | ret = v4l2_async_register_subdev_sensor(&imx219->sd); |
| 1198 | if (ret < 0) { | 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 | goto error_subdev_cleanup; | 1193 | goto error_subdev_cleanup; |
| 1201 | } | 1194 | } |
| 1202 | 1195 | ||
| @@ -11,57 +11,108 @@ · 10 unmodified lines | |||
| 11 | #include <linux/i2c.h> | 11 | #include <linux/i2c.h> |
| 12 | #include <linux/module.h> | 12 | #include <linux/module.h> |
| 13 | #include <linux/pm_runtime.h> | 13 | #include <linux/pm_runtime.h> |
| 14 | #include <linux/regmap.h> | ||
| 14 | 15 | ||
| 16 | #include <media/v4l2-cci.h> | ||
| 15 | #include <media/v4l2-ctrls.h> | 17 | #include <media/v4l2-ctrls.h> |
| 16 | #include <media/v4l2-fwnode.h> | 18 | #include <media/v4l2-fwnode.h> |
| 17 | #include <media/v4l2-subdev.h> | 19 | #include <media/v4l2-subdev.h> |
| 18 | 20 | ||
| 19 | /* Streaming Mode */ | 21 | /* Streaming Mode */ |
| 20 | #define IMX335_REG_MODE_SELECT 0x3000 | 22 | #define IMX335_REG_MODE_SELECT CCI_REG8(0x3000) |
| 21 | #define IMX335_MODE_STANDBY 0x01 | 23 | #define IMX335_MODE_STANDBY 0x01 |
| 22 | #define IMX335_MODE_STREAMING 0x00 | 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 | /* Lines per frame */ | 36 | /* Lines per frame */ |
| 25 | #define IMX335_REG_LPFR 0x3030 | 37 | #define IMX335_REG_VMAX CCI_REG24_LE(0x3030) |
| 26 | 38 | ||
| 27 | /* Chip ID */ | 39 | #define IMX335_REG_OPB_SIZE_V CCI_REG8(0x304c) |
| 28 | #define IMX335_REG_ID 0x3912 | 40 | #define IMX335_REG_ADBIT CCI_REG8(0x3050) |
| 29 | #define IMX335_ID 0x00 | 41 | #define IMX335_REG_Y_OUT_SIZE CCI_REG16_LE(0x3056) |
| 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 | ||
| 44 | 42 | ||
| 45 | /* Group hold register */ | 43 | #define IMX335_REG_SHUTTER CCI_REG24_LE(0x3058) |
| 46 | #define IMX335_REG_HOLD 0x3001 | 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 | /* Test pattern generator */ | 71 | /* Test pattern generator */ |
| 49 | #define IMX335_REG_TPG 0x329e | 72 | #define IMX335_REG_TPG_DIG_CLP_MODE CCI_REG8(0x3280) |
| 50 | #define IMX335_TPG_ALL_000 0 | 73 | #define IMX335_REG_TPG_EN_DUOUT CCI_REG8(0x329c) |
| 51 | #define IMX335_TPG_ALL_FFF 1 | 74 | #define IMX335_REG_TPG CCI_REG8(0x329e) |
| 52 | #define IMX335_TPG_ALL_555 2 | 75 | #define IMX335_TPG_ALL_000 0 |
| 53 | #define IMX335_TPG_ALL_AAA 3 | 76 | #define IMX335_TPG_ALL_FFF 1 |
| 54 | #define IMX335_TPG_TOG_555_AAA 4 | 77 | #define IMX335_TPG_ALL_555 2 |
| 55 | #define IMX335_TPG_TOG_AAA_555 5 | 78 | #define IMX335_TPG_ALL_AAA 3 |
| 56 | #define IMX335_TPG_TOG_000_555 6 | 79 | #define IMX335_TPG_TOG_555_AAA 4 |
| 57 | #define IMX335_TPG_TOG_555_000 7 | 80 | #define IMX335_TPG_TOG_AAA_555 5 |
| 58 | #define IMX335_TPG_TOG_000_FFF 8 | 81 | #define IMX335_TPG_TOG_000_555 6 |
| 59 | #define IMX335_TPG_TOG_FFF_000 9 | 82 | #define IMX335_TPG_TOG_555_000 7 |
| 60 | #define IMX335_TPG_H_COLOR_BARS 10 | 83 | #define IMX335_TPG_TOG_000_FFF 8 |
| 61 | #define IMX335_TPG_V_COLOR_BARS 11 | 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 | /* Input clock rate */ | 114 | /* Input clock rate */ |
| 64 | #define IMX335_INCLK_RATE 24000000 | 115 | #define IMX335_INCLK_RATE 24000000 |
| 65 | 116 | ||
| 66 | /* CSI2 HW configuration */ | 117 | /* CSI2 HW configuration */ |
| 67 | #define IMX335_LINK_FREQ_594MHz 594000000LL | 118 | #define IMX335_LINK_FREQ_594MHz 594000000LL |
| @@ -69,9 +120,6 @@ · 1 unmodified line | |||
| 69 | 120 | ||
| 70 | #define IMX335_NUM_DATA_LANES 4 | 121 | #define IMX335_NUM_DATA_LANES 4 |
| 71 | 122 | ||
| 72 | #define IMX335_REG_MIN 0x00 | ||
| 73 | #define IMX335_REG_MAX 0xfffff | ||
| 74 | |||
| 75 | /* IMX335 native and active pixel array size. */ | 123 | /* IMX335 native and active pixel array size. */ |
| 76 | #define IMX335_NATIVE_WIDTH 2616U | 124 | #define IMX335_NATIVE_WIDTH 2616U |
| 77 | #define IMX335_NATIVE_HEIGHT 1964U | 125 | #define IMX335_NATIVE_HEIGHT 1964U |
| @@ -80,16 +128,6 @@ · 2 unmodified lines | |||
| 80 | #define IMX335_PIXEL_ARRAY_WIDTH 2592U | 128 | #define IMX335_PIXEL_ARRAY_WIDTH 2592U |
| 81 | #define IMX335_PIXEL_ARRAY_HEIGHT 1944U | 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 | * struct imx335_reg_list - imx335 sensor register list | 132 | * struct imx335_reg_list - imx335 sensor register list |
| 95 | * @num_of_regs: Number of registers in the list | 133 | * @num_of_regs: Number of registers in the list |
| @@ -97,7 +135,7 @@ struct imx335_reg { · 1 unmodified line | |||
| 97 | */ | 135 | */ |
| 98 | struct imx335_reg_list { | 136 | struct imx335_reg_list { |
| 99 | u32 num_of_regs; | 137 | u32 num_of_regs; |
| 100 | const struct imx335_reg *regs; | 138 | const struct cci_reg_sequence *regs; |
| 101 | }; | 139 | }; |
| 102 | 140 | ||
| 103 | static const char * const imx335_supply_name[] = { | 141 | static const char * const imx335_supply_name[] = { |
| @@ -138,6 +176,7 @@ struct imx335_mode { · 34 unmodified lines | |||
| 138 | * @pad: Media pad. Only one pad supported | 176 | * @pad: Media pad. Only one pad supported |
| 139 | * @reset_gpio: Sensor reset gpio | 177 | * @reset_gpio: Sensor reset gpio |
| 140 | * @supplies: Regulator supplies to handle power control | 178 | * @supplies: Regulator supplies to handle power control |
| 179 | * @cci: CCI register map | ||
| 141 | * @inclk: Sensor input clock | 180 | * @inclk: Sensor input clock |
| 142 | * @ctrl_handler: V4L2 control handler | 181 | * @ctrl_handler: V4L2 control handler |
| 143 | * @link_freq_ctrl: Pointer to link frequency control | 182 | * @link_freq_ctrl: Pointer to link frequency control |
| @@ -147,6 +186,7 @@ struct imx335_mode { · 3 unmodified lines | |||
| 147 | * @exp_ctrl: Pointer to exposure control | 186 | * @exp_ctrl: Pointer to exposure control |
| 148 | * @again_ctrl: Pointer to analog gain control | 187 | * @again_ctrl: Pointer to analog gain control |
| 149 | * @vblank: Vertical blanking in lines | 188 | * @vblank: Vertical blanking in lines |
| 189 | * @lane_mode: Mode for number of connected data lanes | ||
| 150 | * @cur_mode: Pointer to current selected sensor mode | 190 | * @cur_mode: Pointer to current selected sensor mode |
| 151 | * @mutex: Mutex for serializing sensor controls | 191 | * @mutex: Mutex for serializing sensor controls |
| 152 | * @link_freq_bitmap: Menu bitmap for link_freq_ctrl | 192 | * @link_freq_bitmap: Menu bitmap for link_freq_ctrl |
| @@ -159,6 +199,7 @@ struct imx335 { · 6 unmodified lines | |||
| 159 | struct media_pad pad; | 199 | struct media_pad pad; |
| 160 | struct gpio_desc *reset_gpio; | 200 | struct gpio_desc *reset_gpio; |
| 161 | struct regulator_bulk_data supplies[ARRAY_SIZE(imx335_supply_name)]; | 201 | struct regulator_bulk_data supplies[ARRAY_SIZE(imx335_supply_name)]; |
| 202 | struct regmap *cci; | ||
| 162 | 203 | ||
| 163 | struct clk *inclk; | 204 | struct clk *inclk; |
| 164 | struct v4l2_ctrl_handler ctrl_handler; | 205 | struct v4l2_ctrl_handler ctrl_handler; |
| @@ -171,6 +212,7 @@ struct imx335 { · 6 unmodified lines | |||
| 171 | struct v4l2_ctrl *again_ctrl; | 212 | struct v4l2_ctrl *again_ctrl; |
| 172 | }; | 213 | }; |
| 173 | u32 vblank; | 214 | u32 vblank; |
| 215 | u32 lane_mode; | ||
| 174 | const struct imx335_mode *cur_mode; | 216 | const struct imx335_mode *cur_mode; |
| 175 | struct mutex mutex; | 217 | struct mutex mutex; |
| 176 | unsigned long link_freq_bitmap; | 218 | unsigned long link_freq_bitmap; |
| @@ -210,140 +252,135 @@ static const int imx335_tpg_val[] = { · 33 unmodified lines | |||
| 210 | }; | 252 | }; |
| 211 | 253 | ||
| 212 | /* Sensor mode registers */ | 254 | /* Sensor mode registers */ |
| 213 | static const struct imx335_reg mode_2592x1940_regs[] = { | 255 | static const struct cci_reg_sequence mode_2592x1940_regs[] = { |
| 214 | {0x3000, 0x01}, | 256 | { IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY }, |
| 215 | {0x3002, 0x00}, | 257 | { IMX335_REG_MASTER_MODE, 0x00 }, |
| 216 | {0x3018, 0x04}, | 258 | { IMX335_REG_WINMODE, 0x04 }, |
| 217 | {0x302c, 0x3c}, | 259 | { IMX335_REG_HTRIMMING_START, 48 }, |
| 218 | {0x302e, 0x20}, | 260 | { IMX335_REG_HNUM, 2592 }, |
| 219 | {0x3056, 0x94}, | 261 | { IMX335_REG_Y_OUT_SIZE, 1944 }, |
| 220 | {0x3074, 0xc8}, | 262 | { IMX335_REG_AREA3_ST_ADR_1, 176 }, |
| 221 | {0x3076, 0x28}, | 263 | { IMX335_REG_AREA3_WIDTH_1, 3928 }, |
| 222 | {0x304c, 0x00}, | 264 | { IMX335_REG_OPB_SIZE_V, 0 }, |
| 223 | {0x31a1, 0x00}, | 265 | { IMX335_REG_XVS_XHS_DRV, 0x00 }, |
| 224 | {0x3288, 0x21}, | 266 | { CCI_REG8(0x3288), 0x21 }, |
| 225 | {0x328a, 0x02}, | 267 | { CCI_REG8(0x328a), 0x02 }, |
| 226 | {0x3414, 0x05}, | 268 | { CCI_REG8(0x3414), 0x05 }, |
| 227 | {0x3416, 0x18}, | 269 | { CCI_REG8(0x3416), 0x18 }, |
| 228 | {0x3648, 0x01}, | 270 | { CCI_REG8(0x3648), 0x01 }, |
| 229 | {0x364a, 0x04}, | 271 | { CCI_REG8(0x364a), 0x04 }, |
| 230 | {0x364c, 0x04}, | 272 | { CCI_REG8(0x364c), 0x04 }, |
| 231 | {0x3678, 0x01}, | 273 | { CCI_REG8(0x3678), 0x01 }, |
| 232 | {0x367c, 0x31}, | 274 | { CCI_REG8(0x367c), 0x31 }, |
| 233 | {0x367e, 0x31}, | 275 | { CCI_REG8(0x367e), 0x31 }, |
| 234 | {0x3706, 0x10}, | 276 | { CCI_REG8(0x3706), 0x10 }, |
| 235 | {0x3708, 0x03}, | 277 | { CCI_REG8(0x3708), 0x03 }, |
| 236 | {0x3714, 0x02}, | 278 | { CCI_REG8(0x3714), 0x02 }, |
| 237 | {0x3715, 0x02}, | 279 | { CCI_REG8(0x3715), 0x02 }, |
| 238 | {0x3716, 0x01}, | 280 | { CCI_REG8(0x3716), 0x01 }, |
| 239 | {0x3717, 0x03}, | 281 | { CCI_REG8(0x3717), 0x03 }, |
| 240 | {0x371c, 0x3d}, | 282 | { CCI_REG8(0x371c), 0x3d }, |
| 241 | {0x371d, 0x3f}, | 283 | { CCI_REG8(0x371d), 0x3f }, |
| 242 | {0x372c, 0x00}, | 284 | { CCI_REG8(0x372c), 0x00 }, |
| 243 | {0x372d, 0x00}, | 285 | { CCI_REG8(0x372d), 0x00 }, |
| 244 | {0x372e, 0x46}, | 286 | { CCI_REG8(0x372e), 0x46 }, |
| 245 | {0x372f, 0x00}, | 287 | { CCI_REG8(0x372f), 0x00 }, |
| 246 | {0x3730, 0x89}, | 288 | { CCI_REG8(0x3730), 0x89 }, |
| 247 | {0x3731, 0x00}, | 289 | { CCI_REG8(0x3731), 0x00 }, |
| 248 | {0x3732, 0x08}, | 290 | { CCI_REG8(0x3732), 0x08 }, |
| 249 | {0x3733, 0x01}, | 291 | { CCI_REG8(0x3733), 0x01 }, |
| 250 | {0x3734, 0xfe}, | 292 | { CCI_REG8(0x3734), 0xfe }, |
| 251 | {0x3735, 0x05}, | 293 | { CCI_REG8(0x3735), 0x05 }, |
| 252 | {0x3740, 0x02}, | 294 | { CCI_REG8(0x3740), 0x02 }, |
| 253 | {0x375d, 0x00}, | 295 | { CCI_REG8(0x375d), 0x00 }, |
| 254 | {0x375e, 0x00}, | 296 | { CCI_REG8(0x375e), 0x00 }, |
| 255 | {0x375f, 0x11}, | 297 | { CCI_REG8(0x375f), 0x11 }, |
| 256 | {0x3760, 0x01}, | 298 | { CCI_REG8(0x3760), 0x01 }, |
| 257 | {0x3768, 0x1b}, | 299 | { CCI_REG8(0x3768), 0x1b }, |
| 258 | {0x3769, 0x1b}, | 300 | { CCI_REG8(0x3769), 0x1b }, |
| 259 | {0x376a, 0x1b}, | 301 | { CCI_REG8(0x376a), 0x1b }, |
| 260 | {0x376b, 0x1b}, | 302 | { CCI_REG8(0x376b), 0x1b }, |
| 261 | {0x376c, 0x1a}, | 303 | { CCI_REG8(0x376c), 0x1a }, |
| 262 | {0x376d, 0x17}, | 304 | { CCI_REG8(0x376d), 0x17 }, |
| 263 | {0x376e, 0x0f}, | 305 | { CCI_REG8(0x376e), 0x0f }, |
| 264 | {0x3776, 0x00}, | 306 | { CCI_REG8(0x3776), 0x00 }, |
| 265 | {0x3777, 0x00}, | 307 | { CCI_REG8(0x3777), 0x00 }, |
| 266 | {0x3778, 0x46}, | 308 | { CCI_REG8(0x3778), 0x46 }, |
| 267 | {0x3779, 0x00}, | 309 | { CCI_REG8(0x3779), 0x00 }, |
| 268 | {0x377a, 0x89}, | 310 | { CCI_REG8(0x377a), 0x89 }, |
| 269 | {0x377b, 0x00}, | 311 | { CCI_REG8(0x377b), 0x00 }, |
| 270 | {0x377c, 0x08}, | 312 | { CCI_REG8(0x377c), 0x08 }, |
| 271 | {0x377d, 0x01}, | 313 | { CCI_REG8(0x377d), 0x01 }, |
| 272 | {0x377e, 0x23}, | 314 | { CCI_REG8(0x377e), 0x23 }, |
| 273 | {0x377f, 0x02}, | 315 | { CCI_REG8(0x377f), 0x02 }, |
| 274 | {0x3780, 0xd9}, | 316 | { CCI_REG8(0x3780), 0xd9 }, |
| 275 | {0x3781, 0x03}, | 317 | { CCI_REG8(0x3781), 0x03 }, |
| 276 | {0x3782, 0xf5}, | 318 | { CCI_REG8(0x3782), 0xf5 }, |
| 277 | {0x3783, 0x06}, | 319 | { CCI_REG8(0x3783), 0x06 }, |
| 278 | {0x3784, 0xa5}, | 320 | { CCI_REG8(0x3784), 0xa5 }, |
| 279 | {0x3788, 0x0f}, | 321 | { CCI_REG8(0x3788), 0x0f }, |
| 280 | {0x378a, 0xd9}, | 322 | { CCI_REG8(0x378a), 0xd9 }, |
| 281 | {0x378b, 0x03}, | 323 | { CCI_REG8(0x378b), 0x03 }, |
| 282 | {0x378c, 0xeb}, | 324 | { CCI_REG8(0x378c), 0xeb }, |
| 283 | {0x378d, 0x05}, | 325 | { CCI_REG8(0x378d), 0x05 }, |
| 284 | {0x378e, 0x87}, | 326 | { CCI_REG8(0x378e), 0x87 }, |
| 285 | {0x378f, 0x06}, | 327 | { CCI_REG8(0x378f), 0x06 }, |
| 286 | {0x3790, 0xf5}, | 328 | { CCI_REG8(0x3790), 0xf5 }, |
| 287 | {0x3792, 0x43}, | 329 | { CCI_REG8(0x3792), 0x43 }, |
| 288 | {0x3794, 0x7a}, | 330 | { CCI_REG8(0x3794), 0x7a }, |
| 289 | {0x3796, 0xa1}, | 331 | { CCI_REG8(0x3796), 0xa1 }, |
| 290 | {0x37b0, 0x36}, | 332 | { CCI_REG8(0x37b0), 0x36 }, |
| 291 | {0x3a00, 0x00}, | 333 | { CCI_REG8(0x3a00), 0x00 }, |
| 292 | }; | 334 | }; |
| 293 | 335 | ||
| 294 | static const struct imx335_reg raw10_framefmt_regs[] = { | 336 | static const struct cci_reg_sequence raw10_framefmt_regs[] = { |
| 295 | {0x3050, 0x00}, | 337 | { IMX335_REG_ADBIT, 0x00 }, |
| 296 | {0x319d, 0x00}, | 338 | { IMX335_REG_MDBIT, 0x00 }, |
| 297 | {0x341c, 0xff}, | 339 | { IMX335_REG_ADBIT1, 0x1ff }, |
| 298 | {0x341d, 0x01}, | ||
| 299 | }; | 340 | }; |
| 300 | 341 | ||
| 301 | static const struct imx335_reg raw12_framefmt_regs[] = { | 342 | static const struct cci_reg_sequence raw12_framefmt_regs[] = { |
| 302 | {0x3050, 0x01}, | 343 | { IMX335_REG_ADBIT, 0x01 }, |
| 303 | {0x319d, 0x01}, | 344 | { IMX335_REG_MDBIT, 0x01 }, |
| 304 | {0x341c, 0x47}, | 345 | { IMX335_REG_ADBIT1, 0x47 }, |
| 305 | {0x341d, 0x00}, | ||
| 306 | }; | 346 | }; |
| 307 | 347 | ||
| 308 | static const struct imx335_reg mipi_data_rate_1188Mbps[] = { | 348 | static const struct cci_reg_sequence mipi_data_rate_1188Mbps[] = { |
| 309 | {0x300c, 0x3b}, | 349 | { IMX335_REG_BCWAIT_TIME, 0x3b }, |
| 310 | {0x300d, 0x2a}, | 350 | { IMX335_REG_CPWAIT_TIME, 0x2a }, |
| 311 | {0x314c, 0xc6}, | 351 | { IMX335_REG_INCLKSEL1, 0x00c6 }, |
| 312 | {0x314d, 0x00}, | 352 | { IMX335_REG_INCLKSEL2, 0x02 }, |
| 313 | {0x315a, 0x02}, | 353 | { IMX335_REG_INCLKSEL3, 0xa0 }, |
| 314 | {0x3168, 0xa0}, | 354 | { IMX335_REG_INCLKSEL4, 0x7e }, |
| 315 | {0x316a, 0x7e}, | 355 | { IMX335_REG_SYSMODE, 0x01 }, |
| 316 | {0x319e, 0x01}, | 356 | { IMX335_REG_TCLKPOST, 0x8f }, |
| 317 | {0x3a18, 0x8f}, | 357 | { IMX335_REG_TCLKPREPARE, 0x4f }, |
| 318 | {0x3a1a, 0x4f}, | 358 | { IMX335_REG_TCLK_TRAIL, 0x47 }, |
| 319 | {0x3a1c, 0x47}, | 359 | { IMX335_REG_TCLK_ZERO, 0x0137 }, |
| 320 | {0x3a1e, 0x37}, | 360 | { IMX335_REG_THS_PREPARE, 0x4f }, |
| 321 | {0x3a1f, 0x01}, | 361 | { IMX335_REG_THS_ZERO, 0x87 }, |
| 322 | {0x3a20, 0x4f}, | 362 | { IMX335_REG_THS_TRAIL, 0x4f }, |
| 323 | {0x3a22, 0x87}, | 363 | { IMX335_REG_THS_EXIT, 0x7f }, |
| 324 | {0x3a24, 0x4f}, | 364 | { IMX335_REG_TPLX, 0x3f }, |
| 325 | {0x3a26, 0x7f}, | ||
| 326 | {0x3a28, 0x3f}, | ||
| 327 | }; | 365 | }; |
| 328 | 366 | ||
| 329 | static const struct imx335_reg mipi_data_rate_891Mbps[] = { | 367 | static const struct cci_reg_sequence mipi_data_rate_891Mbps[] = { |
| 330 | {0x300c, 0x3b}, | 368 | { IMX335_REG_BCWAIT_TIME, 0x3b }, |
| 331 | {0x300d, 0x2a}, | 369 | { IMX335_REG_CPWAIT_TIME, 0x2a }, |
| 332 | {0x314c, 0x29}, | 370 | { IMX335_REG_INCLKSEL1, 0x0129 }, |
| 333 | {0x314d, 0x01}, | 371 | { IMX335_REG_INCLKSEL2, 0x06 }, |
| 334 | {0x315a, 0x06}, | 372 | { IMX335_REG_INCLKSEL3, 0xa0 }, |
| 335 | {0x3168, 0xa0}, | 373 | { IMX335_REG_INCLKSEL4, 0x7e }, |
| 336 | {0x316a, 0x7e}, | 374 | { IMX335_REG_SYSMODE, 0x02 }, |
| 337 | {0x319e, 0x02}, | 375 | { IMX335_REG_TCLKPOST, 0x7f }, |
| 338 | {0x3a18, 0x7f}, | 376 | { IMX335_REG_TCLKPREPARE, 0x37 }, |
| 339 | {0x3a1a, 0x37}, | 377 | { IMX335_REG_TCLK_TRAIL, 0x37 }, |
| 340 | {0x3a1c, 0x37}, | 378 | { IMX335_REG_TCLK_ZERO, 0xf7 }, |
| 341 | {0x3a1e, 0xf7}, | 379 | { IMX335_REG_THS_PREPARE, 0x3f }, |
| 342 | {0x3a20, 0x3f}, | 380 | { IMX335_REG_THS_ZERO, 0x6f }, |
| 343 | {0x3a22, 0x6f}, | 381 | { IMX335_REG_THS_TRAIL, 0x3f }, |
| 344 | {0x3a24, 0x3f}, | 382 | { IMX335_REG_THS_EXIT, 0x5f }, |
| 345 | {0x3a26, 0x5f}, | 383 | { IMX335_REG_TPLX, 0x2f }, |
| 346 | {0x3a28, 0x2f}, | ||
| 347 | }; | 384 | }; |
| 348 | 385 | ||
| 349 | static const s64 link_freq[] = { | 386 | static const s64 link_freq[] = { |
| @@ -372,10 +409,10 @@ static const u32 imx335_mbus_codes[] = { · 22 unmodified lines | |||
| 372 | /* Supported sensor mode configurations */ | 409 | /* Supported sensor mode configurations */ |
| 373 | static const struct imx335_mode supported_mode = { | 410 | static const struct imx335_mode supported_mode = { |
| 374 | .width = 2592, | 411 | .width = 2592, |
| 375 | .height = 1940, | 412 | .height = 1944, |
| 376 | .hblank = 342, | 413 | .hblank = 342, |
| 377 | .vblank = 2560, | 414 | .vblank = 2556, |
| 378 | .vblank_min = 2560, | 415 | .vblank_min = 2556, |
| 379 | .vblank_max = 133060, | 416 | .vblank_max = 133060, |
| 380 | .pclk = 396000000, | 417 | .pclk = 396000000, |
| 381 | .reg_list = { | 418 | .reg_list = { |
| @@ -395,101 +432,6 @@ static inline struct imx335 *to_imx335(struct v4l2_subdev *subdev) · 13 unmodified lines | |||
| 395 | return container_of(subdev, struct imx335, sd); | 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 | * imx335_update_controls() - Update control ranges based on streaming mode | 436 | * imx335_update_controls() - Update control ranges based on streaming mode |
| 495 | * @imx335: pointer to imx335 device | 437 | * @imx335: pointer to imx335 device |
| @@ -526,7 +468,8 @@ static int imx335_update_controls(struct imx335 *imx335, · 30 unmodified lines | |||
| 526 | static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain) | 468 | static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain) |
| 527 | { | 469 | { |
| 528 | u32 lpfr, shutter; | 470 | u32 lpfr, shutter; |
| 529 | int ret; | 471 | int ret_hold; |
| 472 | int ret = 0; | ||
| 530 | 473 | ||
| 531 | lpfr = imx335->vblank + imx335->cur_mode->height; | 474 | lpfr = imx335->vblank + imx335->cur_mode->height; |
| 532 | shutter = lpfr - exposure; | 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 | dev_dbg(imx335->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u\n", | 477 | dev_dbg(imx335->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u\n", |
| 535 | exposure, gain, shutter, lpfr); | 478 | exposure, gain, shutter, lpfr); |
| 536 | 479 | ||
| 537 | ret = imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 1); | 480 | cci_write(imx335->cci, IMX335_REG_HOLD, 1, &ret); |
| 538 | if (ret) | 481 | cci_write(imx335->cci, IMX335_REG_VMAX, lpfr, &ret); |
| 539 | return ret; | 482 | cci_write(imx335->cci, IMX335_REG_SHUTTER, shutter, &ret); |
| 540 | 483 | cci_write(imx335->cci, IMX335_REG_GAIN, gain, &ret); | |
| 541 | ret = imx335_write_reg(imx335, IMX335_REG_LPFR, 3, lpfr); | 484 | /* |
| 542 | if (ret) | 485 | * Unconditionally attempt to release the hold, but track the |
| 543 | goto error_release_group_hold; | 486 | * error if the unhold itself fails. |
| 544 | 487 | */ | |
| 545 | ret = imx335_write_reg(imx335, IMX335_REG_SHUTTER, 3, shutter); | 488 | ret_hold = cci_write(imx335->cci, IMX335_REG_HOLD, 0, NULL); |
| 546 | if (ret) | 489 | if (ret_hold) |
| 547 | goto error_release_group_hold; | 490 | ret = ret_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); | ||
| 553 | 491 | ||
| 554 | return ret; | 492 | return ret; |
| 555 | } | 493 | } |
| 556 | 494 | ||
| 557 | static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index) | 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 | if (pattern_index >= ARRAY_SIZE(imx335_tpg_val)) | 499 | if (pattern_index >= ARRAY_SIZE(imx335_tpg_val)) |
| 562 | return -EINVAL; | 500 | return -EINVAL; |
| 563 | 501 | ||
| 564 | if (pattern_index) { | 502 | if (pattern_index) { |
| 565 | const struct imx335_reg tpg_enable_regs[] = { | 503 | const struct cci_reg_sequence tpg_enable_regs[] = { |
| 566 | { 0x3148, 0x10 }, | 504 | { IMX335_REG_TPG_TESTCLKEN, 0x10 }, |
| 567 | { 0x3280, 0x00 }, | 505 | { IMX335_REG_TPG_DIG_CLP_MODE, 0x00 }, |
| 568 | { 0x329c, 0x01 }, | 506 | { IMX335_REG_TPG_EN_DUOUT, 0x01 }, |
| 569 | { 0x32a0, 0x11 }, | 507 | { IMX335_REG_TPG_COLORWIDTH, 0x11 }, |
| 570 | { 0x3302, 0x00 }, | 508 | { IMX335_REG_BLKLEVEL, 0x00 }, |
| 571 | { 0x3303, 0x00 }, | 509 | { IMX335_REG_WRJ_OPEN, 0x00 }, |
| 572 | { 0x336c, 0x00 }, | ||
| 573 | }; | 510 | }; |
| 574 | 511 | ||
| 575 | ret = imx335_write_reg(imx335, IMX335_REG_TPG, 1, | 512 | cci_write(imx335->cci, IMX335_REG_TPG, |
| 576 | imx335_tpg_val[pattern_index]); | 513 | imx335_tpg_val[pattern_index], &ret); |
| 577 | if (ret) | ||
| 578 | return ret; | ||
| 579 | 514 | ||
| 580 | ret = imx335_write_regs(imx335, tpg_enable_regs, | 515 | cci_multi_reg_write(imx335->cci, tpg_enable_regs, |
| 581 | ARRAY_SIZE(tpg_enable_regs)); | 516 | ARRAY_SIZE(tpg_enable_regs), &ret); |
| 582 | } else { | 517 | } else { |
| 583 | const struct imx335_reg tpg_disable_regs[] = { | 518 | const struct cci_reg_sequence tpg_disable_regs[] = { |
| 584 | { 0x3148, 0x00 }, | 519 | { IMX335_REG_TPG_TESTCLKEN, 0x00 }, |
| 585 | { 0x3280, 0x01 }, | 520 | { IMX335_REG_TPG_DIG_CLP_MODE, 0x01 }, |
| 586 | { 0x329c, 0x00 }, | 521 | { IMX335_REG_TPG_EN_DUOUT, 0x00 }, |
| 587 | { 0x32a0, 0x10 }, | 522 | { IMX335_REG_TPG_COLORWIDTH, 0x10 }, |
| 588 | { 0x3302, 0x32 }, | 523 | { IMX335_REG_BLKLEVEL, 0x32 }, |
| 589 | { 0x3303, 0x00 }, | 524 | { IMX335_REG_WRJ_OPEN, 0x01 }, |
| 590 | { 0x336c, 0x01 }, | ||
| 591 | }; | 525 | }; |
| 592 | 526 | ||
| 593 | ret = imx335_write_regs(imx335, tpg_disable_regs, | 527 | cci_multi_reg_write(imx335->cci, tpg_disable_regs, |
| 594 | ARRAY_SIZE(tpg_disable_regs)); | 528 | ARRAY_SIZE(tpg_disable_regs), &ret); |
| 595 | } | 529 | } |
| 596 | 530 | ||
| 597 | return ret; | 531 | return ret; |
| @@ -890,12 +824,14 @@ static int imx335_set_framefmt(struct imx335 *imx335) · 292 unmodified lines | |||
| 890 | { | 824 | { |
| 891 | switch (imx335->cur_mbus_code) { | 825 | switch (imx335->cur_mbus_code) { |
| 892 | case MEDIA_BUS_FMT_SRGGB10_1X10: | 826 | case MEDIA_BUS_FMT_SRGGB10_1X10: |
| 893 | return imx335_write_regs(imx335, raw10_framefmt_regs, | 827 | return cci_multi_reg_write(imx335->cci, raw10_framefmt_regs, |
| 894 | ARRAY_SIZE(raw10_framefmt_regs)); | 828 | ARRAY_SIZE(raw10_framefmt_regs), |
| 829 | NULL); | ||
| 895 | 830 | ||
| 896 | case MEDIA_BUS_FMT_SRGGB12_1X12: | 831 | case MEDIA_BUS_FMT_SRGGB12_1X12: |
| 897 | return imx335_write_regs(imx335, raw12_framefmt_regs, | 832 | return cci_multi_reg_write(imx335->cci, raw12_framefmt_regs, |
| 898 | ARRAY_SIZE(raw12_framefmt_regs)); | 833 | ARRAY_SIZE(raw12_framefmt_regs), |
| 834 | NULL); | ||
| 899 | } | 835 | } |
| 900 | 836 | ||
| 901 | return -EINVAL; | 837 | return -EINVAL; |
| @@ -914,7 +850,8 @@ static int imx335_start_streaming(struct imx335 *imx335) · 12 unmodified lines | |||
| 914 | 850 | ||
| 915 | /* Setup PLL */ | 851 | /* Setup PLL */ |
| 916 | reg_list = &link_freq_reglist[__ffs(imx335->link_freq_bitmap)]; | 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 | if (ret) { | 855 | if (ret) { |
| 919 | dev_err(imx335->dev, "%s failed to set plls\n", __func__); | 856 | dev_err(imx335->dev, "%s failed to set plls\n", __func__); |
| 920 | return ret; | 857 | return ret; |
| @@ -922,8 +859,8 @@ static int imx335_start_streaming(struct imx335 *imx335) · 1 unmodified line | |||
| 922 | 859 | ||
| 923 | /* Write sensor mode registers */ | 860 | /* Write sensor mode registers */ |
| 924 | reg_list = &imx335->cur_mode->reg_list; | 861 | reg_list = &imx335->cur_mode->reg_list; |
| 925 | ret = imx335_write_regs(imx335, reg_list->regs, | 862 | ret = cci_multi_reg_write(imx335->cci, reg_list->regs, |
| 926 | reg_list->num_of_regs); | 863 | reg_list->num_of_regs, NULL); |
| 927 | if (ret) { | 864 | if (ret) { |
| 928 | dev_err(imx335->dev, "fail to write initial registers\n"); | 865 | dev_err(imx335->dev, "fail to write initial registers\n"); |
| 929 | return ret; | 866 | return ret; |
| @@ -936,6 +873,12 @@ static int imx335_start_streaming(struct imx335 *imx335) · 6 unmodified lines | |||
| 936 | return ret; | 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 | /* Setup handler will write actual exposure and gain */ | 882 | /* Setup handler will write actual exposure and gain */ |
| 940 | ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler); | 883 | ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler); |
| 941 | if (ret) { | 884 | if (ret) { |
| @@ -944,8 +887,8 @@ static int imx335_start_streaming(struct imx335 *imx335) · 2 unmodified lines | |||
| 944 | } | 887 | } |
| 945 | 888 | ||
| 946 | /* Start streaming */ | 889 | /* Start streaming */ |
| 947 | ret = imx335_write_reg(imx335, IMX335_REG_MODE_SELECT, | 890 | ret = cci_write(imx335->cci, IMX335_REG_MODE_SELECT, |
| 948 | 1, IMX335_MODE_STREAMING); | 891 | IMX335_MODE_STREAMING, NULL); |
| 949 | if (ret) { | 892 | if (ret) { |
| 950 | dev_err(imx335->dev, "fail to start streaming\n"); | 893 | dev_err(imx335->dev, "fail to start streaming\n"); |
| 951 | return ret; | 894 | return ret; |
| @@ -965,8 +908,8 @@ static int imx335_start_streaming(struct imx335 *imx335) · 13 unmodified lines | |||
| 965 | */ | 908 | */ |
| 966 | static int imx335_stop_streaming(struct imx335 *imx335) | 909 | static int imx335_stop_streaming(struct imx335 *imx335) |
| 967 | { | 910 | { |
| 968 | return imx335_write_reg(imx335, IMX335_REG_MODE_SELECT, | 911 | return cci_write(imx335->cci, IMX335_REG_MODE_SELECT, |
| 969 | 1, IMX335_MODE_STANDBY); | 912 | IMX335_MODE_STANDBY, NULL); |
| 970 | } | 913 | } |
| 971 | 914 | ||
| 972 | /** | 915 | /** |
| @@ -1017,14 +960,14 @@ error_unlock: · 44 unmodified lines | |||
| 1017 | static int imx335_detect(struct imx335 *imx335) | 960 | static int imx335_detect(struct imx335 *imx335) |
| 1018 | { | 961 | { |
| 1019 | int ret; | 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 | if (ret) | 966 | if (ret) |
| 1024 | return ret; | 967 | return ret; |
| 1025 | 968 | ||
| 1026 | if (val != IMX335_ID) { | 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 | IMX335_ID, val); | 971 | IMX335_ID, val); |
| 1029 | return -ENXIO; | 972 | return -ENXIO; |
| 1030 | } | 973 | } |
| @@ -1096,7 +1039,14 @@ static int imx335_parse_hw_config(struct imx335 *imx335) · 65 unmodified lines | |||
| 1096 | if (ret) | 1039 | if (ret) |
| 1097 | return ret; | 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 | dev_err(imx335->dev, | 1050 | dev_err(imx335->dev, |
| 1101 | "number of CSI2 data lanes %d is not supported\n", | 1051 | "number of CSI2 data lanes %d is not supported\n", |
| 1102 | bus_cfg.bus.mipi_csi2.num_data_lanes); | 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 | struct v4l2_ctrl_handler *ctrl_hdlr = &imx335->ctrl_handler; | 1159 | struct v4l2_ctrl_handler *ctrl_hdlr = &imx335->ctrl_handler; |
| 1210 | const struct imx335_mode *mode = imx335->cur_mode; | 1160 | const struct imx335_mode *mode = imx335->cur_mode; |
| 1161 | struct v4l2_fwnode_device_properties props; | ||
| 1211 | u32 lpfr; | 1162 | u32 lpfr; |
| 1212 | int ret; | 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 | if (ret) | 1171 | if (ret) |
| 1216 | return ret; | 1172 | return ret; |
| 1217 | 1173 | ||
| @@ -1228,6 +1184,14 @@ static int imx335_init_controls(struct imx335 *imx335) · 10 unmodified lines | |||
| 1228 | IMX335_EXPOSURE_STEP, | 1184 | IMX335_EXPOSURE_STEP, |
| 1229 | IMX335_EXPOSURE_DEFAULT); | 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 | imx335->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, | 1195 | imx335->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, |
| 1232 | &imx335_ctrl_ops, | 1196 | &imx335_ctrl_ops, |
| 1233 | V4L2_CID_ANALOGUE_GAIN, | 1197 | V4L2_CID_ANALOGUE_GAIN, |
| @@ -1276,6 +1240,8 @@ static int imx335_init_controls(struct imx335 *imx335) · 42 unmodified lines | |||
| 1276 | if (imx335->hblank_ctrl) | 1240 | if (imx335->hblank_ctrl) |
| 1277 | imx335->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; | 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 | if (ctrl_hdlr->error) { | 1245 | if (ctrl_hdlr->error) { |
| 1280 | dev_err(imx335->dev, "control init failed: %d\n", | 1246 | dev_err(imx335->dev, "control init failed: %d\n", |
| 1281 | ctrl_hdlr->error); | 1247 | ctrl_hdlr->error); |
| @@ -1304,6 +1270,11 @@ static int imx335_probe(struct i2c_client *client) · 22 unmodified lines | |||
| 1304 | return -ENOMEM; | 1270 | return -ENOMEM; |
| 1305 | 1271 | ||
| 1306 | imx335->dev = &client->dev; | 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 | /* Initialize subdev */ | 1279 | /* Initialize subdev */ |
| 1309 | v4l2_i2c_subdev_init(&imx335->sd, client, &imx335_subdev_ops); | 1280 | v4l2_i2c_subdev_init(&imx335->sd, client, &imx335_subdev_ops); |
| @@ -8,6 +8,9 @@ · 7 unmodified lines | |||
| 8 | * Copyright (C) 2015 Cogent Embedded, Inc. | 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 | #include <linux/i2c.h> | 14 | #include <linux/i2c.h> |
| 12 | 15 | ||
| 13 | #define MAX9271_DEFAULT_ADDR 0x40 | 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 | * Return 0 on success or a negative error code on failure | 234 | * Return 0 on success or a negative error code on failure |
| 232 | */ | 235 | */ |
| 233 | int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest); | 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 | #define OV2680_ACTIVE_START_TOP 8 | 75 | #define OV2680_ACTIVE_START_TOP 8 |
| 76 | #define OV2680_MIN_CROP_WIDTH 2 | 76 | #define OV2680_MIN_CROP_WIDTH 2 |
| 77 | #define OV2680_MIN_CROP_HEIGHT 2 | 77 | #define OV2680_MIN_CROP_HEIGHT 2 |
| 78 | #define OV2680_MIN_VBLANK 4 | ||
| 79 | #define OV2680_MAX_VBLANK 0xffff | ||
| 78 | 80 | ||
| 79 | /* Fixed pre-div of 1/2 */ | 81 | /* Fixed pre-div of 1/2 */ |
| 80 | #define OV2680_PLL_PREDIV0 2 | 82 | #define OV2680_PLL_PREDIV0 2 |
| @@ -84,10 +86,7 @@ · 3 unmodified lines | |||
| 84 | 86 | ||
| 85 | /* 66MHz pixel clock: 66MHz / 1704 * 1294 = 30fps */ | 87 | /* 66MHz pixel clock: 66MHz / 1704 * 1294 = 30fps */ |
| 86 | #define OV2680_PIXELS_PER_LINE 1704 | 88 | #define OV2680_PIXELS_PER_LINE 1704 |
| 87 | #define OV2680_LINES_PER_FRAME 1294 | 89 | #define OV2680_LINES_PER_FRAME_30FPS 1294 |
| 88 | |||
| 89 | /* If possible send 16 extra rows / lines to the ISP as padding */ | ||
| 90 | #define OV2680_END_MARGIN 16 | ||
| 91 | 90 | ||
| 92 | /* Max exposure time is VTS - 8 */ | 91 | /* Max exposure time is VTS - 8 */ |
| 93 | #define OV2680_INTEGRATION_TIME_MARGIN 8 | 92 | #define OV2680_INTEGRATION_TIME_MARGIN 8 |
| @@ -130,6 +129,8 @@ struct ov2680_ctrls { · 36 unmodified lines | |||
| 130 | struct v4l2_ctrl *test_pattern; | 129 | struct v4l2_ctrl *test_pattern; |
| 131 | struct v4l2_ctrl *link_freq; | 130 | struct v4l2_ctrl *link_freq; |
| 132 | struct v4l2_ctrl *pixel_rate; | 131 | struct v4l2_ctrl *pixel_rate; |
| 132 | struct v4l2_ctrl *vblank; | ||
| 133 | struct v4l2_ctrl *hblank; | ||
| 133 | }; | 134 | }; |
| 134 | 135 | ||
| 135 | struct ov2680_mode { | 136 | struct ov2680_mode { |
| @@ -143,8 +144,6 @@ struct ov2680_mode { · 7 unmodified lines | |||
| 143 | u16 v_end; | 144 | u16 v_end; |
| 144 | u16 h_output_size; | 145 | u16 h_output_size; |
| 145 | u16 v_output_size; | 146 | u16 v_output_size; |
| 146 | u16 hts; | ||
| 147 | u16 vts; | ||
| 148 | }; | 147 | }; |
| 149 | 148 | ||
| 150 | struct ov2680_dev { | 149 | struct ov2680_dev { |
| @@ -359,15 +358,11 @@ static void ov2680_calc_mode(struct ov2680_dev *sensor) · 208 unmodified lines | |||
| 359 | sensor->mode.v_start = (sensor->mode.crop.top + | 358 | sensor->mode.v_start = (sensor->mode.crop.top + |
| 360 | (sensor->mode.crop.height - height) / 2) & ~1; | 359 | (sensor->mode.crop.height - height) / 2) & ~1; |
| 361 | sensor->mode.h_end = | 360 | sensor->mode.h_end = |
| 362 | min(sensor->mode.h_start + width + OV2680_END_MARGIN - 1, | 361 | min(sensor->mode.h_start + width - 1, OV2680_NATIVE_WIDTH - 1); |
| 363 | OV2680_NATIVE_WIDTH - 1); | ||
| 364 | sensor->mode.v_end = | 362 | sensor->mode.v_end = |
| 365 | min(sensor->mode.v_start + height + OV2680_END_MARGIN - 1, | 363 | min(sensor->mode.v_start + height - 1, OV2680_NATIVE_HEIGHT - 1); |
| 366 | OV2680_NATIVE_HEIGHT - 1); | ||
| 367 | sensor->mode.h_output_size = orig_width; | 364 | sensor->mode.h_output_size = orig_width; |
| 368 | sensor->mode.v_output_size = orig_height; | 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 | static int ov2680_set_mode(struct ov2680_dev *sensor) | 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 | cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE, | 397 | cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE, |
| 403 | sensor->mode.v_output_size, &ret); | 398 | sensor->mode.v_output_size, &ret); |
| 404 | cci_write(sensor->regmap, OV2680_REG_TIMING_HTS, | 399 | cci_write(sensor->regmap, OV2680_REG_TIMING_HTS, |
| 405 | sensor->mode.hts, &ret); | 400 | OV2680_PIXELS_PER_LINE, &ret); |
| 406 | cci_write(sensor->regmap, OV2680_REG_TIMING_VTS, | 401 | /* VTS gets set by the vblank ctrl */ |
| 407 | sensor->mode.vts, &ret); | ||
| 408 | cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret); | 402 | cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret); |
| 409 | cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret); | 403 | cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret); |
| 410 | cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret); | 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 | NULL); | 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 | static int ov2680_stream_enable(struct ov2680_dev *sensor) | 484 | static int ov2680_stream_enable(struct ov2680_dev *sensor) |
| 482 | { | 485 | { |
| 483 | int ret; | 486 | int ret; |
| @@ -644,7 +647,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, · 160 unmodified lines | |||
| 644 | struct v4l2_mbus_framefmt *try_fmt; | 647 | struct v4l2_mbus_framefmt *try_fmt; |
| 645 | const struct v4l2_rect *crop; | 648 | const struct v4l2_rect *crop; |
| 646 | unsigned int width, height; | 649 | unsigned int width, height; |
| 647 | int ret = 0; | 650 | int def, max, ret = 0; |
| 648 | 651 | ||
| 649 | crop = __ov2680_get_pad_crop(sensor, sd_state, format->pad, | 652 | crop = __ov2680_get_pad_crop(sensor, sd_state, format->pad, |
| 650 | format->which); | 653 | format->which); |
| @@ -673,6 +676,27 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, · 22 unmodified lines | |||
| 673 | sensor->mode.fmt = format->format; | 676 | sensor->mode.fmt = format->format; |
| 674 | ov2680_calc_mode(sensor); | 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 | unlock: | 700 | unlock: |
| 677 | mutex_unlock(&sensor->lock); | 701 | mutex_unlock(&sensor->lock); |
| 678 | 702 | ||
| @@ -842,6 +866,13 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl) · 163 unmodified lines | |||
| 842 | struct ov2680_dev *sensor = to_ov2680_dev(sd); | 866 | struct ov2680_dev *sensor = to_ov2680_dev(sd); |
| 843 | int ret; | 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 | /* Only apply changes to the controls if the device is powered up */ | 876 | /* Only apply changes to the controls if the device is powered up */ |
| 846 | if (!pm_runtime_get_if_in_use(sensor->sd.dev)) { | 877 | if (!pm_runtime_get_if_in_use(sensor->sd.dev)) { |
| 847 | ov2680_set_bayer_order(sensor, &sensor->mode.fmt); | 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 | case V4L2_CID_TEST_PATTERN: | 895 | case V4L2_CID_TEST_PATTERN: |
| 865 | ret = ov2680_test_pattern_set(sensor, ctrl->val); | 896 | ret = ov2680_test_pattern_set(sensor, ctrl->val); |
| 866 | break; | 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 | default: | 902 | default: |
| 868 | ret = -EINVAL; | 903 | ret = -EINVAL; |
| 869 | break; | 904 | break; |
| @@ -922,8 +957,8 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) · 52 unmodified lines | |||
| 922 | const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops; | 957 | const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops; |
| 923 | struct ov2680_ctrls *ctrls = &sensor->ctrls; | 958 | struct ov2680_ctrls *ctrls = &sensor->ctrls; |
| 924 | struct v4l2_ctrl_handler *hdl = &ctrls->handler; | 959 | struct v4l2_ctrl_handler *hdl = &ctrls->handler; |
| 925 | int exp_max = OV2680_LINES_PER_FRAME - OV2680_INTEGRATION_TIME_MARGIN; | 960 | struct v4l2_fwnode_device_properties props; |
| 926 | int ret = 0; | 961 | int def, max, ret = 0; |
| 927 | 962 | ||
| 928 | v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops); | 963 | v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops); |
| 929 | sensor->sd.internal_ops = &ov2680_internal_ops; | 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 | ARRAY_SIZE(test_pattern_menu) - 1, | 983 | ARRAY_SIZE(test_pattern_menu) - 1, |
| 949 | 0, 0, test_pattern_menu); | 984 | 0, 0, test_pattern_menu); |
| 950 | 985 | ||
| 986 | max = OV2680_LINES_PER_FRAME_30FPS - OV2680_INTEGRATION_TIME_MARGIN; | ||
| 951 | ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, | 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 | ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, | 990 | ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, |
| 955 | 0, 1023, 1, 250); | 991 | 0, 1023, 1, 250); |
| @@ -960,6 +996,21 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) · 4 unmodified lines | |||
| 960 | 0, sensor->pixel_rate, | 996 | 0, sensor->pixel_rate, |
| 961 | 1, sensor->pixel_rate); | 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 | if (hdl->error) { | 1014 | if (hdl->error) { |
| 964 | ret = hdl->error; | 1015 | ret = hdl->error; |
| 965 | goto cleanup_entity; | 1016 | goto cleanup_entity; |
| @@ -968,6 +1019,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) · 2 unmodified lines | |||
| 968 | ctrls->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; | 1019 | ctrls->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; |
| 969 | ctrls->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; | 1020 | ctrls->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; |
| 970 | ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; | 1021 | ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; |
| 1022 | ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; | ||
| 971 | 1023 | ||
| 972 | sensor->sd.ctrl_handler = hdl; | 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 | sensor->pixel_rate = sensor->link_freq[0] * 2; | 1168 | sensor->pixel_rate = sensor->link_freq[0] * 2; |
| 1117 | do_div(sensor->pixel_rate, 10); | 1169 | do_div(sensor->pixel_rate, 10); |
| 1118 | 1170 | ||
| 1119 | /* Verify bus cfg */ | 1171 | if (!bus_cfg.nr_of_link_frequencies) { |
| 1120 | if (bus_cfg.bus.mipi_csi2.num_data_lanes != 1) { | 1172 | dev_warn(dev, "Consider passing 'link-frequencies' in DT\n"); |
| 1121 | ret = dev_err_probe(dev, -EINVAL, | 1173 | goto skip_link_freq_validation; |
| 1122 | "only a 1-lane CSI2 config is supported"); | ||
| 1123 | goto out_free_bus_cfg; | ||
| 1124 | } | 1174 | } |
| 1125 | 1175 | ||
| 1126 | for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) | 1176 | for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) |
| 1127 | if (bus_cfg.link_frequencies[i] == sensor->link_freq[0]) | 1177 | if (bus_cfg.link_frequencies[i] == sensor->link_freq[0]) |
| 1128 | break; | 1178 | break; |
| 1129 | 1179 | ||
| 1130 | if (bus_cfg.nr_of_link_frequencies == 0 || | 1180 | if (bus_cfg.nr_of_link_frequencies == i) { |
| 1131 | bus_cfg.nr_of_link_frequencies == i) { | ||
| 1132 | ret = dev_err_probe(dev, -EINVAL, | 1181 | ret = dev_err_probe(dev, -EINVAL, |
| 1133 | "supported link freq %lld not found\n", | 1182 | "supported link freq %lld not found\n", |
| 1134 | sensor->link_freq[0]); | 1183 | sensor->link_freq[0]); |
| 1135 | goto out_free_bus_cfg; | 1184 | goto out_free_bus_cfg; |
| 1136 | } | 1185 | } |
| 1137 | 1186 | ||
| 1187 | skip_link_freq_validation: | ||
| 1188 | ret = 0; | ||
| 1138 | out_free_bus_cfg: | 1189 | out_free_bus_cfg: |
| 1139 | v4l2_fwnode_endpoint_free(&bus_cfg); | 1190 | v4l2_fwnode_endpoint_free(&bus_cfg); |
| 1140 | return ret; | 1191 | return ret; |
| @@ -768,14 +768,15 @@ static int ov2740_init_controls(struct ov2740 *ov2740) · 767 unmodified lines | |||
| 768 | cur_mode = ov2740->cur_mode; | 768 | cur_mode = ov2740->cur_mode; |
| 769 | size = ARRAY_SIZE(link_freq_menu_items); | 769 | size = ARRAY_SIZE(link_freq_menu_items); |
| 770 | 770 | ||
| 771 | ov2740->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops, | 771 | ov2740->link_freq = |
| 772 | V4L2_CID_LINK_FREQ, | 772 | v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops, |
| 773 | size - 1, 0, | 773 | V4L2_CID_LINK_FREQ, size - 1, |
| 774 | link_freq_menu_items); | 774 | ov2740->supported_modes->link_freq_index, |
| 775 | link_freq_menu_items); | ||
| 775 | if (ov2740->link_freq) | 776 | if (ov2740->link_freq) |
| 776 | ov2740->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; | 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 | ov2740->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, | 780 | ov2740->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, |
| 780 | V4L2_CID_PIXEL_RATE, 0, | 781 | V4L2_CID_PIXEL_RATE, 0, |
| 781 | pixel_rate, 1, pixel_rate); | 782 | pixel_rate, 1, pixel_rate); |
| @@ -1332,9 +1333,16 @@ static int ov2740_probe(struct i2c_client *client) · 550 unmodified lines | |||
| 1332 | return dev_err_probe(dev, ret, "failed to check HW configuration\n"); | 1333 | return dev_err_probe(dev, ret, "failed to check HW configuration\n"); |
| 1333 | 1334 | ||
| 1334 | ov2740->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); | 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 | return dev_err_probe(dev, PTR_ERR(ov2740->reset_gpio), | 1337 | return dev_err_probe(dev, PTR_ERR(ov2740->reset_gpio), |
| 1337 | "failed to get reset GPIO\n"); | 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 | ov2740->clk = devm_clk_get_optional(dev, "clk"); | 1347 | ov2740->clk = devm_clk_get_optional(dev, "clk"); |
| 1340 | if (IS_ERR(ov2740->clk)) | 1348 | if (IS_ERR(ov2740->clk)) |
| @@ -3,7 +3,7 @@ · 2 unmodified lines | |||
| 3 | * ov4689 driver | 3 | * ov4689 driver |
| 4 | * | 4 | * |
| 5 | * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. | 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 | #include <linux/clk.h> | 9 | #include <linux/clk.h> |
| @@ -15,45 +15,86 @@ · 5 unmodified lines | |||
| 15 | #include <linux/regulator/consumer.h> | 15 | #include <linux/regulator/consumer.h> |
| 16 | #include <media/media-entity.h> | 16 | #include <media/media-entity.h> |
| 17 | #include <media/v4l2-async.h> | 17 | #include <media/v4l2-async.h> |
| 18 | #include <media/v4l2-cci.h> | ||
| 18 | #include <media/v4l2-ctrls.h> | 19 | #include <media/v4l2-ctrls.h> |
| 19 | #include <media/v4l2-subdev.h> | 20 | #include <media/v4l2-subdev.h> |
| 20 | #include <media/v4l2-fwnode.h> | 21 | #include <media/v4l2-fwnode.h> |
| 21 | 22 | ||
| 22 | #define CHIP_ID 0x004688 | 23 | #define OV4689_REG_CTRL_MODE CCI_REG8(0x0100) |
| 23 | #define OV4689_REG_CHIP_ID 0x300a | ||
| 24 | |||
| 25 | #define OV4689_XVCLK_FREQ 24000000 | ||
| 26 | |||
| 27 | #define OV4689_REG_CTRL_MODE 0x0100 | ||
| 28 | #define OV4689_MODE_SW_STANDBY 0x0 | 24 | #define OV4689_MODE_SW_STANDBY 0x0 |
| 29 | #define OV4689_MODE_STREAMING BIT(0) | 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 | #define OV4689_EXPOSURE_MIN 4 | 31 | #define OV4689_EXPOSURE_MIN 4 |
| 33 | #define OV4689_EXPOSURE_STEP 1 | 32 | #define OV4689_EXPOSURE_STEP 1 |
| 34 | #define OV4689_VTS_MAX 0x7fff | ||
| 35 | 33 | ||
| 36 | #define OV4689_REG_GAIN_H 0x3508 | 34 | #define OV4689_REG_GAIN CCI_REG16(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 | ||
| 41 | #define OV4689_GAIN_STEP 1 | 35 | #define OV4689_GAIN_STEP 1 |
| 42 | #define OV4689_GAIN_DEFAULT 0x80 | 36 | #define OV4689_GAIN_DEFAULT 0x80 |
| 43 | 37 | ||
| 44 | #define OV4689_REG_TEST_PATTERN 0x5040 | 38 | #define OV4689_REG_DIG_GAIN CCI_REG16(0x352a) |
| 45 | #define OV4689_TEST_PATTERN_ENABLE 0x80 | 39 | #define OV4689_DIG_GAIN_MIN 1 |
| 46 | #define OV4689_TEST_PATTERN_DISABLE 0x0 | 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 | 55 | #define OV4689_REG_VTS CCI_REG16(0x380e) |
| 53 | #define OV4689_REG_VALUE_16BIT 2 | 56 | #define OV4689_VTS_MAX 0x7fff |
| 54 | #define OV4689_REG_VALUE_24BIT 3 | 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 | #define OV4689_LANES 4 | 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 | static const char *const ov4689_supply_names[] = { | 99 | static const char *const ov4689_supply_names[] = { |
| 59 | "avdd", /* Analog power */ | 100 | "avdd", /* Analog power */ |
| @@ -61,11 +102,6 @@ static const char *const ov4689_supply_names[] = { · 1 unmodified line | |||
| 61 | "dvdd", /* Digital core power */ | 102 | "dvdd", /* Digital core power */ |
| 62 | }; | 103 | }; |
| 63 | 104 | ||
| 64 | struct regval { | ||
| 65 | u16 addr; | ||
| 66 | u8 val; | ||
| 67 | }; | ||
| 68 | |||
| 69 | enum ov4689_mode_id { | 105 | enum ov4689_mode_id { |
| 70 | OV4689_MODE_2688_1520 = 0, | 106 | OV4689_MODE_2688_1520 = 0, |
| 71 | OV4689_NUM_MODES, | 107 | OV4689_NUM_MODES, |
| @@ -75,20 +111,18 @@ struct ov4689_mode { · 3 unmodified lines | |||
| 75 | enum ov4689_mode_id id; | 111 | enum ov4689_mode_id id; |
| 76 | u32 width; | 112 | u32 width; |
| 77 | u32 height; | 113 | u32 height; |
| 78 | u32 max_fps; | ||
| 79 | u32 hts_def; | 114 | u32 hts_def; |
| 115 | u32 hts_min; | ||
| 80 | u32 vts_def; | 116 | u32 vts_def; |
| 81 | u32 exp_def; | 117 | u32 exp_def; |
| 82 | u32 pixel_rate; | 118 | u32 pixel_rate; |
| 83 | u32 sensor_width; | 119 | const struct cci_reg_sequence *reg_list; |
| 84 | u32 sensor_height; | 120 | unsigned int num_regs; |
| 85 | u32 crop_top; | ||
| 86 | u32 crop_left; | ||
| 87 | const struct regval *reg_list; | ||
| 88 | }; | 121 | }; |
| 89 | 122 | ||
| 90 | struct ov4689 { | 123 | struct ov4689 { |
| 91 | struct i2c_client *client; | 124 | struct device *dev; |
| 125 | struct regmap *regmap; | ||
| 92 | struct clk *xvclk; | 126 | struct clk *xvclk; |
| 93 | struct gpio_desc *reset_gpio; | 127 | struct gpio_desc *reset_gpio; |
| 94 | struct gpio_desc *pwdn_gpio; | 128 | struct gpio_desc *pwdn_gpio; |
| @@ -99,7 +133,6 @@ struct ov4689 { · 4 unmodified lines | |||
| 99 | 133 | ||
| 100 | u32 clock_rate; | 134 | u32 clock_rate; |
| 101 | 135 | ||
| 102 | struct mutex mutex; /* lock to protect ctrls and cur_mode */ | ||
| 103 | struct v4l2_ctrl_handler ctrl_handler; | 136 | struct v4l2_ctrl_handler ctrl_handler; |
| 104 | struct v4l2_ctrl *exposure; | 137 | struct v4l2_ctrl *exposure; |
| 105 | 138 | ||
| @@ -119,95 +152,108 @@ struct ov4689_gain_range { · 13 unmodified lines | |||
| 119 | 152 | ||
| 120 | /* | 153 | /* |
| 121 | * Xclk 24Mhz | 154 | * Xclk 24Mhz |
| 122 | * max_framerate 30fps | 155 | * max_framerate 90fps |
| 123 | * mipi_datarate per lane 1008Mbps | 156 | * mipi_datarate per lane 1008Mbps |
| 124 | */ | 157 | */ |
| 125 | static const struct regval ov4689_2688x1520_regs[] = { | 158 | static const struct cci_reg_sequence ov4689_2688x1520_regs[] = { |
| 126 | {0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00}, | 159 | /* System control*/ |
| 127 | {0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03}, | 160 | { CCI_REG8(0x0103), 0x01 }, /* SC_CTRL0103 software_reset = 1 */ |
| 128 | {0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04}, | 161 | { CCI_REG8(0x3000), 0x20 }, /* SC_CMMN_PAD_OEN0 FSIN_output_enable = 1 */ |
| 129 | {0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00}, | 162 | { CCI_REG8(0x3021), 0x03 }, /* |
| 130 | {0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72}, | 163 | * SC_CMMN_MISC_CTRL fst_stby_ctr = 0, |
| 131 | {0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01}, | 164 | * sleep_no_latch_enable = 0 |
| 132 | {0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1}, | 165 | */ |
| 133 | {0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00}, | 166 | |
| 134 | {0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04}, | 167 | /* AEC PK */ |
| 135 | {0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00}, | 168 | { CCI_REG8(0x3503), 0x04 }, /* AEC_MANUAL gain_input_as_sensor_gain_format = 1 */ |
| 136 | {0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80}, | 169 | |
| 137 | {0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00}, | 170 | /* ADC and analog control*/ |
| 138 | {0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80}, | 171 | { CCI_REG8(0x3603), 0x40 }, |
| 139 | {0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00}, | 172 | { CCI_REG8(0x3604), 0x02 }, |
| 140 | {0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80}, | 173 | { CCI_REG8(0x3609), 0x12 }, |
| 141 | {0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00}, | 174 | { CCI_REG8(0x360c), 0x08 }, |
| 142 | {0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80}, | 175 | { CCI_REG8(0x360f), 0xe5 }, |
| 143 | {0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00}, | 176 | { CCI_REG8(0x3608), 0x8f }, |
| 144 | {0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80}, | 177 | { CCI_REG8(0x3611), 0x00 }, |
| 145 | {0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08}, | 178 | { CCI_REG8(0x3613), 0xf7 }, |
| 146 | {0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00}, | 179 | { CCI_REG8(0x3616), 0x58 }, |
| 147 | {0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00}, | 180 | { CCI_REG8(0x3619), 0x99 }, |
| 148 | {0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12}, | 181 | { CCI_REG8(0x361b), 0x60 }, |
| 149 | {0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5}, | 182 | { CCI_REG8(0x361e), 0x79 }, |
| 150 | {0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7}, | 183 | { CCI_REG8(0x3634), 0x10 }, |
| 151 | {0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60}, | 184 | { CCI_REG8(0x3635), 0x10 }, |
| 152 | {0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02}, | 185 | { CCI_REG8(0x3636), 0x15 }, |
| 153 | {0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10}, | 186 | { CCI_REG8(0x3646), 0x86 }, |
| 154 | {0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86}, | 187 | { CCI_REG8(0x364a), 0x0b }, |
| 155 | {0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22}, | 188 | |
| 156 | {0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00}, | 189 | /* Sensor control */ |
| 157 | {0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01}, | 190 | { CCI_REG8(0x3700), 0x17 }, |
| 158 | {0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c}, | 191 | { CCI_REG8(0x3701), 0x22 }, |
| 159 | {0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b}, | 192 | { CCI_REG8(0x3703), 0x10 }, |
| 160 | {0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc}, | 193 | { CCI_REG8(0x370a), 0x37 }, |
| 161 | {0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33}, | 194 | { CCI_REG8(0x3706), 0x63 }, |
| 162 | {0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0}, | 195 | { CCI_REG8(0x3709), 0x3c }, |
| 163 | {0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23}, | 196 | { CCI_REG8(0x370c), 0x30 }, |
| 164 | {0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd}, | 197 | { CCI_REG8(0x3710), 0x24 }, |
| 165 | {0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00}, | 198 | { CCI_REG8(0x3720), 0x28 }, |
| 166 | {0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00}, | 199 | { CCI_REG8(0x3729), 0x7b }, |
| 167 | {0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04}, | 200 | { CCI_REG8(0x372b), 0xbd }, |
| 168 | {0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08}, | 201 | { CCI_REG8(0x372c), 0xbc }, |
| 169 | {0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00}, | 202 | { CCI_REG8(0x372e), 0x52 }, |
| 170 | {0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51}, | 203 | { CCI_REG8(0x373c), 0x0e }, |
| 171 | {0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18}, | 204 | { CCI_REG8(0x373e), 0x33 }, |
| 172 | {0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00}, | 205 | { CCI_REG8(0x3743), 0x10 }, |
| 173 | {0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04}, | 206 | { CCI_REG8(0x3744), 0x88 }, |
| 174 | {0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05}, | 207 | { CCI_REG8(0x3745), 0xc0 }, |
| 175 | {0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80}, | 208 | { CCI_REG8(0x374c), 0x00 }, |
| 176 | {0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a}, | 209 | { CCI_REG8(0x374e), 0x23 }, |
| 177 | {0x380d, 0x0e}, {0x380e, 0x06}, {0x380f, 0x12}, | 210 | { CCI_REG8(0x3751), 0x7b }, |
| 178 | {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00}, | 211 | { CCI_REG8(0x3753), 0xbd }, |
| 179 | {0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01}, | 212 | { CCI_REG8(0x3754), 0xbc }, |
| 180 | {0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06}, | 213 | { CCI_REG8(0x3756), 0x52 }, |
| 181 | {0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01}, | 214 | { CCI_REG8(0x376b), 0x20 }, |
| 182 | {0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01}, | 215 | { CCI_REG8(0x3774), 0x51 }, |
| 183 | {0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08}, | 216 | { CCI_REG8(0x3776), 0xbd }, |
| 184 | {0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71}, | 217 | { CCI_REG8(0x3777), 0xbd }, |
| 185 | {0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1}, | 218 | { CCI_REG8(0x3781), 0x18 }, |
| 186 | {0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14}, | 219 | { CCI_REG8(0x3783), 0x25 }, |
| 187 | {0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00}, | 220 | { CCI_REG8(0x3798), 0x1b }, |
| 188 | {0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00}, | 221 | |
| 189 | {0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10}, | 222 | /* Timing control */ |
| 190 | {0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09}, | 223 | { CCI_REG8(0x3819), 0x01 }, /* VSYNC_END_L vsync_end_point[7:0] = 0x01 */ |
| 191 | {0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f}, | 224 | |
| 192 | {0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06}, | 225 | /* OTP control */ |
| 193 | {0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02}, | 226 | { CCI_REG8(0x3d85), 0x36 }, /* OTP_REG85 OTP_power_up_load_setting_enable = 1, |
| 194 | {0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff}, | 227 | * OTP_power_up_load_data_enable = 1, |
| 195 | {0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00}, | 228 | * OTP_bist_select = 1 (compare with zero) |
| 196 | {0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c}, | 229 | */ |
| 197 | {0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01}, | 230 | { CCI_REG8(0x3d8c), 0x71 }, /* OTP_SETTING_STT_ADDRESS_H */ |
| 198 | {0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08}, | 231 | { CCI_REG8(0x3d8d), 0xcb }, /* OTP_SETTING_STT_ADDRESS_L */ |
| 199 | {0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10}, | 232 | |
| 200 | {0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04}, | 233 | /* BLC registers*/ |
| 201 | {0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93}, | 234 | { CCI_REG8(0x4001), 0x40 }, /* DEBUG_MODE */ |
| 202 | {0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3}, | 235 | { CCI_REG8(0x401b), 0x00 }, /* DEBUG_MODE */ |
| 203 | {0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00}, | 236 | { CCI_REG8(0x401d), 0x00 }, /* DEBUG_MODE */ |
| 204 | {0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00}, | 237 | { CCI_REG8(0x401f), 0x00 }, /* DEBUG_MODE */ |
| 205 | {0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10}, | 238 | |
| 206 | {0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00}, | 239 | /* ADC sync control */ |
| 207 | {0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00}, | 240 | { CCI_REG8(0x4500), 0x6c }, /* ADC_SYNC_CTRL */ |
| 208 | {0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00}, | 241 | { CCI_REG8(0x4503), 0x01 }, /* ADC_SYNC_CTRL */ |
| 209 | {0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00}, | 242 | |
| 210 | {REG_NULL, 0x00}, | 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 | static const struct ov4689_mode supported_modes[] = { | 259 | static const struct ov4689_mode supported_modes[] = { |
| @@ -215,16 +261,13 @@ static const struct ov4689_mode supported_modes[] = { · 1 unmodified line | |||
| 215 | .id = OV4689_MODE_2688_1520, | 261 | .id = OV4689_MODE_2688_1520, |
| 216 | .width = 2688, | 262 | .width = 2688, |
| 217 | .height = 1520, | 263 | .height = 1520, |
| 218 | .sensor_width = 2720, | ||
| 219 | .sensor_height = 1536, | ||
| 220 | .crop_top = 8, | ||
| 221 | .crop_left = 16, | ||
| 222 | .max_fps = 30, | ||
| 223 | .exp_def = 1536, | 264 | .exp_def = 1536, |
| 224 | .hts_def = 4 * 2574, | 265 | .hts_def = 10296, |
| 266 | .hts_min = 3432, | ||
| 225 | .vts_def = 1554, | 267 | .vts_def = 1554, |
| 226 | .pixel_rate = 480000000, | 268 | .pixel_rate = 480000000, |
| 227 | .reg_list = ov4689_2688x1520_regs, | 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 | static void ov4689_fill_fmt(const struct ov4689_mode *mode, | 323 | static void ov4689_fill_fmt(const struct ov4689_mode *mode, |
| 358 | struct v4l2_mbus_framefmt *fmt) | 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 | return 0; | 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 | static int ov4689_enum_mbus_code(struct v4l2_subdev *sd, | 345 | static int ov4689_enum_mbus_code(struct v4l2_subdev *sd, |
| 393 | struct v4l2_subdev_state *sd_state, | 346 | struct v4l2_subdev_state *sd_state, |
| 394 | struct v4l2_subdev_mbus_code_enum *code) | 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 | else | 380 | else |
| 428 | val = OV4689_TEST_PATTERN_DISABLE; | 381 | val = OV4689_TEST_PATTERN_DISABLE; |
| 429 | 382 | ||
| 430 | return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN, | 383 | return cci_write(ov4689->regmap, OV4689_REG_TEST_PATTERN, |
| 431 | OV4689_REG_VALUE_08BIT, val); | 384 | val, NULL); |
| 432 | } | 385 | } |
| 433 | 386 | ||
| 434 | static int ov4689_get_selection(struct v4l2_subdev *sd, | 387 | static int ov4689_get_selection(struct v4l2_subdev *sd, |
| 435 | struct v4l2_subdev_state *state, | 388 | struct v4l2_subdev_state *state, |
| 436 | struct v4l2_subdev_selection *sel) | 389 | struct v4l2_subdev_selection *sel) |
| 437 | { | 390 | { |
| 438 | const struct ov4689_mode *mode = to_ov4689(sd)->cur_mode; | ||
| 439 | |||
| 440 | if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) | 391 | if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) |
| 441 | return -EINVAL; | 392 | return -EINVAL; |
| 442 | 393 | ||
| @@ -444,63 +395,114 @@ static int ov4689_get_selection(struct v4l2_subdev *sd, · 1 unmodified line | |||
| 444 | case V4L2_SEL_TGT_CROP_BOUNDS: | 395 | case V4L2_SEL_TGT_CROP_BOUNDS: |
| 445 | sel->r.top = 0; | 396 | sel->r.top = 0; |
| 446 | sel->r.left = 0; | 397 | sel->r.left = 0; |
| 447 | sel->r.width = mode->sensor_width; | 398 | sel->r.width = OV4689_PIXEL_ARRAY_WIDTH; |
| 448 | sel->r.height = mode->sensor_height; | 399 | sel->r.height = OV4689_PIXEL_ARRAY_HEIGHT; |
| 449 | return 0; | 400 | return 0; |
| 450 | case V4L2_SEL_TGT_CROP: | 401 | case V4L2_SEL_TGT_CROP: |
| 451 | case V4L2_SEL_TGT_CROP_DEFAULT: | 402 | case V4L2_SEL_TGT_CROP_DEFAULT: |
| 452 | sel->r.top = mode->crop_top; | 403 | sel->r.top = OV4689_DUMMY_ROWS; |
| 453 | sel->r.left = mode->crop_left; | 404 | sel->r.left = OV4689_DUMMY_COLUMNS; |
| 454 | sel->r.width = mode->width; | 405 | sel->r.width = |
| 455 | sel->r.height = mode->height; | 406 | OV4689_PIXEL_ARRAY_WIDTH - 2 * OV4689_DUMMY_COLUMNS; |
| 407 | sel->r.height = | ||
| 408 | OV4689_PIXEL_ARRAY_HEIGHT - 2 * OV4689_DUMMY_ROWS; | ||
| 456 | return 0; | 409 | return 0; |
| 457 | } | 410 | } |
| 458 | 411 | ||
| 459 | return -EINVAL; | 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 | static int ov4689_s_stream(struct v4l2_subdev *sd, int on) | 450 | static int ov4689_s_stream(struct v4l2_subdev *sd, int on) |
| 463 | { | 451 | { |
| 464 | struct ov4689 *ov4689 = to_ov4689(sd); | 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 | int ret = 0; | 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 | if (on) { | 459 | if (on) { |
| 471 | ret = pm_runtime_resume_and_get(&client->dev); | 460 | ret = pm_runtime_resume_and_get(dev); |
| 472 | if (ret < 0) | 461 | if (ret < 0) |
| 473 | goto unlock_and_return; | 462 | goto unlock_and_return; |
| 474 | 463 | ||
| 475 | ret = ov4689_write_array(ov4689->client, | 464 | ret = cci_multi_reg_write(ov4689->regmap, |
| 476 | ov4689->cur_mode->reg_list); | 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 | if (ret) { | 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 | goto unlock_and_return; | 482 | goto unlock_and_return; |
| 480 | } | 483 | } |
| 481 | 484 | ||
| 482 | ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler); | 485 | ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler); |
| 483 | if (ret) { | 486 | if (ret) { |
| 484 | pm_runtime_put(&client->dev); | 487 | pm_runtime_put(dev); |
| 485 | goto unlock_and_return; | 488 | goto unlock_and_return; |
| 486 | } | 489 | } |
| 487 | 490 | ||
| 488 | ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE, | 491 | ret = cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE, |
| 489 | OV4689_REG_VALUE_08BIT, | 492 | OV4689_MODE_STREAMING, NULL); |
| 490 | OV4689_MODE_STREAMING); | ||
| 491 | if (ret) { | 493 | if (ret) { |
| 492 | pm_runtime_put(&client->dev); | 494 | pm_runtime_put(dev); |
| 493 | goto unlock_and_return; | 495 | goto unlock_and_return; |
| 494 | } | 496 | } |
| 495 | } else { | 497 | } else { |
| 496 | ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE, | 498 | cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE, |
| 497 | OV4689_REG_VALUE_08BIT, | 499 | OV4689_MODE_SW_STANDBY, NULL); |
| 498 | OV4689_MODE_SW_STANDBY); | 500 | pm_runtime_mark_last_busy(dev); |
| 499 | pm_runtime_put(&client->dev); | 501 | pm_runtime_put_autosuspend(dev); |
| 500 | } | 502 | } |
| 501 | 503 | ||
| 502 | unlock_and_return: | 504 | unlock_and_return: |
| 503 | mutex_unlock(&ov4689->mutex); | 505 | v4l2_subdev_unlock_state(sd_state); |
| 504 | 506 | ||
| 505 | return ret; | 507 | return ret; |
| 506 | } | 508 | } |
| @@ -563,18 +565,13 @@ static int __maybe_unused ov4689_power_off(struct device *dev) · 56 unmodified lines | |||
| 563 | return 0; | 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); | 571 | struct v4l2_mbus_framefmt *fmt = |
| 569 | struct v4l2_mbus_framefmt *try_fmt; | 572 | v4l2_subdev_state_get_format(sd_state, 0); |
| 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); | ||
| 576 | 573 | ||
| 577 | mutex_unlock(&ov4689->mutex); | 574 | ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], fmt); |
| 578 | 575 | ||
| 579 | return 0; | 576 | return 0; |
| 580 | } | 577 | } |
| @@ -583,10 +580,6 @@ static const struct dev_pm_ops ov4689_pm_ops = { · 2 unmodified lines | |||
| 583 | SET_RUNTIME_PM_OPS(ov4689_power_off, ov4689_power_on, NULL) | 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 | static const struct v4l2_subdev_video_ops ov4689_video_ops = { | 583 | static const struct v4l2_subdev_video_ops ov4689_video_ops = { |
| 591 | .s_stream = ov4689_s_stream, | 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 | static const struct v4l2_subdev_pad_ops ov4689_pad_ops = { | 587 | static const struct v4l2_subdev_pad_ops ov4689_pad_ops = { |
| 595 | .enum_mbus_code = ov4689_enum_mbus_code, | 588 | .enum_mbus_code = ov4689_enum_mbus_code, |
| 596 | .enum_frame_size = ov4689_enum_frame_sizes, | 589 | .enum_frame_size = ov4689_enum_frame_sizes, |
| 597 | .get_fmt = ov4689_get_fmt, | 590 | .get_fmt = v4l2_subdev_get_fmt, |
| 598 | .set_fmt = ov4689_set_fmt, | 591 | .set_fmt = ov4689_set_fmt, |
| 599 | .get_selection = ov4689_get_selection, | 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 | static const struct v4l2_subdev_ops ov4689_subdev_ops = { | 599 | static const struct v4l2_subdev_ops ov4689_subdev_ops = { |
| 603 | .video = &ov4689_video_ops, | 600 | .video = &ov4689_video_ops, |
| 604 | .pad = &ov4689_pad_ops, | 601 | .pad = &ov4689_pad_ops, |
| @@ -610,7 +607,6 @@ static const struct v4l2_subdev_ops ov4689_subdev_ops = { · 5 unmodified lines | |||
| 610 | */ | 607 | */ |
| 611 | static int ov4689_map_gain(struct ov4689 *ov4689, int logical_gain, int *result) | 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 | const struct ov4689_gain_range *range; | 610 | const struct ov4689_gain_range *range; |
| 615 | unsigned int n; | 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 | if (n == ARRAY_SIZE(ov4689_gain_ranges)) { | 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 | logical_gain); | 622 | logical_gain); |
| 626 | return -EINVAL; | 623 | return -EINVAL; |
| 627 | } | 624 | } |
| @@ -637,10 +634,11 @@ static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl) · 9 unmodified lines | |||
| 637 | { | 634 | { |
| 638 | struct ov4689 *ov4689 = | 635 | struct ov4689 *ov4689 = |
| 639 | container_of(ctrl->handler, struct ov4689, ctrl_handler); | 636 | container_of(ctrl->handler, struct ov4689, ctrl_handler); |
| 640 | struct i2c_client *client = ov4689->client; | 637 | struct regmap *regmap = ov4689->regmap; |
| 641 | int sensor_gain; | 638 | struct device *dev = ov4689->dev; |
| 639 | int sensor_gain = 0; | ||
| 642 | s64 max_expo; | 640 | s64 max_expo; |
| 643 | int ret; | 641 | int ret = 0; |
| 644 | 642 | ||
| 645 | /* Propagate change of current control to all related controls */ | 643 | /* Propagate change of current control to all related controls */ |
| 646 | switch (ctrl->id) { | 644 | switch (ctrl->id) { |
| @@ -654,44 +652,58 @@ static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl) · 7 unmodified lines | |||
| 654 | break; | 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 | return 0; | 656 | return 0; |
| 659 | 657 | ||
| 660 | switch (ctrl->id) { | 658 | switch (ctrl->id) { |
| 661 | case V4L2_CID_EXPOSURE: | 659 | case V4L2_CID_EXPOSURE: |
| 662 | /* 4 least significant bits of expsoure are fractional part */ | 660 | /* 4 least significant bits of exposure are fractional part */ |
| 663 | ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE, | 661 | cci_write(regmap, OV4689_REG_EXPOSURE, ctrl->val << 4, &ret); |
| 664 | OV4689_REG_VALUE_24BIT, ctrl->val << 4); | ||
| 665 | break; | 662 | break; |
| 666 | case V4L2_CID_ANALOGUE_GAIN: | 663 | case V4L2_CID_ANALOGUE_GAIN: |
| 667 | ret = ov4689_map_gain(ov4689, ctrl->val, &sensor_gain); | 664 | ret = ov4689_map_gain(ov4689, ctrl->val, &sensor_gain); |
| 668 | 665 | cci_write(regmap, OV4689_REG_GAIN, sensor_gain, &ret); | |
| 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); | ||
| 678 | break; | 666 | break; |
| 679 | case V4L2_CID_VBLANK: | 667 | case V4L2_CID_VBLANK: |
| 680 | ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS, | 668 | cci_write(regmap, OV4689_REG_VTS, |
| 681 | OV4689_REG_VALUE_16BIT, | 669 | ctrl->val + ov4689->cur_mode->height, &ret); |
| 682 | ctrl->val + ov4689->cur_mode->height); | ||
| 683 | break; | 670 | break; |
| 684 | case V4L2_CID_TEST_PATTERN: | 671 | case V4L2_CID_TEST_PATTERN: |
| 685 | ret = ov4689_enable_test_pattern(ov4689, ctrl->val); | 672 | ret = ov4689_enable_test_pattern(ov4689, ctrl->val); |
| 686 | break; | 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 | default: | 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 | __func__, ctrl->id, ctrl->val); | 700 | __func__, ctrl->id, ctrl->val); |
| 690 | ret = -EINVAL; | 701 | ret = -EINVAL; |
| 691 | break; | 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 | return ret; | 708 | return ret; |
| 697 | } | 709 | } |
| @@ -707,16 +719,15 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689) · 9 unmodified lines | |||
| 707 | struct v4l2_ctrl_handler *handler; | 719 | struct v4l2_ctrl_handler *handler; |
| 708 | const struct ov4689_mode *mode; | 720 | const struct ov4689_mode *mode; |
| 709 | s64 exposure_max, vblank_def; | 721 | s64 exposure_max, vblank_def; |
| 722 | s64 hblank_def, hblank_min; | ||
| 710 | struct v4l2_ctrl *ctrl; | 723 | struct v4l2_ctrl *ctrl; |
| 711 | s64 h_blank_def; | ||
| 712 | int ret; | 724 | int ret; |
| 713 | 725 | ||
| 714 | handler = &ov4689->ctrl_handler; | 726 | handler = &ov4689->ctrl_handler; |
| 715 | mode = ov4689->cur_mode; | 727 | mode = ov4689->cur_mode; |
| 716 | ret = v4l2_ctrl_handler_init(handler, 10); | 728 | ret = v4l2_ctrl_handler_init(handler, 15); |
| 717 | if (ret) | 729 | if (ret) |
| 718 | return ret; | 730 | return ret; |
| 719 | handler->lock = &ov4689->mutex; | ||
| 720 | 731 | ||
| 721 | ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0, | 732 | ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0, |
| 722 | link_freq_menu_items); | 733 | link_freq_menu_items); |
| @@ -726,11 +737,11 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689) · 3 unmodified lines | |||
| 726 | v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, | 737 | v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, |
| 727 | mode->pixel_rate, 1, mode->pixel_rate); | 738 | mode->pixel_rate, 1, mode->pixel_rate); |
| 728 | 739 | ||
| 729 | h_blank_def = mode->hts_def - mode->width; | 740 | hblank_def = mode->hts_def - mode->width; |
| 730 | ctrl = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, h_blank_def, | 741 | hblank_min = mode->hts_min - mode->width; |
| 731 | h_blank_def, 1, h_blank_def); | 742 | v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_HBLANK, |
| 732 | if (ctrl) | 743 | hblank_min, OV4689_HTS_MAX - mode->width, |
| 733 | ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; | 744 | OV4689_HTS_DIVIDER, hblank_def); |
| 734 | 745 | ||
| 735 | vblank_def = mode->vts_def - mode->height; | 746 | vblank_def = mode->vts_def - mode->height; |
| 736 | v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK, | 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 | ARRAY_SIZE(ov4689_test_pattern_menu) - 1, | 765 | ARRAY_SIZE(ov4689_test_pattern_menu) - 1, |
| 755 | 0, 0, ov4689_test_pattern_menu); | 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 | if (handler->error) { | 783 | if (handler->error) { |
| 758 | ret = handler->error; | 784 | ret = handler->error; |
| 759 | dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n", | 785 | dev_err(ov4689->dev, "Failed to init controls(%d)\n", ret); |
| 760 | ret); | ||
| 761 | goto err_free_handler; | 786 | goto err_free_handler; |
| 762 | } | 787 | } |
| 763 | 788 | ||
| @@ -783,19 +808,18 @@ err_free_handler: · 19 unmodified lines | |||
| 783 | static int ov4689_check_sensor_id(struct ov4689 *ov4689, | 808 | static int ov4689_check_sensor_id(struct ov4689 *ov4689, |
| 784 | struct i2c_client *client) | 809 | struct i2c_client *client) |
| 785 | { | 810 | { |
| 786 | struct device *dev = &ov4689->client->dev; | 811 | struct device *dev = ov4689->dev; |
| 787 | u32 id = 0; | 812 | u64 id = 0; |
| 788 | int ret; | 813 | int ret; |
| 789 | 814 | ||
| 790 | ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID, | 815 | ret = cci_read(ov4689->regmap, OV4689_REG_CHIP_ID, &id, NULL); |
| 791 | OV4689_REG_VALUE_16BIT, &id); | ||
| 792 | if (ret) { | 816 | if (ret) { |
| 793 | dev_err(dev, "Cannot read sensor ID\n"); | 817 | dev_err(dev, "Cannot read sensor ID\n"); |
| 794 | return ret; | 818 | return ret; |
| 795 | } | 819 | } |
| 796 | 820 | ||
| 797 | if (id != CHIP_ID) { | 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 | id, CHIP_ID); | 823 | id, CHIP_ID); |
| 800 | return -ENODEV; | 824 | return -ENODEV; |
| 801 | } | 825 | } |
| @@ -812,7 +836,7 @@ static int ov4689_configure_regulators(struct ov4689 *ov4689) · 10 unmodified lines | |||
| 812 | for (i = 0; i < ARRAY_SIZE(ov4689_supply_names); i++) | 836 | for (i = 0; i < ARRAY_SIZE(ov4689_supply_names); i++) |
| 813 | ov4689->supplies[i].supply = ov4689_supply_names[i]; | 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 | ARRAY_SIZE(ov4689_supply_names), | 840 | ARRAY_SIZE(ov4689_supply_names), |
| 817 | ov4689->supplies); | 841 | ov4689->supplies); |
| 818 | } | 842 | } |
| @@ -881,7 +905,8 @@ static int ov4689_probe(struct i2c_client *client) · 62 unmodified lines | |||
| 881 | if (!ov4689) | 905 | if (!ov4689) |
| 882 | return -ENOMEM; | 906 | return -ENOMEM; |
| 883 | 907 | ||
| 884 | ov4689->client = client; | 908 | ov4689->dev = dev; |
| 909 | |||
| 885 | ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520]; | 910 | ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520]; |
| 886 | 911 | ||
| 887 | ov4689->xvclk = devm_clk_get_optional(dev, NULL); | 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 | return -EINVAL; | 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 | ov4689->reset_gpio = devm_gpiod_get_optional(dev, "reset", | 940 | ov4689->reset_gpio = devm_gpiod_get_optional(dev, "reset", |
| 909 | GPIOD_OUT_LOW); | 941 | GPIOD_OUT_LOW); |
| 910 | if (IS_ERR(ov4689->reset_gpio)) { | 942 | if (IS_ERR(ov4689->reset_gpio)) { |
| @@ -923,13 +955,15 @@ static int ov4689_probe(struct i2c_client *client) · 12 unmodified lines | |||
| 923 | return dev_err_probe(dev, ret, | 955 | return dev_err_probe(dev, ret, |
| 924 | "Failed to get power regulators\n"); | 956 | "Failed to get power regulators\n"); |
| 925 | 957 | ||
| 926 | mutex_init(&ov4689->mutex); | ||
| 927 | |||
| 928 | sd = &ov4689->subdev; | 958 | sd = &ov4689->subdev; |
| 929 | v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops); | 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 | ret = ov4689_initialize_controls(ov4689); | 962 | ret = ov4689_initialize_controls(ov4689); |
| 931 | if (ret) | 963 | if (ret) { |
| 932 | goto err_destroy_mutex; | 964 | dev_err(dev, "Failed to initialize controls\n"); |
| 965 | return ret; | ||
| 966 | } | ||
| 933 | 967 | ||
| 934 | ret = ov4689_power_on(dev); | 968 | ret = ov4689_power_on(dev); |
| 935 | if (ret) | 969 | if (ret) |
| @@ -939,35 +973,47 @@ static int ov4689_probe(struct i2c_client *client) · 3 unmodified lines | |||
| 939 | if (ret) | 973 | if (ret) |
| 940 | goto err_power_off; | 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 | sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; | 977 | sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; |
| 978 | ov4689->pad.flags = MEDIA_PAD_FL_SOURCE; | ||
| 947 | ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad); | 979 | ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad); |
| 948 | if (ret < 0) | 980 | if (ret < 0) |
| 949 | goto err_power_off; | 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 | if (ret) { | 985 | if (ret) { |
| 953 | dev_err(dev, "v4l2 async register subdev failed\n"); | 986 | dev_err(dev, "Could not register v4l2 device\n"); |
| 954 | goto err_clean_entity; | 987 | goto err_clean_entity; |
| 955 | } | 988 | } |
| 956 | 989 | ||
| 957 | pm_runtime_set_active(dev); | 990 | pm_runtime_set_active(dev); |
| 991 | pm_runtime_get_noresume(dev); | ||
| 958 | pm_runtime_enable(dev); | 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 | return 0; | 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 | err_clean_entity: | 1011 | err_clean_entity: |
| 964 | media_entity_cleanup(&sd->entity); | 1012 | media_entity_cleanup(&sd->entity); |
| 965 | err_power_off: | 1013 | err_power_off: |
| 966 | ov4689_power_off(dev); | 1014 | ov4689_power_off(dev); |
| 967 | err_free_handler: | 1015 | err_free_handler: |
| 968 | v4l2_ctrl_handler_free(&ov4689->ctrl_handler); | 1016 | v4l2_ctrl_handler_free(&ov4689->ctrl_handler); |
| 969 | err_destroy_mutex: | ||
| 970 | mutex_destroy(&ov4689->mutex); | ||
| 971 | 1017 | ||
| 972 | return ret; | 1018 | return ret; |
| 973 | } | 1019 | } |
| @@ -979,9 +1025,8 @@ static void ov4689_remove(struct i2c_client *client) · 5 unmodified lines | |||
| 979 | 1025 | ||
| 980 | v4l2_async_unregister_subdev(sd); | 1026 | v4l2_async_unregister_subdev(sd); |
| 981 | media_entity_cleanup(&sd->entity); | 1027 | media_entity_cleanup(&sd->entity); |
| 982 | 1028 | v4l2_subdev_cleanup(sd); | |
| 983 | v4l2_ctrl_handler_free(&ov4689->ctrl_handler); | 1029 | v4l2_ctrl_handler_free(&ov4689->ctrl_handler); |
| 984 | mutex_destroy(&ov4689->mutex); | ||
| 985 | 1030 | ||
| 986 | pm_runtime_disable(&client->dev); | 1031 | pm_runtime_disable(&client->dev); |
| 987 | if (!pm_runtime_status_suspended(&client->dev)) | 1032 | if (!pm_runtime_status_suspended(&client->dev)) |
| @@ -463,8 +463,8 @@ static int rdacm20_initialize(struct rdacm20_device *dev) · 462 unmodified lines | |||
| 463 | return ret; | 463 | return ret; |
| 464 | 464 | ||
| 465 | /* | 465 | /* |
| 466 | * Ensure that we have a good link configuration before attempting to | 466 | * Ensure that we have a good link configuration before attempting to |
| 467 | * identify the device. | 467 | * identify the device. |
| 468 | */ | 468 | */ |
| 469 | ret = max9271_configure_i2c(&dev->serializer, | 469 | ret = max9271_configure_i2c(&dev->serializer, |
| 470 | MAX9271_I2CSLVSH_469NS_234NS | | 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 | dev_dbg(&client->dev, "detect link_freq = %lld Hz", link_freq); | 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 | bridge->r.clk_lane_reg1 |= ui_4 << 2; | 330 | bridge->r.clk_lane_reg1 |= ui_4 << 2; |
| 331 | 331 | ||
| 332 | return 0; | 332 | return 0; |
| @@ -1521,11 +1521,14 @@ static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) · 1520 unmodified lines | |||
| 1521 | return 0; | 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 | struct v4l2_dv_timings *timings) | 1525 | struct v4l2_dv_timings *timings) |
| 1526 | { | 1526 | { |
| 1527 | struct tc358743_state *state = to_state(sd); | 1527 | struct tc358743_state *state = to_state(sd); |
| 1528 | 1528 | ||
| 1529 | if (pad != 0) | ||
| 1530 | return -EINVAL; | ||
| 1531 | |||
| 1529 | if (!timings) | 1532 | if (!timings) |
| 1530 | return -EINVAL; | 1533 | return -EINVAL; |
| 1531 | 1534 | ||
| @@ -1553,11 +1556,14 @@ static int tc358743_s_dv_timings(struct v4l2_subdev *sd, · 21 unmodified lines | |||
| 1553 | return 0; | 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 | struct v4l2_dv_timings *timings) | 1560 | struct v4l2_dv_timings *timings) |
| 1558 | { | 1561 | { |
| 1559 | struct tc358743_state *state = to_state(sd); | 1562 | struct tc358743_state *state = to_state(sd); |
| 1560 | 1563 | ||
| 1564 | if (pad != 0) | ||
| 1565 | return -EINVAL; | ||
| 1566 | |||
| 1561 | *timings = state->timings; | 1567 | *timings = state->timings; |
| 1562 | 1568 | ||
| 1563 | return 0; | 1569 | return 0; |
| @@ -1573,11 +1579,14 @@ static int tc358743_enum_dv_timings(struct v4l2_subdev *sd, · 9 unmodified lines | |||
| 1573 | &tc358743_timings_cap, NULL, NULL); | 1579 | &tc358743_timings_cap, NULL, NULL); |
| 1574 | } | 1580 | } |
| 1575 | 1581 | ||
| 1576 | static int tc358743_query_dv_timings(struct v4l2_subdev *sd, | 1582 | static int tc358743_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad, |
| 1577 | struct v4l2_dv_timings *timings) | 1583 | struct v4l2_dv_timings *timings) |
| 1578 | { | 1584 | { |
| 1579 | int ret; | 1585 | int ret; |
| 1580 | 1586 | ||
| 1587 | if (pad != 0) | ||
| 1588 | return -EINVAL; | ||
| 1589 | |||
| 1581 | ret = tc358743_get_detected_timings(sd, timings); | 1590 | ret = tc358743_get_detected_timings(sd, timings); |
| 1582 | if (ret) | 1591 | if (ret) |
| 1583 | return ret; | 1592 | return ret; |
| @@ -1822,9 +1831,6 @@ static const struct v4l2_subdev_core_ops tc358743_core_ops = { · 238 unmodified lines | |||
| 1822 | 1831 | ||
| 1823 | static const struct v4l2_subdev_video_ops tc358743_video_ops = { | 1832 | static const struct v4l2_subdev_video_ops tc358743_video_ops = { |
| 1824 | .g_input_status = tc358743_g_input_status, | 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 | .s_stream = tc358743_s_stream, | 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 | .get_fmt = tc358743_get_fmt, | 1840 | .get_fmt = tc358743_get_fmt, |
| 1835 | .get_edid = tc358743_g_edid, | 1841 | .get_edid = tc358743_g_edid, |
| 1836 | .set_edid = tc358743_s_edid, | 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 | .enum_dv_timings = tc358743_enum_dv_timings, | 1846 | .enum_dv_timings = tc358743_enum_dv_timings, |
| 1838 | .dv_timings_cap = tc358743_dv_timings_cap, | 1847 | .dv_timings_cap = tc358743_dv_timings_cap, |
| 1839 | .get_mbus_config = tc358743_get_mbus_config, | 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 | tc358743_initial_setup(sd); | 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 | tc358743_set_csi_color_space(sd); | 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 | continue; | 844 | continue; |
| 845 | 845 | ||
| 846 | tmp = fout * postdiv; | 846 | tmp = fout * postdiv; |
| 847 | do_div(tmp, fin); | 847 | mul = div64_ul(tmp, fin); |
| 848 | mul = tmp; | ||
| 849 | if (mul > 511) | 848 | if (mul > 511) |
| 850 | continue; | 849 | continue; |
| 851 | 850 | ||
| @@ -1669,8 +1669,8 @@ tda1997x_g_input_status(struct v4l2_subdev *sd, u32 *status) · 1668 unmodified lines | |||
| 1669 | return 0; | 1669 | return 0; |
| 1670 | }; | 1670 | }; |
| 1671 | 1671 | ||
| 1672 | static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, | 1672 | static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, |
| 1673 | struct v4l2_dv_timings *timings) | 1673 | struct v4l2_dv_timings *timings) |
| 1674 | { | 1674 | { |
| 1675 | struct tda1997x_state *state = to_state(sd); | 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 | return 0; | 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 | struct v4l2_dv_timings *timings) | 1698 | struct v4l2_dv_timings *timings) |
| 1699 | { | 1699 | { |
| 1700 | struct tda1997x_state *state = to_state(sd); | 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 | return 0; | 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 | struct v4l2_dv_timings *timings) | 1711 | struct v4l2_dv_timings *timings) |
| 1712 | { | 1712 | { |
| 1713 | struct tda1997x_state *state = to_state(sd); | 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 | static const struct v4l2_subdev_video_ops tda1997x_video_ops = { | 1725 | static const struct v4l2_subdev_video_ops tda1997x_video_ops = { |
| 1726 | .g_input_status = tda1997x_g_input_status, | 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 | .set_fmt = tda1997x_set_format, | 1927 | .set_fmt = tda1997x_set_format, |
| 1931 | .get_edid = tda1997x_get_edid, | 1928 | .get_edid = tda1997x_get_edid, |
| 1932 | .set_edid = tda1997x_set_edid, | 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 | .dv_timings_cap = tda1997x_get_dv_timings_cap, | 1933 | .dv_timings_cap = tda1997x_get_dv_timings_cap, |
| 1934 | .enum_dv_timings = tda1997x_enum_dv_timings, | 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 | /* for setting filter for HD output */ | 195 | /* for setting filter for HD output */ |
| 196 | static int ths7303_s_dv_timings(struct v4l2_subdev *sd, | 196 | static int ths7303_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, |
| 197 | struct v4l2_dv_timings *dv_timings) | 197 | struct v4l2_dv_timings *dv_timings) |
| 198 | { | 198 | { |
| 199 | struct ths7303_state *state = to_state(sd); | 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 | static const struct v4l2_subdev_video_ops ths7303_video_ops = { | 210 | static const struct v4l2_subdev_video_ops ths7303_video_ops = { |
| 211 | .s_stream = ths7303_s_stream, | 211 | .s_stream = ths7303_s_stream, |
| 212 | .s_std_output = ths7303_s_std_output, | 212 | .s_std_output = ths7303_s_std_output, |
| 213 | .s_dv_timings = ths7303_s_dv_timings, | ||
| 214 | }; | 213 | }; |
| 215 | 214 | ||
| 216 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 215 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
| @@ -317,9 +316,14 @@ static const struct v4l2_subdev_core_ops ths7303_core_ops = { · 100 unmodified lines | |||
| 317 | #endif | 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 | static const struct v4l2_subdev_ops ths7303_ops = { | 323 | static const struct v4l2_subdev_ops ths7303_ops = { |
| 321 | .core = &ths7303_core_ops, | 324 | .core = &ths7303_core_ops, |
| 322 | .video = &ths7303_video_ops, | 325 | .video = &ths7303_video_ops, |
| 326 | .pad = &ths7303_pad_ops, | ||
| 323 | }; | 327 | }; |
| 324 | 328 | ||
| 325 | static int ths7303_probe(struct i2c_client *client) | 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 | bt->hsync, bt->vsync); | 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 | struct v4l2_dv_timings *timings) | 362 | struct v4l2_dv_timings *timings) |
| 363 | { | 363 | { |
| 364 | struct ths8200_state *state = to_state(sd); | 364 | struct ths8200_state *state = to_state(sd); |
| 365 | 365 | ||
| 366 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); | 366 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); |
| 367 | 367 | ||
| 368 | if (pad != 0) | ||
| 369 | return -EINVAL; | ||
| 370 | |||
| 368 | if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap, | 371 | if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap, |
| 369 | NULL, NULL)) | 372 | NULL, NULL)) |
| 370 | return -EINVAL; | 373 | return -EINVAL; |
| @@ -385,13 +388,16 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd, · 14 unmodified lines | |||
| 385 | return 0; | 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 | struct v4l2_dv_timings *timings) | 392 | struct v4l2_dv_timings *timings) |
| 390 | { | 393 | { |
| 391 | struct ths8200_state *state = to_state(sd); | 394 | struct ths8200_state *state = to_state(sd); |
| 392 | 395 | ||
| 393 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); | 396 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); |
| 394 | 397 | ||
| 398 | if (pad != 0) | ||
| 399 | return -EINVAL; | ||
| 400 | |||
| 395 | *timings = state->dv_timings; | 401 | *timings = state->dv_timings; |
| 396 | 402 | ||
| 397 | return 0; | 403 | return 0; |
| @@ -420,11 +426,11 @@ static int ths8200_dv_timings_cap(struct v4l2_subdev *sd, · 22 unmodified lines | |||
| 420 | /* Specific video subsystem operation handlers */ | 426 | /* Specific video subsystem operation handlers */ |
| 421 | static const struct v4l2_subdev_video_ops ths8200_video_ops = { | 427 | static const struct v4l2_subdev_video_ops ths8200_video_ops = { |
| 422 | .s_stream = ths8200_s_stream, | 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 | static const struct v4l2_subdev_pad_ops ths8200_pad_ops = { | 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 | .enum_dv_timings = ths8200_enum_dv_timings, | 434 | .enum_dv_timings = ths8200_enum_dv_timings, |
| 429 | .dv_timings_cap = ths8200_dv_timings_cap, | 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 | return error; | 546 | return error; |
| 547 | } | 547 | } |
| 548 | 548 | ||
| 549 | static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, | 549 | static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, |
| 550 | struct v4l2_dv_timings *dv_timings) | 550 | struct v4l2_dv_timings *dv_timings) |
| 551 | { | 551 | { |
| 552 | struct tvp7002 *device = to_tvp7002(sd); | 552 | struct tvp7002 *device = to_tvp7002(sd); |
| 553 | const struct v4l2_bt_timings *bt = &dv_timings->bt; | 553 | const struct v4l2_bt_timings *bt = &dv_timings->bt; |
| 554 | int i; | 554 | int i; |
| 555 | 555 | ||
| 556 | if (pad != 0) | ||
| 557 | return -EINVAL; | ||
| 558 | |||
| 556 | if (dv_timings->type != V4L2_DV_BT_656_1120) | 559 | if (dv_timings->type != V4L2_DV_BT_656_1120) |
| 557 | return -EINVAL; | 560 | return -EINVAL; |
| 558 | for (i = 0; i < NUM_TIMINGS; i++) { | 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 | return -EINVAL; | 569 | return -EINVAL; |
| 567 | } | 570 | } |
| 568 | 571 | ||
| 569 | static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, | 572 | static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, |
| 570 | struct v4l2_dv_timings *dv_timings) | 573 | struct v4l2_dv_timings *dv_timings) |
| 571 | { | 574 | { |
| 572 | struct tvp7002 *device = to_tvp7002(sd); | 575 | struct tvp7002 *device = to_tvp7002(sd); |
| 573 | 576 | ||
| 577 | if (pad != 0) | ||
| 578 | return -EINVAL; | ||
| 579 | |||
| 574 | *dv_timings = device->current_timings->timings; | 580 | *dv_timings = device->current_timings->timings; |
| 575 | return 0; | 581 | return 0; |
| 576 | } | 582 | } |
| @@ -659,12 +665,16 @@ static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) · 82 unmodified lines | |||
| 659 | return 0; | 665 | return 0; |
| 660 | } | 666 | } |
| 661 | 667 | ||
| 662 | static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, | 668 | static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad, |
| 663 | struct v4l2_dv_timings *timings) | 669 | struct v4l2_dv_timings *timings) |
| 664 | { | 670 | { |
| 665 | int index; | 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 | if (err) | 678 | if (err) |
| 669 | return err; | 679 | return err; |
| 670 | *timings = tvp7002_timings[index].timings; | 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 | /* Specific video subsystem operation handlers */ | 872 | /* Specific video subsystem operation handlers */ |
| 863 | static const struct v4l2_subdev_video_ops tvp7002_video_ops = { | 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 | .s_stream = tvp7002_s_stream, | 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 | .enum_mbus_code = tvp7002_enum_mbus_code, | 879 | .enum_mbus_code = tvp7002_enum_mbus_code, |
| 873 | .get_fmt = tvp7002_get_pad_format, | 880 | .get_fmt = tvp7002_get_pad_format, |
| 874 | .set_fmt = tvp7002_set_pad_format, | 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 | .enum_dv_timings = tvp7002_enum_dv_timings, | 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 | /* Set registers according to default video mode */ | 1012 | /* Set registers according to default video mode */ |
| 1003 | timings = device->current_timings->timings; | 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 | #if defined(CONFIG_MEDIA_CONTROLLER) | 1016 | #if defined(CONFIG_MEDIA_CONTROLLER) |
| 1007 | device->pad.flags = MEDIA_PAD_FL_SOURCE; | 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 | kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor); | 245 | kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor); |
| 246 | 246 | ||
| 247 | /* Part 3: Add the media and char device */ | 247 | /* Part 3: Add the media and char device */ |
| 248 | set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); | ||
| 248 | ret = cdev_device_add(&devnode->cdev, &devnode->dev); | 249 | ret = cdev_device_add(&devnode->cdev, &devnode->dev); |
| 249 | if (ret < 0) { | 250 | if (ret < 0) { |
| 251 | clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); | ||
| 250 | pr_err("%s: cdev_device_add failed\n", __func__); | 252 | pr_err("%s: cdev_device_add failed\n", __func__); |
| 251 | goto cdev_add_error; | 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 | return 0; | 256 | return 0; |
| 258 | 257 | ||
| 259 | cdev_add_error: | 258 | cdev_add_error: |
| @@ -619,6 +619,12 @@ static int media_pipeline_explore_next_link(struct media_pipeline *pipe, · 618 unmodified lines | |||
| 619 | link = list_entry(entry->links, typeof(*link), list); | 619 | link = list_entry(entry->links, typeof(*link), list); |
| 620 | last_link = media_pipeline_walk_pop(walk); | 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 | dev_dbg(walk->mdev->dev, | 628 | dev_dbg(walk->mdev->dev, |
| 623 | "media pipeline: exploring link '%s':%u -> '%s':%u\n", | 629 | "media pipeline: exploring link '%s':%u -> '%s':%u\n", |
| 624 | link->source->entity->name, link->source->index, | 630 | link->source->entity->name, link->source->index, |
| @@ -344,30 +344,7 @@ static struct sdio_driver smssdio_driver = { · 343 unmodified lines | |||
| 344 | .probe = smssdio_probe, | 344 | .probe = smssdio_probe, |
| 345 | .remove = smssdio_remove, | 345 | .remove = smssdio_remove, |
| 346 | }; | 346 | }; |
| 347 | 347 | module_sdio_driver(smssdio_driver); | |
| 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); | ||
| 371 | 348 | ||
| 372 | MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver"); | 349 | MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver"); |
| 373 | MODULE_AUTHOR("Pierre Ossman"); | 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 | return -EBUSY; | 633 | return -EBUSY; |
| 634 | 634 | ||
| 635 | err = v4l2_subdev_call(s->sd, | 635 | err = v4l2_subdev_call(s->sd, |
| 636 | video, s_dv_timings, timings); | 636 | pad, s_dv_timings, 0, timings); |
| 637 | if (!err) { | 637 | if (!err) { |
| 638 | s->timings = *timings; | 638 | s->timings = *timings; |
| 639 | s->width = timings->bt.width; | 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 | return 0; | 653 | return 0; |
| 654 | } | 654 | } |
| 655 | return v4l2_subdev_call(s->sd, | 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 | static int cobalt_query_dv_timings(struct file *file, void *priv_fh, | 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 | return 0; | 666 | return 0; |
| 667 | } | 667 | } |
| 668 | return v4l2_subdev_call(s->sd, | 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 | static int cobalt_dv_timings_cap(struct file *file, void *priv_fh, | 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 | if (s->input == 1) | 1080 | if (s->input == 1) |
| 1081 | timings = cea1080p60; | 1081 | timings = cea1080p60; |
| 1082 | else | 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 | if (!err) | 1084 | if (!err) |
| 1085 | *f = v4l2_dv_timings_aspect_ratio(&timings); | 1085 | *f = v4l2_dv_timings_aspect_ratio(&timings); |
| 1086 | return err; | 1086 | return err; |
| @@ -1099,7 +1099,7 @@ static int cobalt_g_selection(struct file *file, void *fh, · 12 unmodified lines | |||
| 1099 | if (s->input == 1) | 1099 | if (s->input == 1) |
| 1100 | timings = cea1080p60; | 1100 | timings = cea1080p60; |
| 1101 | else | 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 | if (err) | 1104 | if (err) |
| 1105 | return err; | 1105 | return err; |
| @@ -1243,7 +1243,7 @@ static int cobalt_node_register(struct cobalt *cobalt, int node) · 137 unmodified lines | |||
| 1243 | if (s->sd) | 1243 | if (s->sd) |
| 1244 | vdev->ctrl_handler = s->sd->ctrl_handler; | 1244 | vdev->ctrl_handler = s->sd->ctrl_handler; |
| 1245 | s->timings = dv1080p60; | 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 | if (!s->is_output && s->sd) | 1247 | if (!s->is_output && s->sd) |
| 1248 | cobalt_enable_input(s); | 1248 | cobalt_enable_input(s); |
| 1249 | vdev->ioctl_ops = s->is_dummy ? &cobalt_ioctl_empty_ops : | 1249 | vdev->ioctl_ops = s->is_dummy ? &cobalt_ioctl_empty_ops : |
| @@ -1,11 +1,13 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | 1 | # SPDX-License-Identifier: GPL-2.0-only |
| 2 | 2 | ||
| 3 | source "drivers/media/pci/intel/ipu3/Kconfig" | 3 | source "drivers/media/pci/intel/ipu3/Kconfig" |
| 4 | source "drivers/media/pci/intel/ipu6/Kconfig" | ||
| 4 | source "drivers/media/pci/intel/ivsc/Kconfig" | 5 | source "drivers/media/pci/intel/ivsc/Kconfig" |
| 5 | 6 | ||
| 6 | config IPU_BRIDGE | 7 | config IPU_BRIDGE |
| 7 | tristate "Intel IPU Bridge" | 8 | tristate "Intel IPU Bridge" |
| 8 | depends on I2C && ACPI | 9 | depends on ACPI || COMPILE_TEST |
| 10 | depends on I2C | ||
| 9 | help | 11 | help |
| 10 | The IPU bridge is a helper library for Intel IPU drivers to | 12 | The IPU bridge is a helper library for Intel IPU drivers to |
| 11 | function on systems shipped with Windows. | 13 | function on systems shipped with Windows. |
| @@ -5,3 +5,4 @@ · 4 unmodified lines | |||
| 5 | obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o | 5 | obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o |
| 6 | obj-y += ipu3/ | 6 | obj-y += ipu3/ |
| 7 | obj-y += ivsc/ | 7 | obj-y += ivsc/ |
| 8 | obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ | ||
| @@ -15,6 +15,8 @@ · 14 unmodified lines | |||
| 15 | #include <media/ipu-bridge.h> | 15 | #include <media/ipu-bridge.h> |
| 16 | #include <media/v4l2-fwnode.h> | 16 | #include <media/v4l2-fwnode.h> |
| 17 | 17 | ||
| 18 | #define ADEV_DEV(adev) ACPI_PTR(&((adev)->dev)) | ||
| 19 | |||
| 18 | /* | 20 | /* |
| 19 | * 92335fcf-3203-4472-af93-7b4453ac29da | 21 | * 92335fcf-3203-4472-af93-7b4453ac29da |
| 20 | * | 22 | * |
| @@ -87,6 +89,7 @@ static const char * const ipu_vcm_types[] = { · 66 unmodified lines | |||
| 87 | "lc898212axb", | 89 | "lc898212axb", |
| 88 | }; | 90 | }; |
| 89 | 91 | ||
| 92 | #if IS_ENABLED(CONFIG_ACPI) | ||
| 90 | /* | 93 | /* |
| 91 | * Used to figure out IVSC acpi device by ipu_bridge_get_ivsc_acpi_dev() | 94 | * Used to figure out IVSC acpi device by ipu_bridge_get_ivsc_acpi_dev() |
| 92 | * instead of device and driver match to probe IVSC device. | 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 | static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) | 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 | unsigned int i; | 106 | unsigned int i; |
| 106 | 107 | ||
| 107 | for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) { | 108 | for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) { |
| 108 | const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i]; | 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 | for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1) | 113 | for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1) |
| 111 | /* camera sensor depends on IVSC in DSDT if exist */ | 114 | /* camera sensor depends on IVSC in DSDT if exist */ |
| 112 | for_each_acpi_consumer_dev(ivsc_adev, consumer) | 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 | return NULL; | 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 | static int ipu_bridge_match_ivsc_dev(struct device *dev, const void *adev) | 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 | csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); | 172 | csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); |
| 164 | if (!csi_dev) { | 173 | if (!csi_dev) { |
| 165 | acpi_dev_put(adev); | 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 | return -ENODEV; | 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 | acpi_status status; | 191 | acpi_status status; |
| 183 | int ret = 0; | 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 | if (ACPI_FAILURE(status)) | 196 | if (ACPI_FAILURE(status)) |
| 187 | return -ENODEV; | 197 | return -ENODEV; |
| 188 | 198 | ||
| 189 | obj = buffer.pointer; | 199 | obj = buffer.pointer; |
| 190 | if (!obj) { | 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 | return -ENODEV; | 202 | return -ENODEV; |
| 193 | } | 203 | } |
| 194 | 204 | ||
| 195 | if (obj->type != ACPI_TYPE_BUFFER) { | 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 | ret = -ENODEV; | 207 | ret = -ENODEV; |
| 198 | goto out_free_buff; | 208 | goto out_free_buff; |
| 199 | } | 209 | } |
| 200 | 210 | ||
| 201 | if (obj->buffer.length > size) { | 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 | ret = -EINVAL; | 213 | ret = -EINVAL; |
| 204 | goto out_free_buff; | 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 | case IPU_SENSOR_ROTATION_INVERTED: | 230 | case IPU_SENSOR_ROTATION_INVERTED: |
| 221 | return 180; | 231 | return 180; |
| 222 | default: | 232 | default: |
| 223 | dev_warn(&adev->dev, | 233 | dev_warn(ADEV_DEV(adev), |
| 224 | "Unknown rotation %d. Assume 0 degree rotation\n", | 234 | "Unknown rotation %d. Assume 0 degree rotation\n", |
| 225 | ssdb->degree); | 235 | ssdb->degree); |
| 226 | return 0; | 236 | return 0; |
| @@ -230,12 +240,14 @@ static u32 ipu_bridge_parse_rotation(struct acpi_device *adev, · 3 unmodified lines | |||
| 230 | static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_device *adev) | 240 | static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_device *adev) |
| 231 | { | 241 | { |
| 232 | enum v4l2_fwnode_orientation orientation; | 242 | enum v4l2_fwnode_orientation orientation; |
| 233 | struct acpi_pld_info *pld; | 243 | struct acpi_pld_info *pld = NULL; |
| 234 | acpi_status status; | 244 | acpi_status status = AE_ERROR; |
| 235 | 245 | ||
| 246 | #if IS_ENABLED(CONFIG_ACPI) | ||
| 236 | status = acpi_get_physical_device_location(adev->handle, &pld); | 247 | status = acpi_get_physical_device_location(adev->handle, &pld); |
| 248 | #endif | ||
| 237 | if (ACPI_FAILURE(status)) { | 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 | return V4L2_FWNODE_ORIENTATION_EXTERNAL; | 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 | orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL; | 265 | orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL; |
| 254 | break; | 266 | break; |
| 255 | default: | 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 | orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL; | 270 | orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL; |
| 258 | break; | 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 | return ret; | 285 | return ret; |
| 273 | 286 | ||
| 274 | if (ssdb.vcmtype > ARRAY_SIZE(ipu_vcm_types)) { | 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 | ssdb.vcmtype = 0; | 289 | ssdb.vcmtype = 0; |
| 277 | } | 290 | } |
| 278 | 291 | ||
| 279 | if (ssdb.lanes > IPU_MAX_LANES) { | 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 | return -EINVAL; | 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 | sensor->ipu_properties); | 478 | sensor->ipu_properties); |
| 466 | 479 | ||
| 467 | if (sensor->csi_dev) { | 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 | snprintf(sensor->ivsc_name, sizeof(sensor->ivsc_name), "%s-%u", | 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 | nodes[SWNODE_IVSC_HID] = NODE_SENSOR(sensor->ivsc_name, | 490 | nodes[SWNODE_IVSC_HID] = NODE_SENSOR(sensor->ivsc_name, |
| 472 | sensor->ivsc_properties); | 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 | struct fwnode_handle *fwnode, *primary; | 651 | struct fwnode_handle *fwnode, *primary; |
| 633 | struct ipu_sensor *sensor; | 652 | struct ipu_sensor *sensor; |
| 634 | struct acpi_device *adev; | 653 | struct acpi_device *adev = NULL; |
| 635 | int ret; | 654 | int ret; |
| 636 | 655 | ||
| 656 | #if IS_ENABLED(CONFIG_ACPI) | ||
| 637 | for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) { | 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 | continue; | 662 | continue; |
| 640 | 663 | ||
| 641 | if (bridge->n_sensors >= IPU_MAX_PORTS) { | 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 | goto err_free_swnodes; | 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 | primary = acpi_fwnode_handle(adev); | 699 | primary = acpi_fwnode_handle(adev); |
| 677 | primary->secondary = fwnode; | 700 | primary->secondary = fwnode; |
| @@ -727,11 +750,16 @@ static int ipu_bridge_ivsc_is_ready(void) · 49 unmodified lines | |||
| 727 | unsigned int i; | 750 | unsigned int i; |
| 728 | 751 | ||
| 729 | for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) { | 752 | for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) { |
| 753 | #if IS_ENABLED(CONFIG_ACPI) | ||
| 730 | const struct ipu_sensor_config *cfg = | 754 | const struct ipu_sensor_config *cfg = |
| 731 | &ipu_supported_sensors[i]; | 755 | &ipu_supported_sensors[i]; |
| 732 | 756 | ||
| 733 | for_each_acpi_dev_match(sensor_adev, cfg->hid, NULL, -1) { | 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 | continue; | 763 | continue; |
| 736 | 764 | ||
| 737 | adev = ipu_bridge_get_ivsc_acpi_dev(sensor_adev); | 765 | adev = ipu_bridge_get_ivsc_acpi_dev(sensor_adev); |
| @@ -4,9 +4,9 @@ · 3 unmodified lines | |||
| 4 | * | 4 | * |
| 5 | * Based partially on Intel IPU4 driver written by | 5 | * Based partially on Intel IPU4 driver written by |
| 6 | * Sakari Ailus <sakari.ailus@linux.intel.com> | 6 | * Sakari Ailus <sakari.ailus@linux.intel.com> |
| 7 | * Samu Onkalo <samu.onkalo@intel.com> | 7 | * Samu Onkalo |
| 8 | * Jouni Högander <jouni.hogander@intel.com> | 8 | * Jouni Högander <jouni.hogander@intel.com> |
| 9 | * Jouni Ukkonen <jouni.ukkonen@intel.com> | 9 | * Jouni Ukkonen |
| 10 | * Antti Laakso <antti.laakso@intel.com> | 10 | * Antti Laakso <antti.laakso@intel.com> |
| 11 | * et al. | 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 | v4l2_async_nf_init(&cio2->notifier, &cio2->v4l2_dev); | 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 | r = devm_request_irq(dev, pci_dev->irq, cio2_irq, IRQF_SHARED, | 1755 | r = devm_request_irq(dev, pci_dev->irq, cio2_irq, IRQF_SHARED, |
| 1761 | CIO2_NAME, cio2); | 1756 | CIO2_NAME, cio2); |
| 1762 | if (r) { | 1757 | if (r) { |
| @@ -1764,6 +1759,11 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, · 1 unmodified line | |||
| 1764 | goto fail_clean_notifier; | 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 | pm_runtime_put_noidle(dev); | 1767 | pm_runtime_put_noidle(dev); |
| 1768 | pm_runtime_allow(dev); | 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 | struct pci_dev *pci_dev = to_pci_dev(dev); | 1807 | struct pci_dev *pci_dev = to_pci_dev(dev); |
| 1808 | struct cio2_device *cio2 = pci_get_drvdata(pci_dev); | 1808 | struct cio2_device *cio2 = pci_get_drvdata(pci_dev); |
| 1809 | void __iomem *const base = cio2->base; | 1809 | void __iomem *const base = cio2->base; |
| 1810 | u16 pm; | ||
| 1811 | 1810 | ||
| 1812 | writel(CIO2_D0I3C_I3, base + CIO2_REG_D0I3C); | 1811 | writel(CIO2_D0I3C_I3, base + CIO2_REG_D0I3C); |
| 1813 | dev_dbg(dev, "cio2 runtime suspend.\n"); | 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 | return 0; | 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 | struct pci_dev *pci_dev = to_pci_dev(dev); | 1819 | struct pci_dev *pci_dev = to_pci_dev(dev); |
| 1826 | struct cio2_device *cio2 = pci_get_drvdata(pci_dev); | 1820 | struct cio2_device *cio2 = pci_get_drvdata(pci_dev); |
| 1827 | void __iomem *const base = cio2->base; | 1821 | void __iomem *const base = cio2->base; |
| 1828 | u16 pm; | ||
| 1829 | 1822 | ||
| 1830 | writel(CIO2_D0I3C_RR, base + CIO2_REG_D0I3C); | 1823 | writel(CIO2_D0I3C_RR, base + CIO2_REG_D0I3C); |
| 1831 | dev_dbg(dev, "cio2 runtime resume.\n"); | 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 | return 0; | 1826 | return 0; |
| 1838 | } | 1827 | } |
| 1839 | 1828 | ||
| @@ -2006,10 +1995,10 @@ static struct pci_driver cio2_pci_driver = { · 166 unmodified lines | |||
| 2006 | 1995 | ||
| 2007 | module_pci_driver(cio2_pci_driver); | 1996 | module_pci_driver(cio2_pci_driver); |
| 2008 | 1997 | ||
| 2009 | MODULE_AUTHOR("Tuukka Toivonen <tuukka.toivonen@intel.com>"); | 1998 | MODULE_AUTHOR("Tuukka Toivonen"); |
| 2010 | MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); | 1999 | MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); |
| 2011 | MODULE_AUTHOR("Jian Xu Zheng"); | 2000 | MODULE_AUTHOR("Jian Xu Zheng"); |
| 2012 | MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>"); | 2001 | MODULE_AUTHOR("Yuning Pu"); |
| 2013 | MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>"); | 2002 | MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>"); |
| 2014 | MODULE_LICENSE("GPL v2"); | 2003 | MODULE_LICENSE("GPL v2"); |
| 2015 | MODULE_DESCRIPTION("IPU3 CIO2 driver"); | 2004 | MODULE_DESCRIPTION("IPU3 CIO2 driver"); |
| @@ -320,10 +320,6 @@ struct pci_dev; · 319 unmodified lines | |||
| 320 | #define CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT 0x4 | 320 | #define CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT 0x4 |
| 321 | #define CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT 0x570 | 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 | struct cio2_csi2_timing { | 323 | struct cio2_csi2_timing { |
| 328 | s32 clk_termen; | 324 | s32 clk_termen; |
| 329 | s32 clk_settle; | 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.